/*
 * Afterglow — K-One effects layer
 * TLJ Audio · https://www.tljaudio.com
 *
 * ═══════════════════════════════════════════════════════════════════════════
 * SCOPE — read before adding anything.
 *
 * This file holds ONLY the four effects Elementor's v4 style schema cannot
 * express. Everything else is an Elementor Global Class or Global Variable,
 * editable by Tommy in the UI. Verified against the schema 2026-08-05:
 *
 *   Reachable in Elementor — DO NOT duplicate here:
 *     · liquid-gold chrome type  → background-gradient-overlay + clip:text
 *     · spectrum gradient        → background-gradient-overlay
 *     · neon glow                → filter: drop-shadow()
 *     · blurred-ink navbar       → backdrop-filter: blur()
 *     · all colour / type / spacing / border / radius
 *
 *   NOT reachable — hence this file:
 *     · repeating-linear-gradient  (no repeating variant in the schema)
 *     · ::before / ::after         (pseudo-elements absent entirely)
 *     · mix-blend-mode on a pseudo-element
 *
 * TOKENS: consume Elementor's variables directly — they compile to custom
 * properties named after the variable label and are defined on the
 * `.elementor-kit-9` body class, so any descendant inherits them:
 *
 *     var(--ink) var(--surface) var(--card) var(--line) var(--plasma)
 *     var(--coral-neon) var(--teal-neon) var(--gold-chrome)
 *     var(--spectrum-crimson|-magenta|-violet|-sea-teal)
 *     var(--muted) var(--mist) var(--snow)
 *     var(--size-*) var(--space-*) var(--container-max) var(--font-*)
 *
 * There is deliberately NO :root mirror block. Never hardcode a hex that
 * already exists as a variable — that would create a second source of truth
 * and break the promise that Tommy can restyle from the Elementor UI.
 *
 * HOOKS: author-supplied class names, applied via an Elementor element's
 * `classes` setting (plain names ride alongside global-class references).
 * Never target Elementor's generated IDs or hashed classes — they change
 * whenever a page is re-saved.
 * ═══════════════════════════════════════════════════════════════════════════
 */


/* ─────────────────────────────────────────────────────────────────────────
   SCANLINE INTENSITY — one place to tune every CRT effect on the site.

   NOTE: these are NOT design tokens and do not contradict the no-:root-mirror
   rule above. They are effect parameters with no Elementor equivalent — the
   schema has no repeating-gradient, so there is nothing in the UI to mirror.
   Each is hued to the surface it sits on, so shading stays in-colour instead of
   turning grey. Raise or lower these to change scanline strength everywhere.

   TWO KINDS, and the difference matters:
     · `--ag-scan-light` paints LIGHT lines. They show on a dark ground and vanish
       against bright text, so they read as texture BEHIND the content.
     · `--ag-scan-over` paints DARK lines in `multiply`. They vanish against a dark
       ground and cut across bright text and logos, so they read as a screen overlay
       ON TOP of everything. This is the CRT look the design language shows.
   Use `over` for chrome bars and screens; `light` only where texture behind the
   content is genuinely what is wanted.
   ───────────────────────────────────────────────────────────────────────── */

:root {
	/* Lightened ~35% on 2026-08-06 (Tommy's call) — the texture should register as
	   screen grain you notice second, not stripes you notice first. Previous values
	   are kept in the trailing comments so the step can be walked back or split. */
	--ag-scan-light:  rgba(244, 244, 246, 0.023); /* on dark grounds — was 0.035 */
	--ag-scan-ink:    rgba(8, 8, 10, 0.18);       /* over holo foil   — was 0.28  */
	--ag-scan-warm:   rgba(74, 52, 16, 0.13);     /* on gold          — was 0.20  */
	--ag-scan-cool:   rgba(10, 52, 48, 0.14);     /* on teal          — was 0.22  */
	--ag-scan-violet: rgba(22, 10, 34, 0.14);     /* on the spectrum  — was 0.21  */
	--ag-scan-over:   rgba(0, 0, 0, 0.13);        /* OVERLAY lines — see note.
	                                                 Currently UNUSED: the navbar and
	                                                 footer that consumed it were cleaned
	                                                 2026-08-06. Kept as the reference
	                                                 implementation of overlay mode. */
	--ag-scan-period: 3px;                        /* line + gap pitch */
}


/* ═════════════════════════════════════════════════════════════════════════
   HELLO ELEMENTOR RESET — neutralised site-wide (Phase 8, 08-02)
   ═════════════════════════════════════════════════════════════════════════
   Hello ships `assets/css/reset.css`, which paints its own brand magenta on
   every link and every button. Exactly three rules do it:

     a                                        { color: #c36 }
     a:hover, a:active                        { color: #336 }
     [type=button], [type=submit], button     { border+color: #c36 }
     ...:focus, ...:hover                     { background: #c36; color: #fff }

   Two things about that hover rule are why this bug kept coming back and was
   patched locally three times (A/B player, newsletter submit, social icons)
   instead of once:

   1. It covers the BARE `button` selector, not just the [type=…] forms. Every
      earlier note recorded only the attribute selectors, so `<button>` elements
      were never in scope and kept bleeding.
   2. `button:hover` is specificity (0,1,1) — the pseudo-class counts. That
      OUTRANKS a single-class component rule like `.ag-btn-try` (0,1,0). So the
      bug was never merely "we declared the neutral at rest only": even a
      component that did declare a background lost the hover state on
      specificity. Both halves had to be true for the symptom to appear, which
      is what made it look intermittent.

   The fix therefore matches Hello's own specificity rather than raising it, and
   sits at the TOP of this file. afterglow.css loads after reset.css, so equal
   specificity wins on order; and because every component rule in this file
   comes LATER, any component that declares its own state still wins its tie
   against this block. That ordering is load-bearing — do not move this block
   down the file, and do not reach for !important.

   Scope note: this neutralises COLOUR only. Hello's button geometry (padding,
   radius, the 1px border) is left alone, so an unstyled button keeps a sane
   shape — it simply renders in the inherited text colour instead of magenta.
   ───────────────────────────────────────────────────────────────────────── */

a {
	color: inherit;
}

a:hover,
a:active,
a:focus,
a:focus-visible {
	color: inherit;
}

/* Every state declared explicitly. Removing a declaration here does not restore
   a neutral — it hands the state back to Hello (hazard #5). */
button,
[type="button"],
[type="submit"],
button:hover,
button:focus,
button:focus-visible,
[type="button"]:hover,
[type="button"]:focus,
[type="button"]:focus-visible,
[type="submit"]:hover,
[type="submit"]:focus,
[type="submit"]:focus-visible {
	background-color: transparent;
	color: inherit;
	border-color: currentColor;
}


/* ═════════════════════════════════════════════════════════════════════════
   STICKY BUY BAR — the only pinned chrome on the site, landing pages only
   ═════════════════════════════════════════════════════════════════════════
   Mechanically this is one `position: sticky; top: 0` element sitting in
   normal flow beneath the (non-sticky) navbar. It travels with the page, then
   pins once it reaches the top. The browser owns the handoff — no scroll
   listener, no measuring the navbar, no per-breakpoint offsets.

   Only the properties Elementor's v4 schema does not expose live here.
   ───────────────────────────────────────────────────────────────────────── */

.ag-buybar-label,
.ag-btn-buy,
.ag-btn-try {
	white-space: nowrap; /* not in the v4 style schema */
}

/* Elementor's `.e-con` carries `width: var(--width)` defaulting to 100%, so every
   nested container stretched — the price block measured 256px for "$149 / $99",
   which shoved "Try for free" away from the buttons. Size these to their content.
   Same root cause as the navbar; if a group ever spreads unexpectedly, look here first. */
.ag-buybar-brandline,
.ag-buybar-actions,
.ag-buybar-price {
	width: auto;
	--width: auto;
	flex: 0 0 auto;
}

/* CRT scanlines across the PRIMARY CTA only.
   Same principle as scanlines on the chrome type: dark stripes in `multiply`, hued to
   the button beneath so the shading stays gold on gold rather than turning grey. The
   overlay is a pseudo-element on the button itself, clipped to the pill radius, and
   pointer-events:none so it never intercepts the click.

   `.ag-btn-try` deliberately has NO scanlines (2026-08-06). It became a ghost/outline
   button — transparent fill — and a `multiply` overlay on a transparent element darkens
   whatever is behind it, so the stripes would have landed on the buy bar's card instead
   of on the button. If the try button ever goes back to a filled treatment, restore its
   `::after` using `--ag-scan-cool` alongside the buy button's rule below. */
.ag-btn-buy {
	position: relative;
	overflow: hidden;
	isolation: isolate;
}

.ag-btn-buy::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	border-radius: inherit;
	mix-blend-mode: multiply;
	background-image: repeating-linear-gradient(
		to bottom,
		var(--ag-scan-warm) 0px,
		var(--ag-scan-warm) 1px,
		transparent 1px,
		transparent var(--ag-scan-period)
	);
}

/* The label must sit above the scanline overlay. */
.ag-btn-buy > * {
	position: relative;
	z-index: 2;
}


/* Two stacked bars cost ~120px of vertical space. On a phone, against a
   full-bleed hero, that is too much — so the bar goes compact: the product
   line and the old price drop out, leaving the decision (price + Buy now)
   and the trial. */
/* ⚠️ Every selector here is scoped to `.ag-buybar` — three of them were not,
   and the leak was invisible for five phases. `.ag-price-was` and the two
   button rules are compact-BAR decisions, but as bare class selectors they
   also hit the buy SECTION's CTA: the struck-through $149 vanished from the
   purchase block on every phone, and the CTA's buttons were quietly shrunk to
   the bar's credit size. Found by Tommy at the 08-03 checkpoint ("the 149
   price is no longer visible here or in the buy bar").

   A rule that encodes a decision about ONE component must name that component,
   even when it sits inside that component's own section of this file. */
@media (max-width: 767px) {
	.ag-buybar .ag-buybar-label,
	.ag-buybar .ag-price-was {
		display: none;
	}

	.ag-buybar-inner {
		justify-content: space-between;
		gap: var(--space-sm);
		padding-inline: 12px;
	}

	.ag-buybar .ag-btn-buy,
	.ag-buybar .ag-btn-try {
		font-size: var(--size-credit);
		padding-inline: 14px;
	}
}

/* ⚠️ THE 381-430px BAND WAS BROKEN — found in 08-03, and the way it hid is the
   lesson. Between the two rules below there was a gap: the tagline goes at 767,
   the trial CTA goes at 380, but from 381 to ~430 the bar still had to fit
   brand + TRY + price + BUY and could not. At 390 the actions row measured
   116..401 inside a 375px viewport — 26px of real document overflow.

   It survived 08-02's sweep because that sweep sampled 360 and 480 and stepped
   straight over the broken band. Those are exactly the widths of real phones:
   390 (iPhone 14/15/16), 393 (Pixel), 414 (Plus), 430 (Pro Max). Sample device
   widths, not round numbers.

   The fix sheds the least useful element first. On the K-One landing page the
   word "K-One" in the bar is the most redundant thing in it — the visitor is
   already on the K-One page — so it goes before the trial CTA does, which keeps
   BOTH buttons on every common phone. That extends the existing progressive-shed
   intent (tagline → brand → trial) rather than inventing a new rule. */
@media (max-width: 430px) {
	.ag-buybar .ag-buybar-brandline {
		display: none;
	}

	/* ⚠️ HIDING A FLEX CHILD CHANGES WHAT `space-between` MEANS.
	   Found by Tommy at the 08-05 checkpoint, on a real device switcher.

	   The bar is `justify-content: space-between` from 767 down, which is
	   correct while it holds two groups: brand on the left, price + buttons on
	   the right. The moment the brand goes `display: none` it leaves flex
	   layout entirely, and `space-between` with a SINGLE item is identical to
	   flex-start — so the price and both CTAs jumped to the left edge and left
	   a hole where the brand used to be. Measured at 360: the inner bar spans
	   16..329 while the actions sat at 35..167, stranding 162px of empty space
	   on the right.

	   Nothing looked broken in a width sweep, because nothing overflowed and
	   nothing was hidden — the layout was merely wrong. This is the 04-03
	   lesson once more: assert what should be VISIBLE and WHERE, not only that
	   nothing moved or overflowed.

	   (0,3,0) so it beats both the bare `.ag-buybar-inner` rule above and
	   Elementor's compiled `.elementor .ag-buybar-inner` at (0,2,0), which
	   loads after this file — hazard #21. */
	.elementor .ag-buybar .ag-buybar-inner {
		justify-content: flex-end;
	}
}

/* Below ~380px even that is tight — drop the secondary CTA. Trying the plugin
   is still one tap away from the page body; buying must never be. */
@media (max-width: 380px) {
	.ag-buybar .ag-btn-try {
		display: none;
	}
}


/* ═════════════════════════════════════════════════════════════════════════
   CTA BLOCK — spectrum hairline on the top edge
   ═════════════════════════════════════════════════════════════════════════
   Everything else about the CTA lives in the `ag-cta` Global Class. Only this
   is here, because the v4 style schema has no pseudo-elements and no
   border-image, so a gradient confined to one edge is unreachable from
   Elementor. A gradient *background* would flood the whole card instead.

   Chosen over a spectrum light-leak flood (2026-08-06, Tommy's call between
   three options). The CTA repeats down a page, and the design language allows
   one full Spectrum flood per page maximum — a flooded CTA would break that
   every time it appeared. As a 2px edge the Spectrum stays an accent light
   source, which is what the language asks for, and survives repetition.
   ───────────────────────────────────────────────────────────────────────── */

.ag-cta,
.ag-frame-spectrum {
	position: relative;
	/* Clips the hairline to the card's 14px radius. Safe for the button glow:
	   the button is inset 40px and its glow blurs 30px, so it never reaches
	   this boundary — measured, not assumed. (.ag-frame-spectrum lends the
	   same 2px spectrum edge to a whole section frame — first used on the
	   four-pillars section, Tommy's call 2026-08-07.) */
	overflow: hidden;
}

.ag-cta::before,
.ag-frame-spectrum::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 2px;
	pointer-events: none;
	background-image: linear-gradient(
		100deg,
		var(--spectrum-crimson),
		var(--spectrum-magenta) 35%,
		var(--spectrum-violet) 68%,
		var(--spectrum-sea-teal)
	);
}


/* ═════════════════════════════════════════════════════════════════════════
   FEATURE CATEGORY PILLS — one colour per category
   ═════════════════════════════════════════════════════════════════════════
   The pill's base treatment (mono, uppercase, outlined, pill radius) lives in
   the `ag-feature-cat` Global Class. Only the per-category colour is here,
   because it has to key off `data-cat`, which `afterglow.js` stamps from the
   pill's own text — Elementor cannot vary a class or attribute per component
   instance, and CSS cannot select on text content. The reasoning is written
   out in full at the matching block in afterglow.js.

   Mapping is Tommy's, 2026-08-06. Categories:
     Tone       800S · Low Cut · De-Harsh · De-Mud
     Dynamics   Leveler · Compression · De-Esser · Special Dry/Wet · Pre-Comp
     Exciter    Shine
     Saturation Color

   Tone was teal-neon first and moved to magenta, because teal-neon and
   sea-teal are indistinguishable at 13px.

   No `data-cat` (JS blocked, or an unrecognised label) keeps the Global
   Class's default colour, which is legible on its own. Nothing breaks.

   ⚠️ TWO OF THESE ARE LIGHTENED TINTS, NOT THE RAW TOKENS — deliberately.
   Measured against the Ink ground at 13px (WCAG AA wants 4.5:1):

     gold-chrome       #E8B339  10.4:1  ✅ token used as-is
     spectrum-sea-teal #2BAEC4   7.6:1  ✅ token used as-is
     spectrum-magenta  #BE2E86   3.7:1  ❌ too low  → #CB589E  5.2:1
     spectrum-crimson  #D11F45   3.8:1  ❌ too low  → #DA4C6A  5.0:1

   The two tints are each the brand colour mixed 20% toward white. They are
   literal hexes rather than tokens because they exist ONLY here, for legible
   small text — the palette itself is unchanged and every other use of magenta
   and crimson still references the token. This follows the Phase 2 precedent
   that component-specific values live outside the token set.
   Do not "tidy" these back to var(--spectrum-magenta) / var(--spectrum-crimson);
   that would silently reintroduce a contrast failure.
   ───────────────────────────────────────────────────────────────────────── */

.ag-feature-cat[data-cat="tone"] {
	color: #CB589E;        /* spectrum-magenta +20% white — 5.2:1 on Ink */
	border-color: #CB589E;
}

.ag-feature-cat[data-cat="dynamics"] {
	color: var(--gold-chrome);
	border-color: var(--gold-chrome);
}

.ag-feature-cat[data-cat="exciter"] {
	color: var(--spectrum-sea-teal);
	border-color: var(--spectrum-sea-teal);
}

.ag-feature-cat[data-cat="saturation"] {
	color: #DA4C6A;        /* spectrum-crimson +20% white — 5.0:1 on Ink */
	border-color: #DA4C6A;
}


/* ═════════════════════════════════════════════════════════════════════════
   FEATURE CAROUSEL — Elementor Pro `nested-carousel`, dressed for Ink
   ═════════════════════════════════════════════════════════════════════════
   Unlike `nav-menu` in Phase 3, this v3 widget DOES render with its own
   widget-type class — `elementor-widget-n-carousel` (note: `n-carousel`, not
   `nested-carousel`). So Elementor's own CSS applies and we can simply
   override it, rather than rebuilding what the bare element lost.

   Elementor's stock chrome is unusable here, measured 2026-08-06:
     · arrows          rgba(238,238,238,0.9) — near-white, foreign to the palette
     · pagination dots #000 — literally invisible on the Ink ground

   The slide content is component #171 (the feature card). Its fixed 240×310
   media box is what keeps every slide the same height, so NOTHING here sets a
   height — doing so would fight the card and clip on narrow viewports.
   ───────────────────────────────────────────────────────────────────────── */

/* Elementor places the arrows INSIDE the swiper area, so by default the left
   one lands on top of the feature card's image. The fix is to inset the slide
   CONTENT, giving the arrows a clear gutter — the arrangement the Nuro
   reference uses. The card itself cannot be re-centred instead: it is component
   #171 and 04-02 is closed.

   ⚠️ The padding MUST go on `.swiper-slide`, never on `.swiper`.
   `overflow: hidden` clips at the PADDING box, not the content box, so padding
   on `.swiper` leaves those gutters visible — and Swiper translates the next
   slide straight into them. The symptom is subtle and easy to ship: the first
   slide looks correct, and a sliver of the following card only appears once you
   start navigating. Found by Tommy, 2026-08-06.

   On `.swiper-slide` the slide still measures the full container width, so
   Swiper's translate maths is untouched and nothing peeks. `box-sizing:
   border-box` (Elementor's global default) keeps the width correct. */
.elementor-widget-n-carousel .swiper-slide {
	padding-inline: 68px;
}

/* 68px per side was sized against a 1140px container. On a phone it eats 136px
   of a 297px slide and leaves the card 141px — narrower than its own 240px
   media box, which is how the feature title ended up painted outside the card
   (measured at 360 in 08-03). The gutter still has to clear the arrows, but the
   arrows are 44px, so 52px is enough and leaves the card usable.

   Still on `.swiper-slide`, never on `.swiper` — hazard #7 is about WHERE the
   padding goes, not how much of it there is. */
@media (max-width: 767px) {
	.elementor-widget-n-carousel .swiper-slide {
		padding-inline: 52px;
	}
}

/* ARROWS — a 44px hit area around a smaller glyph, so the target is reachable
   without the chevron itself getting heavy.

   ⚠️ SPECIFICITY TRAP, measured 2026-08-06. Elementor styles the arrow COLOUR at
   three-class specificity —
     `.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-next`
   — and separately targets the inner `svg`. A two-class rule here wins on size,
   background, border and radius but **silently loses on colour alone**, which is
   easy to miss because everything else visibly applies. Hence the `-prev` /
   `-next` classes below: they match Elementor's specificity, and afterglow.css
   loads later, so ours takes it. No `!important` needed. */
.elementor-widget-n-carousel .elementor-swiper-button {
	width: 44px;
	height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 999px;
	background-color: rgba(20, 20, 23, 0.55);
	border: 1px solid var(--line);
	backdrop-filter: blur(6px);
	transition: color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}

.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-prev,
.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-next {
	color: var(--mist);
}

.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-prev svg,
.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-next svg {
	width: 16px;
	height: 16px;
	fill: currentColor;
}

.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-prev:hover,
.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-next:hover {
	color: var(--teal-neon);
	border-color: var(--teal-neon);
	background-color: rgba(20, 20, 23, 0.85);
}

.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-prev:hover svg,
.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-next:hover svg {
	fill: currentColor;
}

/* Keyboard reachability. Elementor's arrows are focusable; without this the
   focus ring is invisible on a dark ground. */
.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-prev:focus-visible,
.elementor-widget-n-carousel .elementor-swiper-button.elementor-swiper-button-next:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 3px;
	color: var(--teal-neon);
}

/* Elementor disables an arrow at the ends when loop is off. Keep it present but
   clearly inert rather than letting it vanish and shift the layout. */
.elementor-widget-n-carousel .elementor-swiper-button.swiper-button-disabled {
	opacity: 0.35;
	pointer-events: none;
}

/* PAGINATION DOTS — stock is #000 on a near-black ground. */
.elementor-widget-n-carousel .swiper-pagination-bullet {
	width: 7px;
	height: 7px;
	background-color: var(--muted);
	opacity: 1;
	transition: background-color 160ms ease, transform 160ms ease;
}

.elementor-widget-n-carousel .swiper-pagination-bullet-active {
	background-color: var(--teal-neon);
	transform: scale(1.25);
}

.elementor-widget-n-carousel .swiper-pagination-bullet:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 3px;
}


/* ═════════════════════════════════════════════════════════════════════════
   FAQ ACCORDION — Elementor Pro `nested-accordion`, dressed for Ink
   ═════════════════════════════════════════════════════════════════════════
   Widget class is `elementor-widget-n-accordion` (`n-`, not `nested-`), and it
   renders as native <details>/<summary>, which means open/close and keyboard
   operation are the browser's job — no JS of ours, and it works with the script
   disabled.

   Elementor's stock treatment is unusable here, measured 2026-08-06:
     · question colour  rgb(31,33,36) — near-black on the Ink ground, invisible
     · question font    -apple-system — a system font, which the design language
                        explicitly forbids

   The `h3` sits INSIDE the <summary>, so type has to be set on the heading, not
   only on the summary. Setting it on the summary alone leaves the h3 inheriting
   its own UA styles.

   Answers use the ag-type-body Global Class, so nothing about them is here.
   ───────────────────────────────────────────────────────────────────────── */

/* Elementor draws a 1px border on ALL FOUR sides of both the <summary> and the
   answer panel, in rgb(213,216,220) — measured 2026-08-06. On the Ink ground
   that reads as a light box around every row, which is not the hairline-
   separated list the design language asks for. Zero them, then put back a
   single hairline per item. */
.elementor-widget-n-accordion .e-n-accordion-item-title,
.elementor-widget-n-accordion .e-n-accordion-item > .e-con {
	border: 0;
}

.elementor-widget-n-accordion .e-n-accordion-item {
	border-block-end: 1px solid var(--line);
}

.elementor-widget-n-accordion .e-n-accordion-item:first-child {
	border-block-start: 1px solid var(--line);
}

.elementor-widget-n-accordion .e-n-accordion-item-title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-sm);
	padding: var(--space-sm) 0;
	cursor: pointer;
	list-style: none;               /* kill the native disclosure triangle */
	transition: color 160ms ease;
}

/* Safari renders the triangle via a pseudo-element that `list-style` misses. */
.elementor-widget-n-accordion .e-n-accordion-item-title::-webkit-details-marker {
	display: none;
}

/* ⚠️ Elementor visually REVERSES these two, despite the DOM order being header
   then icon — measured 2026-08-06: the icon rendered at x=168 and the question
   at x=937, which reads as a right-aligned question with a stray marker on the
   far left. `flex-direction` computes to `row`, so it is doing it with `order`.
   Set both explicitly rather than trying to out-guess it. */
.elementor-widget-n-accordion .e-n-accordion-item-title-header {
	order: 0;
	flex: 1 1 auto;
	text-align: start;
}

.elementor-widget-n-accordion .e-n-accordion-item-title-icon {
	order: 1;
	margin-inline-start: auto;
}

/* The heading is inside the summary — style it, not just the summary. */
.elementor-widget-n-accordion .e-n-accordion-item-title h3,
.elementor-widget-n-accordion .e-n-accordion-item-title .e-n-accordion-item-title-text {
	font-family: var(--font-body);
	font-size: var(--size-body);
	font-weight: 600;
	line-height: 1.4;
	color: var(--snow);
	margin: 0;
}

.elementor-widget-n-accordion .e-n-accordion-item-title:hover h3,
.elementor-widget-n-accordion .e-n-accordion-item-title:hover .e-n-accordion-item-title-text {
	color: var(--teal-neon);
}

/* Keyboard operation is native, so the focus ring is the only thing missing. */
.elementor-widget-n-accordion .e-n-accordion-item-title:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 3px;
	border-radius: 4px;
}

/* The +/- icon: Font Awesome, inherited near-black. Bring it into the palette
   and let it pick up the hover with the question. */
.elementor-widget-n-accordion .e-n-accordion-item-title-icon,
.elementor-widget-n-accordion .e-n-accordion-item-title-icon svg {
	fill: var(--muted);
	color: var(--muted);
	width: 14px;
	height: 14px;
	flex: 0 0 auto;
	transition: color 160ms ease, fill 160ms ease;
}

.elementor-widget-n-accordion .e-n-accordion-item-title:hover .e-n-accordion-item-title-icon,
.elementor-widget-n-accordion .e-n-accordion-item-title:hover .e-n-accordion-item-title-icon svg {
	fill: var(--teal-neon);
	color: var(--teal-neon);
}

/* Answer panel — breathing room under the question, and the same left edge. */
.elementor-widget-n-accordion .e-n-accordion-item > .e-con {
	padding-block: 0 var(--space-md);
}


/* ═════════════════════════════════════════════════════════════════════════
   A/B AUDIO COMPARE — the one sanctioned Custom HTML component
   ═════════════════════════════════════════════════════════════════════════
   CLAUDE.md §9 names this component specifically as the exception to the
   no-Custom-HTML rule, so ALL of its styling lives here rather than in Global
   Classes — a Custom HTML widget cannot take them.

   Layout follows `claude-files/K-One A-B Audio Compare.dc.html`. Note the
   780px cap: this is deliberately narrower than the 1140 content column,
   because the player is a single focused moment rather than a full-width band.

   Behaviour is in assets/js/ab-compare.js. The controller reads its audio
   sources from data attributes on each track, so swapping in real files is a
   template edit, never a code change.
   ───────────────────────────────────────────────────────────────────────── */

.ag-abx {
	max-width: 780px;
	margin-inline: auto;
	width: 100%;
}

.ag-abx-intro {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-sm);
	margin-block-end: var(--space-xs);
}

.ag-abx-eyebrow {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--muted);
	margin: 0;
}

.ag-abx-eyebrow .ag-abx-num {
	color: var(--teal-neon);
}

.ag-abx-hangul {
	font-family: var(--font-hangul);
	font-size: 26px;
	line-height: 1;
	color: var(--teal-neon);
	text-shadow: 0 0 18px rgba(68, 214, 201, 0.55);
	margin: 0;
}

.ag-abx-heading {
	font-family: var(--font-display);
	font-weight: 800;
	font-size: var(--size-poster);
	line-height: 1.05;
	letter-spacing: -0.01em;
	color: var(--snow);
	margin: 0 0 var(--space-xs);
}

.ag-abx-sub {
	font-family: var(--font-body);
	font-size: var(--size-body);
	line-height: 1.6;
	color: var(--mist);
	margin: 0 0 var(--space-xs);
}

.ag-abx-explain {
	font-family: var(--font-body);
	font-size: var(--size-credit);
	line-height: 1.7;
	color: var(--muted);
	margin: 0 0 var(--space-md);
}

.ag-abx-explain strong {
	color: var(--mist);
	font-weight: 600;
}

/*
 * De-emphasised label colour for text sitting ON THE CARD.
 *
 * --muted (#7A7A85) is tuned against the ink page ground (#08080A), where it
 * measures 4.72:1. The card is --surface (#141417) — lighter — so the same ink
 * drops to 4.33:1 there and fails WCAG AA for normal text. This is a 6-point
 * lift, 4.71:1 on surface: perceptually the same grey, but compliant.
 *
 * Scoped to the player deliberately, exactly like the lightened category pills.
 * Do NOT "fix" this by changing --muted itself — that token is correct for the
 * ink ground it was designed against, and every other use of it depends on that.
 */
.ag-abx {
	--abx-muted-on-card: #80808B;

	/*
	 * Hover / focus fill for the player's buttons.
	 *
	 * Originally this existed to displace a PARENT THEME default: Hello Elementor
	 * paints a magenta on every button's :hover and :focus that is nowhere in
	 * Afterglow. As of 08-02 that reset is neutralised site-wide at the top of
	 * this file, so this is no longer the only thing standing between the player
	 * and magenta — but the fill is a real design value for the player, so it
	 * stays, and every interactive state below still declares background-color
	 * EXPLICITLY. Do not drop those declarations as "redundant": a state that
	 * declares nothing inherits whatever the cascade hands it (hazard #5).
	 */
	--abx-hover-fill: #123734;
}

/* THE CARD — surface ground, hairline, 18px radius per the comp. */
.ag-abx-card {
	background-color: var(--surface);
	border: 1px solid var(--line);
	border-radius: 18px;
	overflow: hidden;
}

.ag-abx-bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-sm);
	flex-wrap: wrap;
	padding: 20px 22px;
	border-block-end: 1px solid var(--line);
}

.ag-abx-now {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.ag-abx-nowlabel {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--abx-muted-on-card);
}

.ag-abx-nowname {
	font-family: var(--font-display);
	font-weight: 800;
	font-size: 19px;
	color: var(--snow);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* THE TOGGLE — the component's whole point. Two real buttons, so keyboard
   operation and aria-pressed come for free. */
.ag-abx-toggle {
	display: flex;
	gap: 0;
	padding: 5px;
	border: 1px solid var(--line);
	border-radius: 999px;
	background-color: var(--ink);
}

.ag-abx-tbtn {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--muted);
	background: transparent;
	border: 0;
	border-radius: 999px;
	padding: 7px 18px;
	cursor: pointer;
	transition: color 140ms ease, background-color 140ms ease;
}

.ag-abx-tbtn:hover,
.ag-abx-tbtn:focus {
	color: var(--mist);
	background-color: var(--abx-hover-fill);
}

/* Selected side. Driven by aria-pressed, so the visual state and the state
   exposed to assistive tech can never drift apart. */
.ag-abx-tbtn[aria-pressed="true"] {
	color: var(--ink);
	background-color: var(--teal-neon);
}

.ag-abx-tbtn:focus-visible,
.ag-abx-play:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 3px;
}

/* TRACK ROWS */
.ag-abx-track {
	display: flex;
	align-items: center;
	gap: var(--space-sm);
	padding: 16px 22px;
	border-block-end: 1px solid var(--line);
	transition: background-color 140ms ease;
}

.ag-abx-track[data-abx-current="true"] {
	background-color: rgba(68, 214, 201, 0.04);
}

.ag-abx-play {
	flex: 0 0 auto;
	width: 46px;
	height: 46px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	background: transparent;
	border: 1px solid var(--line);
	color: var(--mist);
	cursor: pointer;
	transition: color 140ms ease, border-color 140ms ease;
}

.ag-abx-play:hover,
.ag-abx-play:focus {
	color: var(--teal-neon);
	border-color: var(--teal-neon);
	background-color: var(--abx-hover-fill);
}

/* The currently-playing track keeps the teal outline but NOT the hover fill —
   the fill has to stay a pointer affordance, or "hovered" and "playing" become
   the same picture and the row stops telling you which track is live. */
.ag-abx-track[data-abx-current="true"] .ag-abx-play {
	color: var(--teal-neon);
	border-color: var(--teal-neon);
	background-color: transparent;
}

.ag-abx-track[data-abx-current="true"] .ag-abx-play:hover,
.ag-abx-track[data-abx-current="true"] .ag-abx-play:focus {
	background-color: var(--abx-hover-fill);
}

/* Play / pause glyph drawn in CSS — no icon font, no SVG dependency. */
.ag-abx-playicon {
	display: block;
	width: 0;
	height: 0;
	border-block-start: 7px solid transparent;
	border-block-end: 7px solid transparent;
	border-inline-start: 12px solid currentColor;
	margin-inline-start: 3px;
}

.ag-abx-play[aria-pressed="true"] .ag-abx-playicon {
	width: 12px;
	height: 14px;
	border: 0;
	margin-inline-start: 0;
	background-image: linear-gradient(
		to right,
		currentColor 0 4px,
		transparent 4px 8px,
		currentColor 8px 12px
	);
}

.ag-abx-meta {
	flex: 0 0 auto;
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 150px;
	max-width: 180px;
}

.ag-abx-name {
	font-family: var(--font-body);
	font-weight: 600;
	font-size: var(--size-body);
	color: var(--snow);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.ag-abx-genre {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--abx-muted-on-card);
}

.ag-abx-wave {
	flex: 1 1 auto;
	min-width: 60px;
	display: block;
	width: 100%;
	height: 44px;
}

.ag-abx-time {
	flex: 0 0 auto;
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	color: var(--mist);
	min-width: 82px;
	text-align: end;
}

.ag-abx-foot {
	display: flex;
	align-items: baseline;
	gap: var(--space-xs);
	padding: 15px 22px;
	background-color: var(--ink);
}

.ag-abx-footlabel {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	font-weight: 700;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--muted);
	flex: 0 0 auto;
}

.ag-abx-chain {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	color: var(--mist);
	line-height: 1.6;
	min-width: 0;
}

/*
 * K-One's own name inside the signal chain.
 *
 * The controller wraps every spelling of the product name in this span, so the
 * chain line itself shows whether the plugin is in circuit — lit while Active,
 * dimmed on Bypass. Tommy writes the chain as plain text in one attribute; the
 * highlighting is found, not authored.
 *
 * Both states are real text colours that pass AA on the ink foot (teal 10.3:1,
 * muted 4.72:1) — "off" here means de-emphasised, never unreadable, and the
 * meaning is never carried by colour alone: the Active/Bypass toggle already
 * states it.
 */
.ag-abx-kone {
	color: var(--teal-neon);
	font-weight: 700;
	transition: color 140ms ease;
}

.ag-abx[data-abx-mode="bypass"] .ag-abx-kone {
	color: var(--muted);
	font-weight: 400;
}

@media (prefers-reduced-motion: reduce) {
	.ag-abx-kone {
		transition: none;
	}
}

.ag-abx-note {
	font-family: var(--font-body);
	font-size: var(--size-credit);
	color: var(--muted);
	margin: var(--space-sm) 0 0;
}

/* The row is the widest thing here; below the container it needs to wrap
   rather than squeeze the waveform to nothing.

   The intro and foot rows joined this block in 08-03. Every OTHER container in
   that pass got an Elementor breakpoint variant instead — the decided mechanism
   — but the A/B player is the site's one sanctioned Custom HTML component
   (CLAUDE.md §9), so it has no Global Classes to hang a variant on and all of
   its styling already lives here. Keeping its responsive rules in the same file
   as the rest of its styling is the consistent choice, not an exception to the
   mechanism. Measured at 360 before the fix: intro children [130, 43] and foot
   children [48, 195], both still two-across in a 297px card. */
@media (max-width: 767px) {
	.ag-abx-track {
		flex-wrap: wrap;
	}

	.ag-abx-wave {
		order: 3;
		flex-basis: 100%;
	}

	.ag-abx-meta {
		max-width: none;
		flex: 1 1 auto;
	}

	.ag-abx-intro,
	.ag-abx-foot {
		flex-direction: column;
		align-items: flex-start;
		gap: 4px;
	}
}


/* ═════════════════════════════════════════════════════════════════════════
   NAVIGATION — the one place styling lives in CSS rather than a Global Class
   ═════════════════════════════════════════════════════════════════════════
   The nav is Elementor Pro's `nav-menu`, a v3 widget, chosen deliberately: it
   brings dropdown, mobile collapse, keyboard handling and aria for free, which
   matters because K-One becomes a "Plugins" dropdown when the second plugin
   ships. v3 widgets cannot take Global Classes — but they CAN read the same
   token custom properties, so the single source of truth survives. Never
   hardcode a value here that exists as a variable.
   ───────────────────────────────────────────────────────────────────────── */

/* NO SCANLINES ON THE CHROME (removed 2026-08-06, Tommy's call).
   The navbar and footer previously carried a `--ag-scan-over` overlay on ::after.
   They are now clean surfaces. The CRT texture stays where it earns its keep —
   the CTA buttons, chrome type and holo foil — rather than running across every
   bar on every page, where it read as noise on the site's most-repeated furniture.
   Deliberately not reinstated: do not add it back without asking.

   Nothing else depended on that rule. The absolutely-positioned descendants here
   (the nav underline, the mobile dropdown) declare their own containing blocks at
   `.menu-item` and `.ag-navbar-right`, so the removed `position/isolation` on the
   bars themselves is not load-bearing. */

/* LAYOUT — inside an atomic container, Elementor 4 renders a v3 widget as a bare
   `.elementor-element` with NO `elementor-widget-nav-menu` class, and it fills the
   row. Target the element wrapper directly so the nav sizes to its content. */
.ag-navbar-right > .elementor-element,
.ag-footer-row > .elementor-element {
	width: auto;
	max-width: none;
	flex: 0 0 auto;
}

/* Elementor's `.e-con` sets `width: var(--width)` defaulting to 100%, so this group
   filled the whole row and `space-between` had nothing left to distribute — which is
   why the lockup and links both sat hard left. Size it to its content instead. */
.ag-navbar .ag-navbar-right {
	width: auto;
	--width: auto;
	flex: 0 0 auto;
}

.ag-navbar .elementor-nav-menu--main,
.ag-footer .elementor-nav-menu--main {
	justify-content: flex-end;
}

/* Elementor styles `.elementor-item::before/::after` UNSCOPED — opacity toggles and
   transform:scale() meant for its own pointer effects. Anything of ours built on those
   pseudo-elements inherits that behaviour. Neutralise them, and hang our underline off
   the LIST ITEM instead (`.menu-item`), which Elementor never touches. */
.ag-navbar .elementor-nav-menu .elementor-item::before,
.ag-navbar .elementor-nav-menu .elementor-item::after,
.ag-footer .elementor-nav-menu .elementor-item::before,
.ag-footer .elementor-nav-menu .elementor-item::after {
	content: none !important;
}

.ag-navbar .elementor-nav-menu .elementor-item,
.ag-footer .elementor-nav-menu .elementor-item {
	font-family: var(--font-body);
	font-size: var(--size-body);
	font-weight: 500;
	line-height: 1.4;
	color: var(--mist);
	background: transparent;
	border: 0;
	box-shadow: none;   /* the glow lives on the li, not here — a blurred box-shadow
	                       on the link wrapped the whole box and read as a glowing
	                       rectangle rather than an underline */
	transition: color 160ms ease;

	/* Elementor's stock nav-link padding is 13px 20px, and it reasserts itself in the
	   hover state — measured: 6.8/15.3px at rest, 13/20px hovered, which grew the link,
	   the row and the whole bar. Pinned in every state so the box is immovable. The
	   !important is deliberate and load-bearing; do not "clean it up". */
	padding: 0.4em 0.9em !important;
}

.ag-navbar .elementor-nav-menu .elementor-item:hover,
.ag-navbar .elementor-nav-menu .elementor-item:focus,
.ag-navbar .elementor-nav-menu .elementor-item:focus-visible,
.ag-navbar .elementor-nav-menu .elementor-item.elementor-item-active,
.ag-footer .elementor-nav-menu .elementor-item:hover,
.ag-footer .elementor-nav-menu .elementor-item:focus,
.ag-footer .elementor-nav-menu .elementor-item:focus-visible,
.ag-footer .elementor-nav-menu .elementor-item.elementor-item-active {
	padding: 0.4em 0.9em !important;
	border: 0 !important;
	font-size: var(--size-body) !important;
	font-weight: 500 !important;
	line-height: 1.4 !important;
}

/* `body ` prefix is load-bearing: the header/footer templates' own post-84/
   post-88 CSS styles :hover and .elementor-item-active at (0,4,0) with the
   v3 globals (accent computes to mist) and loads AFTER this file, so equal
   specificity loses. body+4 classes = one element higher, wins regardless
   of order. Found 2026-08-12 — until then the teal current-page state had
   silently never applied (no draft URL ever matched a menu item to show it). */
body .ag-navbar .elementor-nav-menu .elementor-item:hover,
body .ag-navbar .elementor-nav-menu .elementor-item:focus-visible,
body .ag-footer .elementor-nav-menu .elementor-item:hover,
body .ag-footer .elementor-nav-menu .elementor-item:focus-visible {
	color: var(--snow);            /* HOVER — white text */
	background: transparent;
}

/* current-page-ancestor: WP's class when a PAGE-object item's target is an
   ancestor of the current page (the Support docs tree, Phase 7). Elementor
   maps neither it nor current-menu-ancestor to elementor-item-active. */
body .ag-navbar .elementor-nav-menu .elementor-item.elementor-item-active,
body .ag-navbar .elementor-nav-menu .current-menu-item > .elementor-item,
body .ag-navbar .elementor-nav-menu .current-menu-ancestor > .elementor-item,
body .ag-navbar .elementor-nav-menu .current-page-ancestor > .elementor-item,
body .ag-footer .elementor-nav-menu .elementor-item.elementor-item-active,
body .ag-footer .elementor-nav-menu .current-menu-item > .elementor-item,
body .ag-footer .elementor-nav-menu .current-menu-ancestor > .elementor-item,
body .ag-footer .elementor-nav-menu .current-page-ancestor > .elementor-item {
	color: var(--teal-neon);       /* CURRENT PAGE — teal text */
	background: transparent;
}

/* THE UNDERLINE — on the <li>, using ::BEFORE.
   Every other pseudo-element here is already claimed by Elementor:
     · a.elementor-item::before / ::after  → its pointer effects
     · li::after                           → its menu DIVIDER, via
       `.elementor-nav-menu--layout-horizontal .elementor-nav-menu > li:not(:last-child)::after`
   That divider rule outranks ours and applies to every item except the last, which is
   why the underline previously appeared on Support alone. li::before is unclaimed.
   Absolutely positioned, so it is outside flow and cannot shift anything. */
.ag-navbar .elementor-nav-menu > .menu-item,
.ag-footer .elementor-nav-menu > .menu-item {
	position: relative;
}

.ag-navbar .elementor-nav-menu > .menu-item::before,
.ag-footer .elementor-nav-menu > .menu-item::before {
	content: "";
	position: absolute;
	left: 0.9em;
	right: 0.9em;
	bottom: 0.1em;
	height: 2px;
	border-radius: 2px;
	background: var(--teal-neon);
	box-shadow: 0 0 9px 0 rgba(68, 214, 201, 0.9);
	opacity: 0;
	transition: opacity 160ms ease;
	pointer-events: none;
}

.ag-navbar .elementor-nav-menu > .menu-item:hover::before,
.ag-navbar .elementor-nav-menu > .menu-item:focus-within::before,
.ag-navbar .elementor-nav-menu > .menu-item.current-menu-item::before,
.ag-navbar .elementor-nav-menu > .menu-item.current-menu-ancestor::before,
.ag-navbar .elementor-nav-menu > .menu-item.current-page-ancestor::before,
.ag-footer .elementor-nav-menu > .menu-item:hover::before,
.ag-footer .elementor-nav-menu > .menu-item:focus-within::before,
.ag-footer .elementor-nav-menu > .menu-item.current-menu-item::before,
.ag-footer .elementor-nav-menu > .menu-item.current-menu-ancestor::before,
.ag-footer .elementor-nav-menu > .menu-item.current-page-ancestor::before {
	opacity: 1;
}

/* THE UNDERLINE, PART TWO — removed 2026-09-04 (09-05 follow-up).
   A body-prefixed rule here once forced `content: ""` back onto the dropdown
   `<li>::before`, "restoring the missing hamburger hover underline" from the
   08-06 checkpoint. Its comment admitted the suppressor it was overriding was
   "identified by BEHAVIOUR, not by name" — repeated CSSOM scans never found
   it. The suppressor was OUR OWN rule, two sections down this file:
   "Dropdown menus: the underline must sit under the LETTERS, not the panel"
   sets `content: none` in dropdowns on purpose, because the li there is a
   full-width block and the ::before bar stretches the whole panel.

   The restore therefore re-created a panel-wide teal bar on hover/current in
   the hamburger, stacking on top of the intended text-decoration underline —
   found by Tommy on a real iPhone (sticky tap-hover made it linger; desktop
   hover is transient, so width sweeps never flagged it). Dropdown underlines
   are text-decoration ONLY — hover included, see that section. If a hamburger
   underline ever "goes missing" again, START at the letters-only rule; do not
   resurrect a ::before here. */

/* MOBILE PANEL — Elementor's own dropdown positioning lives under
   `.elementor-widget-nav-menu`, which does not exist inside an atomic container, so the
   panel rendered `position: static` and grew the bar from 102px to 255px, shoving the
   logo down. Anchor it as an overlay under the right-hand group instead. */
.ag-navbar .ag-navbar-right {
	position: relative;
}

.ag-navbar .elementor-nav-menu--dropdown {
	position: absolute;
	top: calc(100% + 14px);
	right: 0;
	width: max-content;
	min-width: 200px;
	z-index: 30;
}

/* The open panel has to clear the STICKY BUY BAR, which is the only pinned
   chrome on the site and carries z-index: 90. The navbar is position: relative
   with z-index: auto, so it never formed a stacking context that could compete
   — the panel's own z-index: 30 is scoped inside the navbar and loses to the
   bar's 90 no matter how high it goes. Raising the navbar itself above 90 is
   the fix, and it has to live HERE rather than on the panel.

   Safe because the navbar is not sticky (Phase 3: the buy bar is the only
   pinned element). The two only share screen space at the top of a landing
   page, which is exactly when the menu must win. Found by Tommy at the 08-02
   checkpoint — the toggle was reachable but its panel opened behind the bar. */
body .ag-navbar {
	z-index: 100;
}

/* Dropdown — the "Plugins" submenu, once plugin two ships.
   The FOOTER panel joins here (08-02, Tommy): its nav is the same widget and
   was inheriting Elementor's stock white sheet with a 3px radius on Ink.
   Appearance is shared; positioning deliberately is not — see the toggle note
   below. */
.ag-navbar .elementor-nav-menu--dropdown,
.ag-footer .elementor-nav-menu--dropdown,
.ag-navbar .elementor-nav-menu--main .sub-menu {
	background-color: var(--card);
	border: 1px solid var(--line);
	border-radius: 10px;
	overflow: hidden;
	box-shadow: 0 18px 40px rgba(8, 8, 10, 0.55);
}

.ag-navbar .sub-menu .elementor-sub-item {
	font-family: var(--font-body);
	font-size: var(--size-body);
	color: var(--mist);
	padding: 0.6em 1em;
}

.ag-navbar .sub-menu .elementor-sub-item:hover {
	color: var(--teal-neon);
	background-color: var(--surface);
}

/* Mobile toggle and panel — navbar and footer share the treatment. Elementor's
   stock toggle is a near-black glyph on a 5%-black wash with a 3px radius,
   which is invisible on Ink; only the navbar had been dressed, so the footer
   toggle read as a white box (Tommy, 08-02 checkpoint). */
.ag-navbar .elementor-menu-toggle,
.ag-footer .elementor-menu-toggle {
	color: var(--mist);
	background: transparent;
	border: 1px solid var(--line);
	border-radius: 8px;
	padding: 0.45em;
}

.ag-navbar .elementor-menu-toggle:hover,
.ag-navbar .elementor-menu-toggle:focus-visible,
.ag-footer .elementor-menu-toggle:hover,
.ag-footer .elementor-menu-toggle:focus-visible {
	color: var(--teal-neon);
}

.ag-navbar .elementor-nav-menu--dropdown .elementor-item,
.ag-footer .elementor-nav-menu--dropdown .elementor-item {
	padding: 0.8em 1.1em;
}

/* ⚠️ Superseded — the footer panel is an OVERLAY, positioned in the responsive
   block at the end of this file. 08-02 left it in normal flow, reasoning that a
   footer has nothing below it to cover. That was wrong: it has its own rows
   below, and opening the menu grew the footer by 141px and pushed them all
   down. Only the sizing remains here; the positioning lives with the rest of
   the responsive rules. */
.ag-footer .elementor-nav-menu--dropdown {
	width: max-content;
	min-width: 200px;
}

/* Keyboard focus must be visible on a dark ground. */
.ag-navbar a:focus-visible,
.ag-navbar button:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 3px;
	border-radius: 4px;
}


/* ─────────────────────────────────────────────────────────────────────────
   0 · GRADIENT-CLIPPED TEXT — the half Elementor cannot do
   `background-clip: text` alone is not enough: the glyphs keep their own
   colour and you see the gradient only bleeding at the edges (grey text with
   a gold outline). Making the fill transparent is what reveals the gradient,
   and there is no text-fill-color property in the v4 style schema — so the
   Global Class carries the gradient and this carries the reveal.
   ───────────────────────────────────────────────────────────────────────── */

.ag-chrome-type,
.ag-spectrum-text {
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
	padding-block-end: 0.08em; /* reserve descender space — clipped text crops hard */

	/* A clipped gradient is sized to the ELEMENT BOX, not the glyphs. On a
	   full-width heading, short text samples only the left end of the ramp —
	   which is why the Spectrum read as "80% crimson with magenta creeping in".
	   Shrink-wrapping the box to the text makes the full ramp span the word,
	   at any length, so the gradient adapts to whatever it is used on. */
	width: fit-content;
	max-width: 100%;
}

/* Centred contexts need the shrink-wrapped box centred too. */
.ag-chrome-type.ag-center,
.ag-spectrum-text.ag-center {
	margin-inline: auto;
}

/* Gold chrome wants a warm bloom behind it, not a drop-shadow on transparent ink. */
.ag-chrome-type {
	filter: drop-shadow(0 0 22px rgba(232, 179, 57, 0.28));
}

.ag-spectrum-text {
	filter: drop-shadow(0 0 24px rgba(190, 46, 134, 0.30));
}


/* ─────────────────────────────────────────────────────────────────────────
   1 · CRT SCANLINES
   Faint horizontal lines over a dark surface. The dark must always win —
   if you can read the lines as stripes rather than texture, turn it down.
   ───────────────────────────────────────────────────────────────────────── */

.ag-scanlines {
	position: relative;
	isolation: isolate;
}

.ag-scanlines::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background-image: repeating-linear-gradient(
		to bottom,
		var(--ag-scan-light) 0px,
		var(--ag-scan-light) 1px,
		transparent 1px,
		transparent var(--ag-scan-period)
	);
}

/* Scanlines ON gradient-clipped text.
   The default rule above paints light stripes across the whole element box,
   which on a heading reads as a lined rectangle rather than lined letters.
   Clipping a second background layer to the glyphs is not reachable here
   (the gradient layer is owned by the Elementor Global Class, and adding to
   a background stack from a separate rule is not possible in CSS).
   So instead: dark stripes in `multiply`. Against the near-black ground the
   result is imperceptible; across the bright glyphs it darkens into visible
   scanlines. The lines land on the letters, not the box. */
.ag-chrome-type.ag-scanlines::after,
.ag-spectrum-text.ag-scanlines::after {
	mix-blend-mode: multiply;
	background-image: repeating-linear-gradient(
		to bottom,
		var(--ag-scan-warm) 0px,
		var(--ag-scan-warm) 1px,
		transparent 1px,
		transparent var(--ag-scan-period)
	);
}

/* The spectrum runs cool, so its lines take a cool shadow rather than a warm one. */
.ag-spectrum-text.ag-scanlines::after {
	background-image: repeating-linear-gradient(
		to bottom,
		var(--ag-scan-violet) 0px,
		var(--ag-scan-violet) 1px,
		transparent 1px,
		transparent var(--ag-scan-period)
	);
}

/* Optional soft vignette + glow bloom, for player and screen surfaces. */
.ag-scanlines--bloom::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background: radial-gradient(
		ellipse at 50% 0%,
		rgba(68, 214, 201, 0.10) 0%,
		transparent 60%
	);
}


/* ─────────────────────────────────────────────────────────────────────────
   2 · STICKER COLLAGE
   Glowing cut-out label, tilted. Badges and CTAs.
   ───────────────────────────────────────────────────────────────────────── */

/* A sticker is a small tinted LABEL with text in it — not a big empty panel.
   Sizing comes from its own padding, so it hugs whatever text it wraps. */
.ag-sticker {
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
	width: fit-content;
	padding: 0.42em 0.9em;
	border-radius: 8px;
	font-family: var(--font-body);
	font-weight: 600;
	font-size: var(--size-credit);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	white-space: nowrap;

	/* Tinted gradient ground rather than a flat fill — the cut-out look. */
	color: var(--ink);
	background-image: linear-gradient(150deg, #6FF0E4 0%, var(--teal-neon) 55%, #2AA79B 100%);
	box-shadow:
		0 0 0 1.5px rgba(8, 10, 10, 0.55),
		0 0 18px rgba(68, 214, 201, 0.55),
		0 0 40px rgba(68, 214, 201, 0.22);
	transform: rotate(-3deg);
	transform-origin: center;
}

.ag-sticker--coral {
	background-image: linear-gradient(150deg, #FF8A63 0%, var(--coral-neon) 55%, #C24A26 100%);
	box-shadow:
		0 0 0 1.5px rgba(8, 10, 10, 0.55),
		0 0 18px rgba(240, 101, 61, 0.55),
		0 0 40px rgba(240, 101, 61, 0.22);
	transform: rotate(2.5deg);
}

.ag-sticker--gold {
	background-image: linear-gradient(150deg, #FFF6D6 0%, var(--gold-chrome) 50%, #9C6B1E 100%);
	box-shadow:
		0 0 0 1.5px rgba(8, 10, 10, 0.55),
		0 0 18px rgba(232, 179, 57, 0.55),
		0 0 40px rgba(232, 179, 57, 0.22);
	transform: rotate(-1.5deg);
}

/* Outline variant — glowing cut-out on the dark, no fill. */
.ag-sticker--outline {
	color: var(--teal-neon);
	background-image: none;
	background-color: var(--ink);
	box-shadow:
		0 0 0 1.5px var(--teal-neon),
		0 0 16px rgba(68, 214, 201, 0.45);
	transform: rotate(1.5deg);
}

/* Larger badge, for edition marks and the loud collage pieces. */
.ag-sticker--lg {
	font-size: var(--size-subhead);
	padding: 0.5em 1em;
	border-radius: 10px;
}

/* A collage is stickers at different sizes and angles, overlapping slightly. */
.ag-sticker-collage {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 14px 18px;
}

.ag-sticker-collage > .ag-sticker:nth-child(3n) { transform: rotate(3.5deg); }
.ag-sticker-collage > .ag-sticker:nth-child(4n) { transform: rotate(-4deg); }


/* ─────────────────────────────────────────────────────────────────────────
   3 · HOLOGRAPHIC FOIL
   Iridescent spectrum sheen. Edition badges, sticker shine, dividers.
   ───────────────────────────────────────────────────────────────────────── */

.ag-holo {
	position: relative;
	overflow: hidden;
	isolation: isolate;
}

/* `screen` lifts on a near-black ground; `color-dodge` barely registered and
   read as "too dark". The sheen is the point — let it actually shine. */
.ag-holo::after {
	content: "";
	position: absolute;
	inset: -40%;
	z-index: 1;
	pointer-events: none;
	opacity: 0.9;
	mix-blend-mode: screen;
	background: linear-gradient(
		115deg,
		transparent 14%,
		var(--spectrum-crimson) 30%,
		var(--spectrum-magenta) 42%,
		var(--spectrum-violet) 54%,
		var(--spectrum-sea-teal) 66%,
		transparent 84%
	);
	background-size: 220% 220%;
	animation: ag-holo-sweep 7s ease-in-out infinite alternate;
}

/* The reference shows foil with scanlines running through it — the lines are
   what make it read as foil rather than a plain gradient. */
.ag-holo::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	background-image: repeating-linear-gradient(
		to bottom,
		var(--ag-scan-ink) 0px,
		var(--ag-scan-ink) 1px,
		transparent 1px,
		transparent var(--ag-scan-period)
	);
}

@keyframes ag-holo-sweep {
	from { background-position: 0% 50%; }
	to   { background-position: 100% 50%; }
}

/* Hero variant — the foil dimmed into a backdrop. Sheen turned well down and
   slowed, and an ink veil layered UNDER the scanlines so the dark wins behind
   copy. Content inside the hero must sit at z-index 3 or above to clear the
   veil (z2) and sheen (z1). This layer is the placeholder for the planned
   out-of-focus concert-montage loop: swap the ::after sheen for a <video>
   behind the same veil + scanlines and nothing else changes. */
.ag-holo-hero::after {
	opacity: 0.42; /* 0.24 read as page ground — the band has to pop as a band */
	animation-duration: 13s;
}

.ag-holo-hero::before {
	background-image:
		radial-gradient(140% 120% at 45% 10%, rgba(8, 8, 10, 0.18) 0%, rgba(8, 8, 10, 0.72) 85%),
		repeating-linear-gradient(
			to bottom,
			var(--ag-scan-ink) 0px,
			var(--ag-scan-ink) 1px,
			transparent 1px,
			transparent var(--ag-scan-period)
		);
}

/* ─────────────────────────────────────────────────────────────────────────
   HERO BACKGROUND VIDEO (05-03)
   The concert-montage loop the sheen above was always standing in for (see
   the ag-holo-hero note). The <video> lives in an HTML widget inside the
   hero container and slots in at the sheen's z-level (1) — the existing ink
   veil + scanlines (::before, z2) become its treatment, and the content grid
   (z3+) clears both. When the video layer is present, :has() retires the
   animated sheen so the two never stack (screen-blend over footage).
   Reduced-motion hides the <video>; the wrapper div carries the poster as an
   inline background-image, so those users get the static frame. */

/* The HTML widget's own wrapper is position:relative, so it — not the hero —
   would catch the layer's inset:0. Lift the wrapper itself out of flow to
   cover the hero; the layer then fills it. */
.ag-holo-hero > .elementor-widget:has(.ag-hero-video) {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
}

.ag-holo-hero .ag-hero-video {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background-position: center;
	background-size: cover;
	background-repeat: no-repeat;
	background-color: var(--ink);
}

.ag-holo-hero .ag-hero-video video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.ag-holo-hero:has(.ag-hero-video)::after {
	content: none; /* browsers without :has() keep the sheen — acceptable degrade */
}

@media (prefers-reduced-motion: reduce) {
	.ag-holo-hero .ag-hero-video video {
		display: none;
	}
}

/* ─────────────────────────────────────────────────────────────────────────
   ROLLING TICKER
   The strip under the hero (design language: ticker under the hero poster).
   Band ground and type live in Global Classes; this file owns the motion and
   the nowrap belt, which the v4 schema cannot express. The track only rolls
   once afterglow.js has doubled its content (`--live`) — a single copy
   snapping back at -50% would jump, so without JS it stays a static line. */

.ag-ticker {
	white-space: nowrap;
}

.ag-ticker-track {
	display: inline-flex;
}

/* All load-bearing for the loop: translateX(-50%) resolves against the
   ELEMENT box, which must therefore hug the full cloned content — but the
   track is a flex ITEM in the row-flex belt with Elementor's `min-width: 0`,
   so default flex-shrink squeezes it to the belt no matter what `width`
   says (even inline). `flex: none` is the actual fix; `width: max-content`
   then sizes the box to the clones, and the belt's own overflow (in its
   Global Class) clips. Three classes because the Global Class sheet loads
   later at (0,2,0) — the bullet-restyle registry-order trap. */
.elementor .e-con.ag-ticker-track {
	flex: none;
	width: max-content;
}

.ag-ticker-track > * {
	flex: none;
}

.ag-ticker-track--live {
	animation: ag-ticker-roll 26s linear infinite;
	will-change: transform;
}

.ag-ticker-item {
	padding-inline-end: 30px;
}

/* "Finish that vocal now" pops flat gold-chrome out of the teal line —
   wrapped by the afterglow.js phrase pass (same contract: no JS → teal,
   legible). Was coral for one round; Tommy switched it to gold 2026-08-11.
   The span class stays `ag-coral`-free: renamed ag-gold-flat. */
.ag-ticker-item .ag-gold-flat {
	color: var(--gold-chrome);
}

@keyframes ag-ticker-roll {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
	.ag-ticker-track--live {
		animation: none;
	}
}

/* ─────────────────────────────────────────────────────────────────────────
   TWO-TONE HEADING — accent tail
   "Hear it In Action": the first words stay snow, the tail goes teal-neon,
   all inside ONE h2 (the v4 heading strips inline markup, so the split is
   painted, not marked up). Stops are in ch so they track the fluid font
   size; measured for Bricolage 800 — "Hear it " ends at 5.2ch. If the
   heading copy changes, re-measure the boundary or the split lands mid-word.
   width: fit-content is load-bearing: the gradient spans the element box,
   which must hug the text for ch stops to line up. */

.ag-h2-accent-tail {
	width: fit-content;
	/* One gradient, clipped to the glyphs: snow for the head, a hard stop to
	   the flat gold-chrome token for the tail (the palette colour, not the
	   liquid-chrome ramp — Tommy's call, 2026-08-07).
	   The transition must complete INSIDE the space glyph: "Hear it" ends at
	   ~4.85ch and "in" starts at ~5.22ch, so 4.95→5.15 sits safely between —
	   a wider band bleeds snow into the first gold letter. */
	background-image: linear-gradient(
		90deg,
		var(--snow) 0,
		var(--snow) 4.95ch,
		var(--gold-chrome) 5.15ch
	);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

/* ─────────────────────────────────────────────────────────────────────────
   HAND-DRAWN SWASH — coral underline beneath a heading's first word
   The stroke is Tommy's own drawing (assets/img/hand-drawn-underline.png,
   white on transparent, cropped to the stroke's bounding box). It is used
   as a MASK with coral-neon poured through it, so the artwork and the
   colour stay independent — redraw the PNG or retune the token freely.
   Sized in ch so it tracks the fluid font size; measured for "Four"
   (3.4ch) in Bricolage 800 on the pillars heading. If the first word
   changes, re-measure its ch width. */

.ag-h2-swash-first {
	position: relative;
	padding-bottom: 0.16em;
}

.ag-h2-swash-first::after {
	content: "";
	position: absolute;
	left: 0.05ch;
	bottom: 0;
	width: 3.4ch;
	height: 0.28em; /* the stroke's own ~8.7:1 aspect at 3.4ch wide */
	background-color: var(--coral-neon);
	-webkit-mask-image: url(../img/hand-drawn-underline.png);
	mask-image: url(../img/hand-drawn-underline.png);
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────────────────
   SPECTRUM WORD — "new" in the buy section heading
   ─────────────────────────────────────────────────────────────────────────
   This used to be a three-zone gradient clipped to the glyphs, with the word
   boundaries measured in `ch` (Anton: "YOUR " ended at 4.28ch, "NEW" at
   ~7.6ch). It worked, but only ever on ONE LINE: the ramp spans the whole
   element, so the moment the heading wrapped it repeated down every line and
   coloured whichever glyphs happened to fall in that ch range. Measured wrap
   behaviour was not even monotonic — one line at 1100px+, two at 768-1024,
   one again at 600-767, three below 500 — because the buy grid re-flows
   underneath it.

   Replaced in 08-03 by wrapping the word itself: `afterglow.js` HL_PHRASES
   adds a `.ag-spectrum-word` span, the same mechanism already used for the
   hero's "K-Pop". A span is width-agnostic, so the accent now holds at every
   breakpoint instead of being switched off below 1100px, and re-measuring on
   a copy change is no longer required — only the PHRASE has to still match.

   Nothing is needed here any more; the colour comes from `.ag-spectrum-word`.
   ───────────────────────────────────────────────────────────────────────── */

/* Buy section, mobile: the price belongs with the button it prices.
   `ag-cta-actions` is [try, price, buy] in DOM order, so when the row simply
   wrapped, the break landed after `price` and it ended up beside TRY FOR FREE —
   pricing the free thing.

   The first attempt gave TRY `flex: 1 0 100%` to force the break. That worked
   but stretched TRY across the full width, so it read as the biggest, most
   important button on the page — louder than BUY NOW, which inverts the whole
   point (Tommy: "it looks a bit weird that the try for free button is bigger
   than the buy button"). A full-bleed button cannot be un-stretched with
   max-width either: flex line-breaking uses the hypothetical main size, so
   clamping the width also removes the break.

   Grid solves both at once — TRY spans the row (which forces the break) while
   `justify-self: end` keeps it at its natural width, right-aligned over the
   price + BUY pair below it. Scoped to the CTA on purpose: `ag-btn-try` is
   shared with the buy bar and the hero, so this cannot be a class variant.

   ⚠️ The grid itself is declared as an Elementor MOBILE VARIANT on
   `ag-cta-actions`, not here. `display` is class-owned and compiles to
   `.elementor .ag-cta-actions` (0,2,0), so a bare `.ag-cta-actions` rule in
   this file loses on specificity however late it sits — placing it in the
   responsive block at the end does not help. Only the child placement lives
   here, because it must be scoped to the CTA. */
@media (max-width: 767px) {
	.ag-cta-actions .ag-btn-try {
		grid-column: 1 / -1;
		justify-self: end;
	}
}

/* Dropdown menus: the underline must sit under the LETTERS, not the panel.
   The navbar's glowing bar is a ::before on `.menu-item`, insetting 0.9em from
   each edge — correct when the item hugs its text in a horizontal bar, wrong in
   a dropdown where the item is a full-width block and the bar stretches the
   whole panel. Text-decoration hugs the glyphs by construction, so the dropdown
   uses that instead and keeps the same teal. Applies to both menus. */
/* ⚠️ `elementor-nav-menu--dropdown` is on a wrapping <nav>, NOT on the <ul>.
   The list items are therefore grandchildren, so a `--dropdown > .menu-item`
   child combinator silently matches nothing. Go through the inner list. */
.ag-navbar .elementor-nav-menu--dropdown .elementor-nav-menu > .menu-item::before,
.ag-footer .elementor-nav-menu--dropdown .elementor-nav-menu > .menu-item::before {
	content: none;
}

.ag-navbar .elementor-nav-menu--dropdown .elementor-item.elementor-item-active,
.ag-navbar .elementor-nav-menu--dropdown .current-menu-item > .elementor-item,
.ag-navbar .elementor-nav-menu--dropdown .current-page-ancestor > .elementor-item,
.ag-navbar .elementor-nav-menu--dropdown .elementor-item:focus-visible,
.ag-footer .elementor-nav-menu--dropdown .elementor-item.elementor-item-active,
.ag-footer .elementor-nav-menu--dropdown .current-menu-item > .elementor-item,
.ag-footer .elementor-nav-menu--dropdown .current-page-ancestor > .elementor-item,
.ag-footer .elementor-nav-menu--dropdown .elementor-item:focus-visible {
	text-decoration: underline;
	text-decoration-color: var(--teal-neon);
	text-decoration-thickness: 2px;
	text-underline-offset: 5px;
}

/* Hover joins the same short underline — but only where hover is a real input.
   On touch, tap-hover is STICKY (iOS keeps :hover until the next tap), which is
   exactly how the removed panel-wide bar got caught lingering on a real phone.
   Gated, phones show the underline only on the current page; mouse and the
   MobileView extension (a mouse in a phone suit) get the hover affordance the
   08-06 checkpoint asked for. Keyboard focus is covered UNgated above. */
@media (hover: hover) {
	.ag-navbar .elementor-nav-menu--dropdown .elementor-item:hover,
	.ag-footer .elementor-nav-menu--dropdown .elementor-item:hover {
		text-decoration: underline;
		text-decoration-color: var(--teal-neon);
		text-decoration-thickness: 2px;
		text-underline-offset: 5px;
	}
}

/* Anchor jumps (hero → demo section) glide instead of teleporting. */
@media (prefers-reduced-motion: no-preference) {
	html {
		scroll-behavior: smooth;
	}
}


/* ─────────────────────────────────────────────────────────────────────────
   4 · SPECTRUM LIGHT LEAK
   Blurred brushstroke of light. Section seams, behind product shots.
   Accent only — per CLAUDE.md §2 the Spectrum is never a flat fill or a
   section background.
   ───────────────────────────────────────────────────────────────────────── */

.ag-light-leak {
	position: relative;
	isolation: isolate;
}

/* A BRUSHSTROKE, not a filled block. The stroke tapers at both ends and
   thins toward its edges, so it reads as a swipe of light across the dark
   rather than a blurred rectangle sitting on it. Two gradients do the work:
   the spectrum ramp along the stroke, and an elliptical mask across it. */
.ag-light-leak::before {
	content: "";
	position: absolute;
	z-index: -1;
	left: -12%;
	right: -12%;
	top: 50%;
	height: 62%;
	transform: translateY(-50%) rotate(-7deg);
	pointer-events: none;
	opacity: 0.9;
	mix-blend-mode: screen;
	filter: blur(26px);

	background-image: linear-gradient(
		100deg,
		var(--spectrum-crimson) 0%,
		var(--spectrum-magenta) 34%,
		var(--spectrum-violet) 66%,
		var(--spectrum-sea-teal) 100%
	);

	/* Taper the ends and feather the long edges. */
	-webkit-mask-image: radial-gradient(
		ellipse 46% 40% at 50% 50%,
		#000 0%,
		rgba(0, 0, 0, 0.85) 42%,
		rgba(0, 0, 0, 0.25) 72%,
		transparent 100%
	);
	mask-image: radial-gradient(
		ellipse 46% 40% at 50% 50%,
		#000 0%,
		rgba(0, 0, 0, 0.85) 42%,
		rgba(0, 0, 0, 0.25) 72%,
		transparent 100%
	);
}

/* Vertical variant for section seams. */
.ag-light-leak--seam::before {
	left: 0;
	right: 0;
	top: auto;
	bottom: -30%;
	height: 60%;
	transform: none;
	filter: blur(58px);
	opacity: 0.4;
}


/* ─────────────────────────────────────────────────────────────────────────
   Motion preferences
   ───────────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
	.ag-holo::after {
		animation: none;
		background-position: 50% 50%;
	}
}

/* ─────────────────────────────────────────────────────────────────────────
   THE CHAIN SECTION — k-one page, 05-02
   ───────────────────────────────────────────────────────────────────────── */

/* The K-One brush logo is a JPEG on pure black. On the Ink ground its
   bounding box would read as a faintly darker rectangle, so the black is
   removed optically: screen blending keeps the bright brush strokes and
   lets the ground show through everywhere else. Unsupported-blend fallback
   is a black-on-ink rectangle — visible only on close inspection.
   (The atomic e-image widget puts its classes ON the <img>, not a wrapper —
   hence img.ag-logo-center, not .ag-logo-center img.) */
.ag-chain-pivot img.ag-logo-center {
	mix-blend-mode: screen;
}

/* "one instrument" — the section's single accent. afterglow.js wraps the
   phrase in this span: CSS cannot select text, and Elementor paragraphs
   strip inline markup on save, so it cannot be authored in the editor —
   the same constraint family as the painted headings above. If the script
   never runs the phrase stays body-coloured and fully legible. */
.ag-chain-pivot .ag-hl {
	color: var(--teal-neon);
}

/* Hero H1 sizing — 54px display size cannot fit "The new gold standard" on
   one line in the hero column (608px needed, 562px available), and Tommy
   wants the break exactly there: line 1 "The new gold standard", line 2
   "for vocal production" (2026-08-11). The 18ch cap forces the break —
   measured for Bricolage 800: line 1 ends at 16.8ch, "…for" reaches
   19.3ch, line 2 is 15.0ch — and ch tracks the fluid size, so the break
   holds at every viewport. If the heading copy changes, re-measure.
   `.elementor h1…` outranks the Global Class selector (0,2,1 vs 0,2,0). */
.elementor h1.ag-hero-headline {
	font-size: clamp(2rem, 1.6rem + 1.75vw, 3rem);
	max-width: 18ch;
}

/* Hero H1 "gold" — LIQUID gold chrome (Tommy, 2026-08-11, supersedes the
   flat-token first pass): the 7-band metal ramp from ag-chrome-type,
   clipped to the glyphs. Wrapped by the afterglow.js phrase pass; the
   painted-gradient heading technique is single-line-only and the hero H1
   wraps, hence the span route. Degradation: no JS → snow, legible. */
h1.ag-hero-headline .ag-gold {
	background-image: linear-gradient(
		176deg,
		#FFF6D6 0%,
		#F6D979 22%,
		#E8B339 44%,
		#9C6B1E 60%,
		#E8B339 74%,
		#FCEAA8 90%,
		#C8901F 100%
	);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

/* Spectrum accent words — the ~100° sweep from §2, clipped to the glyphs.
   Two places use it: the hero stanza's "K-Pop", and (from 08-03) the buy
   section heading's "new", which used to be a ch-measured paint that broke
   whenever the heading wrapped. Both are wrapped by the same afterglow.js
   HL_PHRASES pass and share its degradation contract: if the script never
   runs, the word simply renders in the heading's own colour.

   ⚠️ They share the SHAPE but not the ramp, and must not be merged back into
   one rule. The hero word sits on a dark film; the buy heading sits on the
   white packshot frame. Lightening is the correct move on one and actively
   harmful on the other — measured, the +35% ramp below would drop the buy
   word's teal end from 2.64:1 to 1.87:1 on white. */
.ag-hero-copy .ag-spectrum-word,
.ag-frame-white .ag-spectrum-word {
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

/* Buy frame (on white): the palette ramp, unmodified. */
.ag-frame-white .ag-spectrum-word {
	background-image: linear-gradient(
		100deg,
		var(--spectrum-crimson),
		var(--spectrum-magenta) 35%,
		var(--spectrum-violet) 68%,
		var(--spectrum-sea-teal)
	);
}

/* Hero (on the concert film): a LIGHTENED ramp, because the palette values are
   not legible there — Tommy asked for it brighter, and the measurements agree.
   Against the worst realistic backdrop (the brightest sampled video frame under
   the veil's actual alpha at this word, 0.51 desktop / 0.56 mobile → rgb(79,25,24)),
   the palette stops measured:

     crimson 2.69   magenta 2.64   violet 1.94   sea-teal 5.36

   Only the teal end was ever passing; violet at 1.94:1 is why the word read as
   mud in the middle. "K-Pop" is 17px/400 — normal text, so the bar is the full
   4.5:1, not the 3:1 large-text allowance. Lifted per stop toward white until
   each clears it, violet furthest because it starts darkest:

     #E16D86 4.54   #D577B0 4.78   #AE93CA 5.27   #6BC6D6 7.19   (mobile all higher)

   Literal hexes, scoped to this one word, palette tokens untouched — the same
   precedent as the category pills (Phase 4). Do NOT "tidy" these back to the
   tokens; that is the bug this fixes. Re-measure if the hero film is regraded. */
.ag-hero-copy .ag-spectrum-word {
	background-image: linear-gradient(
		100deg,
		#E16D86,
		#D577B0 35%,
		#AE93CA 68%,
		#6BC6D6
	);
}

/* ─────────────────────────────────────────────────────────────────────────
   FOOTER NEWSLETTER — Elementor Pro form widget, Afterglow dress
   The form is the v3 Pro `form` widget (native and editable; Phase 9 points
   its submit action at Brevo). v3 renders its own .elementor-* markup, so
   the dress is scoped here rather than in Global Classes — the proven
   nav-menu pattern.
   ───────────────────────────────────────────────────────────────────────── */

.ag-footer form.elementor-form {
	min-width: 320px;
	max-width: 420px;
}

.ag-footer .elementor-form .elementor-form-fields-wrapper {
	display: flex;
	position: relative; /* anchors the in-field submit circle */
	margin: 0; /* displaces Elementor Pro's negative gutter margins */
}

/* Four classes on purpose: Pro's own gutter rule
   (.elementor-form-fields-wrapper.elementor-labels-above
   .elementor-field-group) ties at three and later cascade order wins. */
.ag-footer .elementor-form .elementor-form-fields-wrapper .elementor-field-group {
	padding: 0;
	margin: 0;
}

.ag-footer .elementor-form .elementor-field-type-email {
	flex: 1 1 220px;
}

/* The field's own label is screen-reader-only; the visible label is the
   "Stay in the loop" heading directly above the field. */
.ag-footer .elementor-form label {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.ag-footer .elementor-form .elementor-field-textual {
	width: 100%;
	background-color: var(--card);
	border: 1px solid var(--line);
	border-radius: 999px;
	padding: 10px 46px 10px 16px; /* right clears the in-field arrow */
	font-family: var(--font-body);
	font-size: var(--size-credit);
	color: var(--snow);
	/* The inset shadow re-paints the card ground ABOVE background-color.
	   Chrome's autofill white and extension-injected user-origin
	   !important backgrounds (password managers) both force
	   background-color, which no author rule can out-cascade — but
	   neither can cover paint layered on top of it. text-fill-color
	   likewise survives a forced color. Verified 2026-08-11 against a
	   forced-white profile. */
	box-shadow: inset 0 0 0 100px var(--card);
	-webkit-text-fill-color: var(--snow);
	caret-color: var(--snow);
	/* Backgrounds paint under the border ring; on the pill's tight curves
	   the anti-aliased 1px border blends with that underlay, and a forced
	   light background (see above) shows as a brighter arc in the corners
	   (Chrome/Firefox; Safari unaffected). Clipping the background to the
	   padding box removes the underlay entirely. */
	background-clip: padding-box;
}

.ag-footer .elementor-form .elementor-field-textual:-webkit-autofill,
.ag-footer .elementor-form .elementor-field-textual:-webkit-autofill:hover,
.ag-footer .elementor-form .elementor-field-textual:-webkit-autofill:focus {
	-webkit-box-shadow: inset 0 0 0 100px var(--card);
	-webkit-text-fill-color: var(--snow);
	caret-color: var(--snow);
}

.ag-footer .elementor-form .elementor-field-textual::placeholder {
	color: var(--muted);
}

.ag-footer .elementor-form .elementor-field-textual:focus {
	background-color: var(--card);
	border-color: var(--teal-neon);
	outline: none;
}

/* Submit lives INSIDE the pill: a 32px ghost-teal circle with an arrow,
   absolutely placed over the field's right end. The button element and its
   "Subscribe" text stay in the DOM for keyboard and screen readers — the
   text is visually collapsed (font-size: 0 keeps it in the accessibility
   tree) and the arrow is drawn by CSS. Ghost-teal per the TRY FOR FREE
   precedent. The explicit transparent background on hover/focus originally
   existed to displace Hello's [type="submit"]:hover magenta; that is now
   neutralised site-wide at the top of this file (08-02), so this is no
   longer the only thing holding the state. Keep it anyway — it is this
   component's own declared appearance, and re-deriving it from the global
   neutral would make the ghost button depend on a rule 1800 lines away. */
.ag-footer .elementor-form .elementor-field-type-submit {
	position: absolute;
	top: 50%;
	right: 5px;
	transform: translateY(-50%);
	width: auto;
}

.ag-footer .elementor-form .elementor-button[type="submit"] {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	min-height: 32px; /* Elementor's button base sets min-height: 40px */
	padding: 0;
	background-color: transparent;
	border: 1px solid var(--teal-neon);
	border-radius: 50%;
	color: var(--teal-neon);
}

.ag-footer .elementor-form .elementor-button[type="submit"]:hover,
.ag-footer .elementor-form .elementor-button[type="submit"]:focus {
	background-color: transparent;
	color: var(--teal-neon);
}

/* The label lives in nested spans that Elementor colours at higher
   specificity — the same "wins on background, silently loses on colour
   alone" trap as the carousel arrows. Colour the spans directly. */
.ag-footer .elementor-form .elementor-button[type="submit"] .elementor-button-content-wrapper {
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--teal-neon);
}

.ag-footer .elementor-form .elementor-button[type="submit"] .elementor-button-text {
	font-size: 0; /* visually collapsed, still announced as "Subscribe" */
}

/* CSS-drawn chevron instead of a '\2192' glyph — font arrow metrics sit
   off-center in the 32px circle; borders + rotate are exact. The -3px
   nudge is optical: a right-pointing chevron's visual mass sits right of
   its bounding-box center. */
.ag-footer .elementor-form .elementor-button[type="submit"] .elementor-button-content-wrapper::after {
	content: '';
	width: 8px;
	height: 8px;
	border-top: 2px solid var(--teal-neon);
	border-right: 2px solid var(--teal-neon);
	transform: rotate(45deg);
	/* The ">" occupies the eastern half of its rotated box, so the SYMBOL's
	   centre sits ~3px right of the box centre; a -6px margin shifts the
	   flex-centred box 3px left to compensate (Tommy-calibrated). */
	margin-left: -6px;
}

.ag-footer .elementor-form .elementor-button[type="submit"]:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 2px;
}

/* ================================================================
   PHASE 6 — Home news feed + single post (2026-08-12)

   The news system is deliberately boring to author: Tommy writes a
   normal WP post (title, body, featured image) and Loop Grid renders
   it through loop-item template #371. Everything visual lives here,
   hooked on the plain CSS classes set in the two v3 templates
   (#371 news card, #373 single post) — v3 widgets can't take
   Global Classes, same division as the FAQ/carousel chrome.
   ================================================================ */

/* --- News grid (Home page, loop-grid widget) --- */

.ag-news-grid .elementor-loop-container {
	gap: 24px;
}

/* The loop item document root — one card per post. v3 container
   defaults would pad it; the card owns its own geometry instead. */
.ag-news-card {
	background: var(--card);
	border: 1px solid var(--line);
	border-radius: 14px;
	overflow: hidden;
	padding: 0;
	height: 100%;
	transition: border-color 180ms ease;
}

.e-loop-item:hover .ag-news-card {
	border-color: var(--muted);
}

/* Elementor's stock inter-widget margin fights the card's own rhythm */
.ag-news-card .elementor-widget:not(:last-child) {
	margin-bottom: 0;
}

/* Thumbnail: uniform editorial crop. This is a thumbnail treatment of
   Tommy's own featured-image choice, not a presentation of a locked
   product asset — the never-crop rule protects the latter. */
.ag-news-card-media img {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

.ag-news-card-date {
	padding: 16px 20px 0;
}

.ag-news-card-date .elementor-post-info__item--type-date {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	color: var(--muted);
	text-transform: uppercase;
	letter-spacing: 0.12em;
}

.ag-news-card-title {
	padding: 6px 20px 0;
}

.ag-news-card-title h3.elementor-heading-title {
	font-family: var(--font-display);
	font-weight: 800;
	font-size: 1.25rem;
	line-height: 1.3;
	color: var(--snow);
	margin: 0;
}

.ag-news-card-title h3.elementor-heading-title a {
	color: inherit;
	transition: color 180ms ease;
}

.ag-news-card-title h3.elementor-heading-title a:hover,
.ag-news-card-title h3.elementor-heading-title a:focus-visible {
	color: var(--teal-neon);
}

.ag-news-card-excerpt {
	padding: 8px 20px 22px;
	font-family: var(--font-body);
	font-size: 0.95rem;
	line-height: 1.6;
	color: var(--mist);
}

.ag-news-card-excerpt p {
	margin: 0;
}

/* Zero-posts state — Loop Grid's own nothing-found message, enabled on
   the widget (enable_nothing_found_message). Muted, not alarmed: an empty
   news feed is a quiet fact, not an error. */
.ag-news-grid .e-loop-nothing-found-message__text {
	font-family: var(--font-body);
	font-size: var(--size-body);
	color: var(--muted);
	padding-left: 49px; /* aligns with ag-inset-h headings */
}

/* --- Single post (template #373) --- */

/* .e-con carries its own width custom properties at equal specificity and
   later in the cascade, so the measure must be set on both the property
   Elementor reads (--width) and max-width, at two-class strength. The
   min-width:0 undoes flex's min-width:auto — the 2048px featured image
   otherwise forces the container wider than the viewport. */
/* ⚠️ `--width` must be a MIN(), not a fixed length. As a flat 760px the column
   could not shrink, so a news post overflowed a 360px phone by 415px — the worst
   break on the site, and it survived every earlier sweep because the news
   SINGLE-post template was never in the tested page set (Home's news CARDS were,
   which is not the same template). Any page type that renders from its own
   template needs testing in its own right. */
.e-con.ag-post {
	--width: min(760px, 100%);
	max-width: 760px;
	margin-inline: auto;
	padding: 48px 24px 64px;
}

@media (max-width: 767px) {
	.e-con.ag-post {
		padding: 32px 20px 48px;
	}
}

.ag-post .elementor-widget {
	min-width: 0;
}

.ag-post-hero img,
.ag-post img {
	max-width: 100%;
}

.ag-post .elementor-widget:not(:last-child) {
	margin-bottom: 0;
}

.ag-post-meta .elementor-post-info__item--type-date {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	color: var(--muted);
	text-transform: uppercase;
	letter-spacing: 0.12em;
}

.ag-post-title h1.elementor-heading-title {
	font-family: var(--font-display);
	font-weight: 800;
	font-size: var(--size-poster);
	line-height: 1.15;
	color: var(--snow);
	margin: 10px 0 22px;
}

.ag-post-hero img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: 18px;
}

/* Post body is Gutenberg-authored — style the primitives on Ink.
   Scoped hard to .ag-post-content so nothing leaks site-wide. */
.ag-post-content {
	padding-top: 28px;
	font-family: var(--font-body);
	font-size: var(--size-body);
	line-height: 1.7;
	color: var(--mist);
}

.ag-post-content p {
	margin: 0 0 1.2em;
}

.ag-post-content h2,
.ag-post-content h3 {
	font-family: var(--font-display);
	font-weight: 800;
	color: var(--snow);
	line-height: 1.25;
	margin: 1.6em 0 0.6em;
}

.ag-post-content h2 { font-size: 1.5rem; }
.ag-post-content h3 { font-size: 1.2rem; }

.ag-post-content a {
	color: var(--teal-neon);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.ag-post-content ul,
.ag-post-content ol {
	margin: 0 0 1.2em;
	padding-inline-start: 1.4em;
}

.ag-post-content li {
	margin-bottom: 0.4em;
}

.ag-post-content img {
	max-width: 100%;
	height: auto;
	border-radius: 14px;
}

.ag-post-content blockquote {
	margin: 1.4em 0;
	padding: 0.2em 0 0.2em 1.2em;
	border-left: 2px solid var(--line);
	color: var(--snow);
}

.ag-post-content figcaption {
	font-family: var(--font-mono);
	font-size: var(--size-credit);
	color: var(--muted);
	margin-top: 0.5em;
}

/* The Home mini-hero block that used to sit here (ag-mini-hero, -card,
   -logo, -render, plus the sticker reposition) was REMOVED in 08-02. Its
   Global Classes were retired when 08-01 replaced the Home hero with the
   compact mixwave band, leaving both the classes and these rules orphaned.
   The three radial spectrum glows it carried were not lost — 08-01 copied
   them into `ag-news-glow` further down this file, which is why deleting
   this block changes nothing on Home. */

/* Letter typesetting is ART-DIRECTED at full width: Tommy hand-placed line
   breaks (2026-08-12) tuned for the 720px column, and no word may ever break
   mid-word (hyphens: manual honours only explicit &shy;, i.e. none).
   Below 900px the column starts compressing and his breaks would
   double-wrap into fragments — so the <br>s switch off and the prose falls
   back to natural ragged-right flow (justify without hyphens is unreadable
   in narrow columns). The breaks are still in the content; only their
   rendering is viewport-dependent. */
.ag-letter-body p {
	hyphens: manual;
	-webkit-hyphens: manual;
}

@media (max-width: 900px) {
	.ag-letter .ag-letter-body {
		text-align: start;
	}

	.ag-letter .ag-letter-body p br {
		display: none;
	}
}

/* About letter footer — the signature row reads as the card's own footer:
   it cancels the frame's padding to touch three edges (negative margins in
   the Global Class), and carries the spectrum as a DARK BAND — the ramp at
   low alpha over plasma, with a 1px spectrum hairline on its top edge (the
   CTA precedent). Not a flat bright fill; the design language's accent rule
   holds. */
.ag-letter-foot {
	position: relative;
	background:
		linear-gradient(
			100deg,
			rgba(209, 31, 69, 0.28),
			rgba(190, 46, 134, 0.24) 40%,
			rgba(110, 63, 143, 0.26) 70%,
			rgba(43, 174, 196, 0.24)
		),
		var(--plasma);
	border-bottom-left-radius: 17px;  /* frame r18 minus its 1px border */
	border-bottom-right-radius: 17px;
}

.ag-letter-foot::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 1px;
	background: linear-gradient(
		100deg,
		var(--spectrum-crimson),
		var(--spectrum-magenta) 40%,
		var(--spectrum-violet) 70%,
		var(--spectrum-sea-teal)
	);
}

/* Square signature photo — crop whatever ratio Tommy uploads. Sized well
   past the slim band and pulled up with a negative top margin so it
   straddles the band's top edge, half inside the letter area (the
   profile-card overlap). The hairline (z0 ::before) stays visible left of
   the photo; the photo needs its own plane above the band's paint. */
img.ag-letter-foot-photo {
	flex: 0 0 auto;   /* the flex row was squeezing it otherwise */
	position: relative;
	z-index: 1;
	width: 310px;   /* matches the Pontus portrait on the landing page (311px) */
	height: 310px;
	object-fit: cover;
	margin-top: -185px;
	box-shadow: 0 18px 44px rgba(8, 8, 10, 0.55);
	border: 1px solid var(--line);
}

/* --muted is tuned for Ink and dips below AA on the lighter band; mist holds. */
.ag-letter-foot .ag-type-credit {
	color: var(--mist);
}

/* ============================================================
   PHASE 7 — SUPPORT (docs home · category pages · contact form)
   Layout (flex, gap, padding, radius, icon size) lives in the
   Global Classes ag-docs-grid / -card / -icon / -back; surface,
   border, type and hover character live here, per the division
   of labour. Support reads calm and utilitarian: no scanlines,
   no floods, teal is the only interactive accent.
   ============================================================ */

/* --- Docs category nav (component #470, on all 8 pages) ------
   Flat icon+label cards, auto-width, wrapping — the persistent
   top nav of the docs section (checkpoint revision: timeoff-docs
   "always-visible nav + content window" pattern, translated to a
   top strip). Each card is an atomic flexbox rendered as an <a>
   (container link), so link styling is neutralised and the whole
   card is the hit target. */
.ag-docs-card {
	background: var(--card);
	border: 1px solid var(--line);
	color: inherit;
	text-decoration: none;
	transition: border-color 200ms ease, transform 200ms ease;
}
/* Hazard #1: atomic containers default to width:100%, which
   stacks the cards full-width. body prefix outranks the base
   rule in base-desktop.css, which loads after this file. */
body .ag-docs-card {
	width: auto;
	flex: 0 0 auto;
}
.ag-docs-card:hover,
.ag-docs-card:focus-visible {
	border-color: var(--teal-neon);
	transform: translateY(-2px);
}
.ag-docs-card:focus-visible {
	outline: 2px solid var(--teal-neon);
	outline-offset: 2px;
}

/* Card labels: the subhead class at nav size. body+2 classes
   outranks the compiled global-class rule, which loads later. */
body .ag-docs-card .ag-type-subhead {
	font-size: 15px;
	margin: 0;
}

/* The e-svg widget inlines the icon file; size comes from the
   ag-docs-icon class on the widget box. */
.ag-docs-icon svg {
	display: block;
	width: 100%;
	height: 100%;
}

/* Current category — lit card. Pure CSS: WP's page-id body class
   crossed with each card's href, so the shared nav component
   needs no per-page variance (overrides carry content only) and
   no JS. Page ids are the Phase 7 support pages; update if a
   category page is ever recreated. */
.page-id-446 .ag-docs-card[href$="/support/account-management/"],
.page-id-447 .ag-docs-card[href$="/support/installing-k-one/"],
.page-id-448 .ag-docs-card[href$="/support/minimum-requirements/"],
.page-id-449 .ag-docs-card[href$="/support/k-one-manual/"],
.page-id-450 .ag-docs-card[href$="/support/faq/"],
.page-id-451 .ag-docs-card[href$="/support/known-issues/"],
.page-id-452 .ag-docs-card[href$="/support/contact/"] {
	border-color: var(--teal-neon);
	box-shadow: 0 0 12px 0 rgba(68, 214, 201, 0.25);
}
.page-id-446 .ag-docs-card[href$="/support/account-management/"] .ag-type-subhead,
.page-id-447 .ag-docs-card[href$="/support/installing-k-one/"] .ag-type-subhead,
.page-id-448 .ag-docs-card[href$="/support/minimum-requirements/"] .ag-type-subhead,
.page-id-449 .ag-docs-card[href$="/support/k-one-manual/"] .ag-type-subhead,
.page-id-450 .ag-docs-card[href$="/support/faq/"] .ag-type-subhead,
.page-id-451 .ag-docs-card[href$="/support/known-issues/"] .ag-type-subhead,
.page-id-452 .ag-docs-card[href$="/support/contact/"] .ag-type-subhead {
	color: var(--teal-neon);
}

/* --- The content window --------------------------------------
   The panel each category "opens in" below the nav strip. */
.ag-docs-window {
	background: var(--card);
	border: 1px solid var(--line);
}

/* --muted is tuned for Ink (4.72:1) and dips below AA on the
   lighter window card — same finding as the A/B player (Phase 4).
   #80808B is the documented contrast-corrected literal (4.71:1
   on card); do not "tidy" it back to the token. Covers the
   min-req notes, placeholder markers, and form helper lines. */
.ag-docs-window .ag-type-note,
.ag-docs-window .ag-contact-form .elementor-field-type-html {
	color: #80808B;
}

/* The ag-docs-back category back-link rules were REMOVED in 08-02. The
   Support checkpoint redesign replaced per-category back-links with the
   persistent docs card strip, so the class was never placed on a page —
   it and these rules were orphaned from the day they were written. */

/* --- Inline links in running text ----------------------------
   First inline <a> in html-v3 content shipped in Phase 7 (FAQ
   answers, the min-requirements trial note). Scoped to the two
   type classes so chrome/nav/buttons stay untouched; the About
   letter carries no links, and Tommy's manual <u> spans are
   unaffected (different element). ag-type-credit joined in 09-01 for
   the footer's legal-row "Privacy policy" link. */
.ag-type-body a,
.ag-type-note a,
.ag-type-credit a {
	color: var(--teal-neon);
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* --- Form response messages (09-01) --------------------------
   Pro renders the post-submit line as .elementor-message in #333,
   which is invisible on Ink/Card (measured 1.2:1 on both forms).
   Both forms share this: success in teal, errors in coral. Placed
   at (0,2,1) so it beats Pro's widget CSS regardless of order. */
body .elementor-form .elementor-message {
	font-family: var(--font-body);
	font-size: 14px;
	line-height: 1.5;
	margin-top: 12px;
	color: var(--mist);
}
body .elementor-form .elementor-message.elementor-message-success {
	color: var(--teal-neon);
}
body .elementor-form .elementor-message.elementor-message-danger {
	color: var(--coral-neon);
}
/* Pro's tick icon is a fixed #008a20 fill; repainted in teal-neon
   (#44D6C9 literal — tokens cannot reach inside a data URI). */
body .elementor-form .elementor-message.elementor-message-svg::before {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='11' fill='none' viewBox='0 0 14 11'%3E%3Cpath fill='%2344D6C9' fill-rule='evenodd' d='M4.75 8.15 1.6 5 .55 6.05l4.2 4.2 9-9L12.7.2z' clip-rule='evenodd'/%3E%3C/svg%3E");
}

/* --- Contact form (Brevo attaches in Phase 9) ----------------
   Scoped to the v3 form widget via _css_classes. The submit
   button's magenta #cc3366 hover is Hello Elementor's global
   rule — KNOWN and WAIVED until the Phase 8 site-wide fix; do
   not neutralise it here. */
.ag-contact-form .elementor-field-group {
	margin-bottom: 14px;
}
.ag-contact-form .elementor-field-label {
	font-family: var(--font-body);
	font-size: 14px;
	color: var(--snow);
}
.ag-contact-form .elementor-mark-required .elementor-field-label:after {
	color: var(--coral-neon);
}

/* Inputs: the 05-02 hardening pattern — repaint, don't cascade.
   The inset shadow + text-fill defeat both Chrome autofill and
   extension-injected user-origin !important backgrounds. */
.ag-contact-form .elementor-field-textual {
	background: var(--card);
	box-shadow: inset 0 0 0 100px var(--card);
	-webkit-text-fill-color: var(--snow);
	background-clip: padding-box;
	color: var(--snow);
	caret-color: var(--teal-neon);
	border: 1px solid var(--line);
	border-radius: 10px;
	font-family: var(--font-body);
	font-size: 15px;
	padding: 10px 14px;
}
.ag-contact-form .elementor-field-textual::placeholder {
	color: var(--muted);
}
.ag-contact-form .elementor-field-textual:focus {
	border-color: var(--teal-neon);
	box-shadow: inset 0 0 0 100px var(--card),
		0 0 0 3px rgba(68, 214, 201, 0.18);
	outline: none;
}
.ag-contact-form .elementor-field-textual:-webkit-autofill,
.ag-contact-form .elementor-field-textual:-webkit-autofill:hover,
.ag-contact-form .elementor-field-textual:-webkit-autofill:focus {
	-webkit-box-shadow: inset 0 0 0 100px var(--card);
	-webkit-text-fill-color: var(--snow);
}

/* Helper lines are `html`-type fields in the form repeater
   (Tommy edits them like any other field). Pulled up under the
   field they annotate. */
.ag-contact-form .elementor-field-type-html {
	font-family: var(--font-body);
	font-size: 13px;
	color: var(--muted);
	margin-top: -8px;
}

/* Submit: ghost teal per the TRY FOR FREE precedent — gold
   stays the purchase moment, and this is not one. Rest state
   only; see the waiver note above for hover. */
.ag-contact-form .elementor-button[type="submit"] {
	background: transparent;
	border: 1px solid var(--teal-neon);
	border-radius: 999px;
	color: var(--teal-neon);
	font-family: var(--font-body);
	font-size: 14px;
	letter-spacing: 0.04em;
	padding: 12px 28px;
}

/* --- K-One manual (page #449) --------------------------------
   Six v3 text-editor chapters inside ag-manual-body, plus a
   sticky "On this page" rail in ag-manual-toc. The Global
   Classes own the two-column layout, the sticky position and the
   tablet/mobile collapse (rail moves above the content); type,
   surface and link character live here, per the division of
   labour. Everything is scoped under the two ag-manual-* classes
   so nothing leaks into the other docs windows.

   The scrollspy that lights the current TOC link is
   armManualToc() in afterglow.js. Without JS the rail is plain
   anchor links — fully functional, nothing hidden. */

.ag-manual-body {
	font-family: var(--font-body);
}

/* Chapter headings double as chapter separators (hairline +
   breathing room). Each chapter is its own widget, so
   :first-child cannot reach "the document's first heading" from
   here — the opening chapter is de-spaced by its anchor id. */
.ag-manual-body h3 {
	font-family: var(--font-display);
	font-weight: 800;
	font-size: 26px;
	line-height: 1.2;
	color: var(--snow);
	margin: 44px 0 16px;
	padding-top: 36px;
	border-top: 1px solid var(--line);
}
.ag-manual-body h3#overview {
	margin-top: 6px;
	padding-top: 0;
	border-top: 0;
}
.ag-manual-body h4 {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 18px;
	line-height: 1.3;
	color: var(--snow);
	margin: 30px 0 10px;
}

/* Running text mirrors ag-type-body, capped at the ag-measure
   640px reading width; the rail takes the rest of the row. */
.ag-manual-body p,
.ag-manual-body ul,
.ag-manual-body ol {
	font-size: var(--size-body);
	line-height: 1.7;
	color: var(--mist);
	max-width: 640px;
	margin: 0 0 14px;
}
.ag-manual-body ul,
.ag-manual-body ol {
	padding-left: 22px;
}
.ag-manual-body li {
	margin-bottom: 6px;
}
.ag-manual-body li::marker {
	color: var(--teal-neon);
}
.ag-manual-body strong {
	color: var(--snow);
}
.ag-manual-body a {
	color: var(--teal-neon);
	text-decoration: underline;
	text-underline-offset: 2px;
	/* Legal text prints bare URLs as link text (EULA §14: ec.europa.eu/…/odr).
	   Unbroken, one overflowed a 360px phone by 34px (09-04). */
	overflow-wrap: anywhere;
}
.ag-manual-body code {
	font-family: var(--font-mono);
	font-size: 13px;
	color: var(--snow);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: 2px 7px;
	white-space: nowrap;
}
/* Preset paths overflow a phone screen — let the chip scroll
   itself rather than the page. */
.ag-manual-body p.ag-manual-path {
	overflow-x: auto;
}

/* The signal-flow line: mono, boxed, allowed to wrap — each
   arrow break reads as the chain continuing. */
.ag-manual-body p.ag-manual-flow {
	font-family: var(--font-mono);
	font-size: 14px;
	line-height: 1.9;
	color: var(--snow);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: 10px;
	padding: 14px 18px;
}

/* Tommy's *Note:* asides — quieter, set off by a rule. #80808B
   is the documented contrast-corrected muted for the card ground
   (same literal as the docs-window notes; do not tidy to token). */
.ag-manual-body p.ag-manual-note {
	border-left: 2px solid var(--line);
	padding-left: 14px;
	font-size: 14px;
	color: #80808B;
}

/* Anchor landings: keep the heading clear of the viewport edge.
   The navbar is not sticky (Phase 3 decision), so this is
   comfort, not chrome clearance. */
.ag-manual-body h3[id],
.ag-manual-body h4[id] {
	scroll-margin-top: 24px;
}

/* --- The "On this page" rail --------------------------------- */
.ag-manual-toc {
	background: var(--surface);
	border: 1px solid var(--line);
}
/* Cap + internal scroll only where the rail is sticky (desktop);
   on tablet/mobile it is a static block above the content and a
   nested scrollbar there would be a trap. */
@media (min-width: 1025px) {
	.ag-manual-toc {
		max-height: calc(100vh - 48px);
		overflow-y: auto;
	}
}
.ag-manual-toc .ag-manual-toc-label {
	font-family: var(--font-mono);
	font-size: 11px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: #80808B;
	margin: 0 0 10px;
}
.ag-manual-toc ul {
	list-style: none;
	margin: 0;
	padding: 0;
}
.ag-manual-toc ul ul {
	margin: 2px 0 4px;
	padding-left: 12px;
}
.ag-manual-toc a {
	display: block;
	padding: 5px 8px;
	font-family: var(--font-body);
	font-size: 13.5px;
	line-height: 1.35;
	color: var(--mist);
	text-decoration: none;
	border-left: 2px solid transparent;
	border-radius: 0 6px 6px 0;
}
.ag-manual-toc a:hover,
.ag-manual-toc a:focus-visible {
	color: var(--teal-neon);
}
.ag-manual-toc a.is-current {
	color: var(--teal-neon);
	border-left-color: var(--teal-neon);
	background: rgba(68, 214, 201, 0.06);
}

/* Collapsed TOC (Tommy, round 2): wherever the rail stacks above
   the content (≤1024 — the same width the ag-manual class goes
   column), the link list folds behind a hamburger toggle so a
   phone reader isn't scrolled past 23 links before the manual
   starts. armManualTocToggle() in afterglow.js adds the --armed
   class and the button semantics; without JS nothing is hidden —
   the list simply stays open, same contract as the reveals. */
@media (max-width: 1024px) {
	.ag-manual-toc--armed .ag-manual-toc-label {
		display: flex;
		align-items: center;
		gap: 10px;
		cursor: pointer;
		padding: 6px 0;
		-webkit-user-select: none;
		user-select: none;
	}
	/* The hamburger: three teal bars, drawn not typed — glyph
	   rendering varies across platforms, gradients do not. */
	.ag-manual-toc--armed .ag-manual-toc-label::before {
		content: "";
		flex: 0 0 auto;
		width: 14px;
		height: 10px;
		background: repeating-linear-gradient(
			to bottom,
			var(--teal-neon) 0 2px,
			transparent 2px 4px
		);
	}
	.ag-manual-toc--armed .ag-manual-toc-label:focus-visible {
		outline: 2px solid var(--teal-neon);
		outline-offset: 2px;
	}
	.ag-manual-toc--armed:not(.is-open) ul {
		display: none;
	}
	.ag-manual-toc--armed:not(.is-open) .ag-manual-toc-label {
		margin: 0;
	}

	/* The bar is sticky at these widths (round 3 — position lives in
	   the ag-manual-toc class's tablet variant, like the desktop
	   rail's). The shadow separates it from content sliding beneath;
	   an opened list taller than the phone screen scrolls itself. */
	.ag-manual-toc--armed {
		box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
	}
	.ag-manual-toc--armed.is-open {
		max-height: calc(100vh - 16px);
		overflow-y: auto;
	}

	/* Anchor landings must clear the pinned bar (~63px tall), not
	   just the viewport edge — otherwise every TOC jump hides its
	   own heading under the bar. */
	.ag-manual-body h3[id],
	.ag-manual-body h4[id] {
		scroll-margin-top: 84px;
	}
}

/* Self-contained narrow tweak (nothing here fights a rule
   defined later — only these manual rules touch these elements). */
@media (max-width: 767px) {
	.ag-manual-body h3 {
		font-size: 22px;
	}
}

/* --- Known issues list (page #451) ---------------------------
   A Loop Grid over the tlj_known_issue post type (registered in
   child functions.php), loop-item template #544. Tommy adds
   entries in wp-admin → Known issues → Add new — same flow as
   News, separate type so the feeds can never cross. Design is
   the docs register at its calmest: hairline-separated rows,
   mono date, no cards, no links (an issue is a list entry, not
   a page). */
.ag-issues-grid .elementor-loop-container {
	gap: 0;
}
.ag-issue-item {
	border-top: 1px solid var(--line);
	padding: 22px 0 10px;
	max-width: 640px; /* the ag-measure reading width */
}
/* Elementor's kit styles theme-post-title at
   `.elementor-widget-theme-post-title .elementor-heading-title`
   (0,2,0) with the primary global (teal) — hazard family #16/#48:
   match the specificity, (0,2,1) here. */
body .ag-issue-title .elementor-heading-title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 17px;
	line-height: 1.35;
	color: var(--snow);
	margin: 0;
}
.ag-issue-date .elementor-icon-list-text {
	font-family: var(--font-mono);
	font-size: 12px;
	letter-spacing: 0.04em;
	color: #80808B; /* contrast-corrected muted on card — documented literal */
}
.ag-issue-date .elementor-icon-list-item {
	padding: 0;
}
.ag-issue-date {
	margin: 2px 0 8px;
}
.ag-issue-body {
	font-family: var(--font-body);
	font-size: 15px;
	line-height: 1.65;
	color: var(--mist);
}
/* The empty state ("No known issues…") is the loop grid's own
   nothing-found paragraph. */
.ag-issues-grid .e-loop-nothing-found-message {
	font-family: var(--font-body);
	font-size: 15px;
	color: #80808B;
}

/* ============================================================
   PHASE 8 · 08-01 — TOMMY'S SITE-REVISION LIST (2026-08-16)
   ============================================================ */

/* --- Home hero (mixwave-style) -------------------------------
   The section ground is PURE #000 (in the ag-home-hero class),
   matching k-one-hero-home.png's baked ground — measured, and a
   documented imagery-matching exception to Ink (the white
   packshot frame precedent).

   THE BAND MECHANIC (measured on mixwave.com): the image renders
   at natural aspect, full width, anchored top, inside a band that
   clips at `min(58vh, 48.06vw)`. 48.06vw IS the image's rendered
   height (1162/2418 aspect) — so when 58vh is smaller (wide
   windows) the unit's bottom is cropped, and when 48.06vw wins
   (narrow windows) the band equals the image exactly and the
   whole unit is visible. Tune the 58vh knob to taste; re-derive
   48.06vw only if the hero image is ever re-exported. */
.ag-home-hero-band {
	position: relative;
	overflow: hidden;
	/* THE CROP MATH (fractions measured from the PNG's pixel content;
	   checkpoint round 1, Tommy: compact section, keep ALL the glow):
	   the unit's glow begins at row 476 of the 2418-wide render, so the
	   img offset below skips exactly the dead black and nothing else.
	   Glow + unit together are the bottom 28.4vw of the image.
	   Band = min(36vh, 28.4vw):
	   wide windows -> 36vh wins -> the unit's bottom is cropped;
	   narrow windows -> 28.4vw wins -> glow and unit fully visible.
	   36vh is the taste knob; re-measure the fractions only if the hero
	   image is re-exported. */
	height: min(36vh, 28.4vw);
	/* Escape the hero's 24px inline padding — the image band runs
	   edge-to-edge like the reference. */
	width: calc(100% + 48px);
	margin-inline: -24px;
}
.ag-home-hero-band img {
	position: absolute;
	top: -19.6vw; /* skip the baked-in black; the glow starts right here */
	left: 0;
	width: 100%;
	height: auto;
	display: block;
}

/* --- News section: the spectrum glow backdrop ----------------
   Ported from the retired mini-hero (same three radial glows,
   same "accent wash, never a flood" rationale). The class
   carries position:relative; isolation keeps the -1 layer
   inside this section's stacking context so the glows paint
   under the news cards but above nothing else. */
.ag-news-glow {
	overflow: hidden;
	isolation: isolate;
}
.ag-news-glow::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image:
		radial-gradient(50% 60% at 18% 30%, rgba(209, 31, 69, 0.40), transparent 60%),
		radial-gradient(55% 60% at 85% 70%, rgba(43, 174, 196, 0.36), transparent 62%),
		radial-gradient(60% 70% at 55% 110%, rgba(110, 63, 143, 0.45), transparent 65%);
}

/* --- CRT scanlines on the two spectrum beds (Tommy, 2026-08-31) --
   The News glow above and the A/B band's light-leak both paint their
   spectrum on ::before at z -1 inside an isolated context; the lines
   take ::after at the same z, so they paint after the glow (on top of
   it) and still under the cards / player — the hero's layering. Ink
   lines, as on the holo foil: invisible on the bare ground, visible
   only where the spectrum lifts it, so the dark still wins. Scoped
   to the A/B band rather than every ag-light-leak on purpose — no
   scanlines anywhere Tommy has not asked for (2026-08-06 rule). */
.ag-news-glow::after,
.ag-abx-band.ag-light-leak::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image: repeating-linear-gradient(
		to bottom,
		var(--ag-scan-ink) 0px,
		var(--ag-scan-ink) 1px,
		transparent 1px,
		transparent var(--ag-scan-period)
	);
}

/* --- Footer social icons -------------------------------------
   Three e-svg links under the lockup. The SVGs bake mist strokes
   (#B9B9C2) with an accessible <title>; the stroke is set on the
   svg ROOT, so one hover rule recolours the whole glyph, plus
   the Instagram dot's fill. Hrefs are placeholders until Tommy
   supplies the real profile URLs. */
/* Footer row 2 (checkpoint round: social left ↔ subscribe right).
   The newsletter row right-aligns its content via a local style, so
   the auto margin walks the icon group to the row's left edge. */
.ag-footer .ag-social {
	margin-right: auto;
}
.ag-social svg {
	width: 24px;
	height: 24px;
	display: block;
	/* The root fill attribute does not survive e-svg rendering, so the
	   glyph inherits fill from the link colour (hazard #17). These glyphs
	   are stroke-only, so the neutral must be declared explicitly whatever
	   that inherited colour is — this rule is still required after the
	   Hello reset was neutralised at the top of this file; it is about the
	   stripped attribute, not about the magenta. The Instagram dot keeps
	   its own fill attribute, which outranks inheritance. */
	fill: none;
}
/* Tap target (08-02). The glyph is 24x24 — fine for a mouse, well under the
   ~44px a fingertip needs. Rather than scale or pad the icon (either would
   move the row Tommy approved at the 08-01 checkpoint), an invisible centred
   box extends the LINK's hit area while the layout stays byte-identical.

   Height takes the full 44px; width takes 38px, which is the icon's 24px plus
   its 14px flex gap. That is deliberate, not a rounding error: the icons sit
   38px centre-to-centre, so a 44px-wide box would overlap its neighbour and
   the later link in DOM order would steal taps from the earlier one. At 38px
   each icon owns exactly its own half of the gap, and no target overlaps. */
.ag-social a {
	position: relative;
}

.ag-social a::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 38px;
	height: 44px;
	transform: translate(-50%, -50%);
}

.ag-social a:hover svg,
.ag-social a:focus-visible svg {
	stroke: var(--teal-neon);
}
.ag-social a:hover svg circle[fill],
.ag-social a:focus-visible svg circle[fill] {
	fill: var(--teal-neon);
}

/* --- About: the printed letter -------------------------------
   about-bg.png behind the framed section, darkened, with CRT
   scanlines ON TOP of the image (Tommy's explicit ask — this
   satisfies the 2026-08-06 "no scanlines without asking" rule,
   and finally consumes --ag-scan-over, overlay mode's reference
   knob). Page-scoped at (0,2,0) so it overrides the compiled
   ag-section-frame background without a same-prop class stack.
   Layer order: scanlines, scrim, image. */
.page-id-389 .ag-section-frame {
	background:
		repeating-linear-gradient(
			to bottom,
			var(--ag-scan-over) 0px,
			var(--ag-scan-over) 1px,
			transparent 1px,
			transparent var(--ag-scan-period)
		),
		linear-gradient(rgba(8, 8, 10, 0.42), rgba(8, 8, 10, 0.60)),
		url("/wp-content/uploads/2026/08/about-bg.png") center / cover no-repeat;
}

/* Checkpoint round 2 (Tommy): the signature band sits closer to the
   letter so the portrait rides onto the white paper. The spacing knob
   is the ag-letter-foot CLASS's margin block-start (104px -> 16px in
   Elementor) — a file-level rule here loses to the compiled class. */

/* The paper itself: white ground + black print. Ink-family
   near-black rather than #000 (design-language neutrals rule);
   the eyebrow steps down to a print-grey that holds AA on white.
   Tommy's stored letter elements are untouched — these scoped
   rules recolour them only while they sit on the paper. */
.ag-paper {
	max-width: 100%;
	box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
/* Hazard #1: the container defaults to width:100%, stretching the sheet
   past its 720px letter column and leaving the text left of centre.
   fit-content hugs the column + padding; body prefix beats base CSS. */
body .ag-paper {
	width: fit-content;
}
.ag-paper .ag-type-body {
	color: #17171A;
}
.ag-paper .ag-eyebrow {
	color: #55555E;
}
/* ⚠️ A `body .ag-paper { padding: … }` rule used to sit here, added in 08-01 and
   carried into 08-03. It never did anything, and could never have done anything:
   Elementor compiles a Global Class as `.elementor .ag-paper` — specificity
   (0,2,0) — which beats `body .ag-paper` at (0,1,1) regardless of file order.
   The paper's padding therefore stayed at its desktop 56px on every screen,
   which is most of why the letter column was strangled.

   This extends hazard #18: it is not enough to notice that a value is
   CLASS-owned, because the obvious workaround — prefixing `body ` to outrank
   the class — still loses. A class-owned value must be changed ON THE CLASS,
   as a breakpoint variant. The mobile padding now lives there. */

/* ⚠️ The `fit-content` above is correct at desktop and INVERTS on a phone.
   `fit-content` resolves toward MIN-content as the available width shrinks, so
   once the viewport drops below the letter's natural column the sheet stops
   hugging the column and starts strangling it. Measured at 360 before this
   fix: paper 179px, letter body 67px, letter height 10,808px — the founder's
   letter rendered one or two words per line for its entire length.

   Below the letter's art-directed range the sheet should simply be the column:
   fill the available width and let the prose set its own measure. 720px stays
   the cap, so nothing changes at or above the width the letter was tuned for.

   This is the general shape of the whole 08-03 defect — a desktop-correct rule
   with no narrow counterpart — and the reason a `fit-content`/`max-content`
   width should always be paired with one. */
@media (max-width: 767px) {
	body .ag-paper {
		width: 100%;
		max-width: 720px;
	}
}


/* ═════════════════════════════════════════════════════════════════════════
   RESPONSIVE OVERRIDES — must be LAST in this file
   ═════════════════════════════════════════════════════════════════════════
   These narrow-width rules override component rules that are defined further
   up. Two of them were originally written mid-file and silently did nothing,
   because the base rule they had to beat lives LATER (`img.ag-letter-foot-photo`
   ~line 2400, `.ag-footer .ag-social` ~line 2770) and won the tie on order at
   equal specificity. Raising specificity would have worked and would have been
   the wrong fix: it hides the ordering problem and the next rule added below
   them breaks again.

   So: anything that has to beat a component rule goes HERE, at the end. That is
   the mirror of the Hello-reset block at the TOP of the file — resets first so
   components beat them, responsive last so it beats components. Do not relocate
   either block.
   ───────────────────────────────────────────────────────────────────────── */

@media (max-width: 767px) {

	/* --- About signature ---------------------------------------------------
	   At 310px the portrait is as wide as the phone, so the credit was pushed
	   underneath it. Tommy asked for it "just small enough to make room for my
	   name next to it": 112px leaves the name its own column even at 360, and
	   the upward overlap onto the paper scales with the photo. */
	img.ag-letter-foot-photo {
		width: 112px;
		height: 112px;
		margin-top: -56px;
	}

	/* --- Footer ------------------------------------------------------------
	   Left-aligning every row left the hamburger stranded under the lockup and
	   the social + newsletter blocks hanging off the left edge. Tommy's call:
	   lockup and toggle share the top row, everything below is centred. */
	.ag-footer .ag-footer-row:first-child {
		flex-direction: row;
		flex-wrap: nowrap;
		align-items: center;
		justify-content: space-between;
		gap: var(--space-sm);
		/* anchors the dropdown panel below — see the overlay note */
		position: relative;
	}

	/* The row centres its CHILDREN, but the nav widget's own box is 76px tall
	   against the lockup's 65px and the toggle sits at the top of it — so the
	   toggle read 16px high of the logo even though the row alignment was
	   correct. Centre the toggle inside its own wrapper too. Targeted
	   structurally (the row has exactly two children) rather than by Elementor's
	   generated element id, which changes on re-save. */
	.ag-footer .ag-footer-row:first-child > *:last-child {
		display: flex;
		align-items: center;
		justify-content: flex-end;
	}

	/* Opening the footer menu grew the footer by 141px and shoved everything
	   below it down. 08-02 deliberately left this panel in normal flow, on the
	   reasoning that a footer has nothing below it to cover — but it does have
	   its own rows below, and pushing them is worse than overlaying them
	   (Tommy, 08-03). It now behaves like the navbar's: an overlay anchored to
	   the row, so the footer's height never changes. */
	.ag-footer .elementor-nav-menu--dropdown {
		position: absolute;
		top: calc(100% + 10px);
		right: 0;
		width: max-content;
		min-width: 200px;
		z-index: 30;
		margin-top: 0;
	}

	.ag-footer .ag-footer-row:not(:first-child) {
		flex-direction: column;
		align-items: center;
		justify-content: center;
		text-align: center;
	}

	/* The desktop rule uses `margin-right: auto` to walk the icons to the row's
	   left edge. An auto margin absorbs all free space on that side and so
	   OVERRIDES align-self — centring cannot work until it is cancelled. */
	.ag-footer .ag-social {
		margin-right: 0;
		justify-content: center;
	}

	/* `align-items` on a child governs that child's OWN children; the child's
	   position within the column is `align-self`. Both are needed here. */
	.ag-footer .ag-footer-row:not(:first-child) > * {
		align-self: center;
		align-items: center;
		text-align: center;
	}

	/* --- Footer newsletter -------------------------------------------------
	   The email field is `flex: 1 1 220px` and, like every flex item, carries an
	   implicit `min-width: auto` — so it refuses to shrink below its content and
	   pushed the whole newsletter block 23px past the row at 360. Zeroing the
	   basis and the min-width lets it take whatever room the phone has. */
	.ag-footer .elementor-form .elementor-field-type-email {
		flex: 1 1 0;
		min-width: 0;
	}

	.ag-footer .ag-cta-copy {
		min-width: 0;
		max-width: 100%;
	}
}


/* ═══════════════════════════════════════════════════════════════════════════
   SCROLL REVEAL — the site's only scroll-linked motion (08-05)
   ═══════════════════════════════════════════════════════════════════════════

   ⚠️ THIS BLOCK MUST STAY LAST IN THE FILE.
   afterglow.css is ordered deliberately: resets (top) → components (middle) →
   responsive (bottom) → this. Hazard #22: two rules written mid-file in 08-03
   silently did nothing because the component rules they had to beat were
   defined later and won on order. Raising specificity "fixes" that while
   hiding it. Do not relocate this block, and do not add anything after it.

   WHY THE HIDDEN STATE IS ARMED BY JS, NOT BY CSS
   ------------------------------------------------
   The obvious way to write this is `.ag-reveal { opacity: 0 }` with JS adding
   a class to reveal. That is a trap: if the observer never runs — script
   blocked, a JS error earlier in the file, an ancient browser — every section
   stays at opacity 0 and the site is a blank page. So the hidden state lives
   behind `--armed`, which ONLY JavaScript adds. No JS, no arming, no hiding:
   the page renders exactly as it did before this plan. Same degradation
   contract as the ticker's `--live` and the category pills.

   WHY NOT ELEMENTOR'S NATIVE INTERACTIONS (checked 2026-08-18, rejected)
   ---------------------------------------------------------------------
   `modules/interactions/` is real and active, with scrollIn/fade/slide and an
   editor UI. It was rejected because it registers itself `'hidden' => true`
   with `release_status => DEV`; because its runtime contains ZERO matches for
   prefers-reduced-motion; and because it animates through the Web Animations
   API, which ignores CSS media queries entirely — so a reduced-motion opt-out
   would have needed custom JS anyway. Do not migrate to it without re-reading
   08-05-PLAN.md.

   THE THREE DIALS: distance (16px), duration (500ms), and the observer's
   rootMargin in afterglow.js. Tuned with Tommy at the 08-05 checkpoint.
   ═══════════════════════════════════════════════════════════════════════════ */

/* (0,2,0) to match how Elementor compiles a Global Class (`.elementor .cls`).
   Hazard #21: a bare `.ag-reveal` rule loses to that regardless of file order,
   and `body .ag-reveal` (0,1,1) still loses. */
.elementor .ag-reveal.ag-reveal--armed {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity 500ms ease, transform 500ms ease;
}

.elementor .ag-reveal.ag-reveal--armed.is-in {
	opacity: 1;
	transform: none;
}

/* Sections already on screen when the observer arms are revealed without a
   transition, so nothing above the fold ever flickers on load. */
.elementor .ag-reveal.ag-reveal--armed.is-instant {
	transition: none;
}

/* Joins the five prefers-reduced-motion blocks already in this file. Belt and
   braces: afterglow.js also refuses to arm anything when this matches, so a
   visitor who asked for less motion gets a completely still page even if this
   rule were somehow overridden. */
@media (prefers-reduced-motion: reduce) {
	.elementor .ag-reveal,
	.elementor .ag-reveal.ag-reveal--armed,
	.elementor .ag-reveal.ag-reveal--armed.is-in {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* ═══════════════════════════════════════════════════════════════════════════
   PRINT — legal / docs pages (09-04)
   ═══════════════════════════════════════════════════════════════════════════
   The EULA and privacy policy are legal documents people print or save as
   PDF. On paper the Afterglow register is wrong: Ink ground, mist text,
   scanlines, neon links. This block flips the docs window to black-on-white
   and drops the site chrome. It is scoped to the docs register
   (ag-docs-window / ag-manual-body), so the manual prints cleanly too and
   nothing else is touched. Screen rules are untouched. Sits at the END of
   the file on purpose (hazard #22: order is load-bearing).
   Selectors verified against the live /privacy-policy/ markup 2026-09-01. */
@media print {
	#wpadminbar,
	[data-elementor-type="header"],
	[data-elementor-type="footer"],
	.ag-buybar,
	.ag-ticker,
	.ag-scanlines {
		display: none !important;
	}

	html,
	body,
	.elementor .ag-section-end,
	.elementor .ag-section-frame,
	.elementor .ag-docs-window,
	.elementor .ag-manual-body {
		background: #fff !important;
		color: #111 !important;
		box-shadow: none !important;
		border-color: #ccc !important;
	}

	/* Glows, light-leaks and scanlines are pseudo-elements on the containers. */
	.elementor .ag-section-end::before,
	.elementor .ag-section-end::after,
	.elementor .ag-section-frame::before,
	.elementor .ag-section-frame::after,
	.elementor .ag-docs-window::before,
	.elementor .ag-docs-window::after {
		display: none !important;
	}

	.elementor .ag-type-h2,
	.ag-manual-body h3,
	.ag-manual-body h4,
	.ag-manual-body strong,
	.ag-manual-body code {
		color: #000 !important;
		background: none !important;
		-webkit-text-fill-color: #000;
	}
	.ag-manual-body p,
	.ag-manual-body li,
	.ag-manual-body ul,
	.ag-manual-body ol {
		color: #111 !important;
		max-width: none;
	}
	.ag-manual-body li::marker {
		color: #000;
	}
	.ag-manual-body a {
		color: #000 !important;
		text-decoration: underline;
	}
	.ag-manual-body h3 {
		border-top-color: #ccc;
		break-after: avoid;
	}
	.ag-manual-body h4 {
		break-after: avoid;
	}
	.ag-manual-body ul,
	.ag-manual-body ol,
	.ag-manual-body li {
		break-inside: avoid;
	}
}

/* ── Moonbase live price (09-05) ─────────────────────────────────────────
   Moonbase renders localized prices as "99 €" — number, space, symbol.
   In Space Mono (struck original, .ag-price-was) and Bricolage (.ag-price-now)
   that space reads as a hole and the € seems to float. Tighten the gap only;
   glyph size is the typeface's own. Safe below the responsive section: nothing
   else declares word-spacing on these classes. Static "$149"/"$99" (no space)
   is unaffected — word-spacing without a space character is a no-op.

   Scoped to the STRUCK price only (Tommy, 09-05 checkpoint round 2): the big
   .ag-price-now price keeps its natural Bricolage space — a font-relative
   -0.35ch swallowed it whole at 23px while merely trimming Space Mono's wide
   13px space. Both gaps land at roughly 5px now, just via different routes. */
.ag-price-was {
	word-spacing: -0.35ch;
}

/* The currency symbol alone, once afterglow.js has wrapped it (the Moonbase
   render is plain text, so styling the symbol needs a span). Space Mono's €
   at 13px reads as a tiny "e" under the strike; scale the glyph, not the
   price. The big price's symbol is left as designed. */
.ag-price-was .ag-price-cur {
	/* inline-block: the parent's line-through does not propagate into an
	   atomic inline, so the strike ends at the digits (the separator space
	   rides inside this span — see afterglow.js). The nudge re-seats the
	   enlarged glyph, which otherwise rides high beside the 13px digits. */
	display: inline-block;
	font-size: 1.3em;
	transform: translateY( 0.1em );
}
