/* ============================================================================
   ZX-DECK — KIT V2, the scroll-snap deck layer.            2026-09-02
   Lifted verbatim (values unchanged) out of landing/index-v2.html's
   <style id="v2-slides">, which the owner ruled IS the site theme.
   Design record: design/KIT-V2-DESIGN.md · gap review:
   ops/ANGA-GAP-REVIEW-HOMEPAGE-2026-09-02.md · anchor:
   design/ANGA-REFERENCE-SPEC-2026-09-02.md.

   ── LOAD ORDER (not optional) ─────────────────────────────────────────────
     shared.css · disclose.css · dataviz.css · premium.css · motion.css
     glow3.css · type.css · zx-system.css   <-- kit v1, FROZEN
     zx-deck.css                            <-- THIS FILE, always last
   AFTER glow3.css specifically: glow3 ships
   `.js-line-cta:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab)`
   at (0,5,0) with `color:#fff!important`. The twelve `!important`s on
   `.zx a.zx-btn*` below exist only to beat it; delete them, or load this file
   before glow3, and the LINE CTA reverts to white-on-green at 2.26:1.

   ── LAYERS ON KIT V1, NEVER RESTATES IT ───────────────────────────────────
   css/zx-system.css is frozen at md5 7d6b23714bf0fe0553cb787e59d54326 and is
   never edited from here. This file CONSUMES its tokens — --zx-s1..s4,
   --zx-r-pill/-r-1/-r-2/-r-3, --zx-sh-1..3, --zx-dur-interact, --zx-dur-enter,
   --zx-ease, --zx-container, --zx-font-display/-body — and adds only what kit
   v1 does not own: our grounds, the two brand pairs, the paper/die-cut
   geometry, three composed shadow tiers, three motion shorthands, and the deck
   primitives themselves. The one exception is the type scale (70/50/24), which
   is restated here because the deck's display face is carried by an inner
   `<span class="zx-d">` that kit v1 does not know about — see KIT-V2-DESIGN.md
   §"Known duplication".

   ── OPT-IN CONTRACT (same shape as kit v1's) ──────────────────────────────
   Every rule in this file is scoped under `:where(body.zx-deck)` (or
   `body.zx:where(.zx-deck)` where the rule already keyed off kit v1's body
   class). :where() contributes ZERO specificity, so every rule keeps the exact
   weight it had inline — that is load-bearing, not tidiness: `.zx h1,.zx h2,
   .zx h3{margin:0}` is (0,1,1) and is MEANT to lose to type.css's
   `body.type-v2 :is(article,section) h2{margin-top:68px;margin-bottom:18px}`
   at (0,1,3). A plain descendant-class prefix made it win and shortened the
   rendered page by 424px at 1440. Measured, then fixed. A page that LINKS
   this stylesheet but has NOT added `zx-deck` to <body> gets ZERO changed
   pixels — including the paper grain, which is `body.zx-deck::after`.
   Two further opt-ins, both independent:
     html.zx-snap        turns the scroll-snap deck on. Add it from the same
                         inline script that adds `js`. Without it nothing snaps
                         and `--zx-deck-hdr` is unset, so the slide height lock
                         falls back to a plain 100svh. Homepage / industry /
                         article pages snap; money pages must NOT.
     .zx-slide--nosnap   per-slide escape hatch. A slide taller than the
                         snapport creates a proximity band the reader cannot
                         rest inside (the rack slide is 1,600px against 821px),
                         which strands anything that has to reappear at a
                         scroll position — e.g. the rail. Put this modifier on
                         any slide that overruns. It replaces the old
                         id-hardcoded `#svc` exception.

   ── HARD CONTRACTS ────────────────────────────────────────────────────────
   1. THE RAIL IS A SIBLING OF `.zx-sec`, NEVER A CHILD.  `.zx-sec` carries
      `overflow:hidden` (it clips the tear discs into half-moons, which is the
      whole die-cut effect) and `overflow:hidden` kills `position:sticky` for
      every descendant. Nest `.zx-tabs` inside a slide and you get a dead rail
      with no error — the same class of defect ANGA §8(a)#1 already records
      for services.html.
   2. NO BARE `.lit` RULE, EVER.  glow3.css:45 ships `.lit{position:relative}`
      and kit v1 already neutralises it with a twelve-`:not()` selector
      (zx-system.css:417). index-v2 has zero `.lit` elements; index.html and
      medical-tourism.html have 27 each, so a bare rule here breaks them.
   3. `.dx` IS OWNED TWICE.  css/disclose.css owns it site-wide. Every `.dx`
      rule here keeps the `body.zx-deck` prefix so the shared behaviour on the
      other 28 pages is untouched.
   4. GATE-CHECKED CLASSES ARE NEVER RESTYLED: .answer-box .answer-block
      .faq-item .vs-col .cmpgrid .cmpbox .cmp-us .cmp-lbl .proofgrid .mt-stat
      .blk. `.answer-block` is explicitly handed back to the cascade below.
   5. EVERY max-width ELEMENT CARRIES `margin-inline:auto`.

   ── DELIBERATE DIVERGENCES FROM ANGA (do NOT "fix" toward the reference) ───
   a. SCROLL-SNAP. ANGA ships `scroll-snap-type:none` on every element of every
      page. This is a snap deck — `y proximity` on the root, `start` on each
      slide. The owner's format; it is the point of the page, not a gap.
   b. DOTS. ANGA has no dots on any carousel and neither does ours — the
      carousel is arrows-only, exactly as the reference. `.zx-dots` is a DECK
      POSITION indicator for a ten-slide document, a different component with a
      different job. Desktop-only from 1240px.
   c. h3 line-height 1.42 vs ANGA's 32/24 = 1.333 — Thai tone marks stack above
      and below the baseline; every line-height here runs long on purpose.
   d. Hero media takes ~44% of the first viewport vs ANGA's 21.1%; the brief
      asks 40-50%.

   ── MEASURED, so nobody re-opens it ───────────────────────────────────────
   * The paper grain is `position:fixed; z-index:3; pointer-events:none`. <body>
     is NOT a stacking context here (static, z-index auto, no transform, filter,
     isolation or blend), so the grain and `.zx-dots` (z-index 18) share the
     ROOT stacking context and the dots paint ABOVE the grain. Verified
     2026-09-02 by computed style + elementFromPoint on the rail. The gap
     review's R6 ("kit v2 lifts the grain, the dots render under it") does not
     reproduce; no z-index was changed.
   * `.zx-break::before` (the ambient sky bloom) is currently OUT-SPECIFIED on
     #about by the sawtooth `::before`, and was before this extraction too. The
     attribute selector `.zx-sec[data-prev-ground]::before` was chosen at
     (0,3,1) precisely so that ordering is preserved byte-for-byte. If you ever
     want the bloom back, move it to a `::after`.
   ============================================================================ */

/* ============================================================
   index-v2 — ITERATION 4 (2026-09-02)

   THIS BLOCK IS NOW PAGE-SPECIFIC ONLY.
   The physics layer (tokens, carousel, sticky tabs) moved OUT of this file
   and INTO the shipped kit — `css/zx-system.css` + `js/zx-carousel.js` +
   `js/zx-tabs.js` — which this page links above and opts into with
   `class="zx"` on <body>. Everything below is either
     (a) brand values the kit deliberately does not own (our palette), or
     (b) a component that exists only on this page, or
     (c) a documented, reasoned override of exactly one kit/shared value.
   No token ladder is re-declared here. Kit names are used verbatim:
     --zx-r-pill/-r-1/-r-2/-r-3 · --zx-s1..s4 · --zx-sh-1..3
     --zx-ease/--zx-dur-interact/--zx-dur-enter · --zx-container
     --zx-font-display/--zx-font-body

   CONTRAST RULE ADOPTED THIS ITERATION (measured, not asserted):
   the bright brand pair sky #3E8EF7 -> mint #18B87A is DECORATION ONLY.
   White on #18B87A is 2.57:1 and #18B87A on cream is 2.38:1 — both fail AA
   in either direction. Anything that carries text, backs text, or IS text
   (background-clip:text numerals) uses the deepened pair
   #1A5CBE -> #0A6E4A instead: white on it measures 6.33 / 6.28, and it on
   cream measures 5.86 / 5.82. One palette, two jobs, no exceptions.
   (Iteration 6 deepened both stops from #1F6FE0/#0B7A52 — see the token block.)

   RADIUS: exactly four values render on this page — 999 (pill) / 20 / 10 / 4
   — plus ONE documented exception, `.answer-block`'s 18px, which is a
   gate-checked class (SC19) and is therefore left untouched on purpose.

   Never restyles a gate-checked class (.answer-box .answer-block .faq-item
   .vs-col .cmpgrid .cmpbox .cmp-us .cmp-lbl .proofgrid .mt-stat .blk).
   ============================================================ */

/* -- (a) BRAND VALUES the kit does not own -------------------------------
   Declared on `body.zx` (not a bare `.zx`) so they sit at the same
   specificity as zx-system.css's own `body.zx{}` block and win on source
   order — a bare `.zx{}` here would be (0,1,0) and would LOSE every kit
   token it tried to extend. */
body.zx:where(.zx-deck){
  --zx-cream:#FAF6EC;
  --zx-alt:#F2F0E7;
  --zx-ink:#0B1F2A;
  --zx-ink-deep:#061520;
  --zx-on-ink:#F1ECE1;
  --zx-on-ink-dim:#A9BCC8;
  --zx-dim:#48596A;
  --zx-rule:rgba(11,31,42,.14);
  --zx-rule-ink:rgba(241,236,225,.17);

  /* decoration pair — bars, underlines, glows, hairlines. Never behind text. */
  --zx-sky:#3E8EF7;
  --zx-mint:#18B87A;
  --zx-grad:linear-gradient(to right,#3E8EF7,#18B87A);

  /* text-safe pair — buttons, badges, gradient numerals, the CTA band.
     ITERATION 6, eval-5 minor #1: the six lowest-margin pairs on the page
     (4.17 / 4.41 / 4.76 x4) all traced to ONE token, the sky stop, in one of
     two directions — white sitting ON it (buttons, stamp, VS badge, the CTA
     band headline) or IT sitting on cream/alt (the clipped numerals, the
     eyebrow). So no word, size or weight moved; both stops went darker.
                       white-on   on-cream(#FAF6EC)  on-alt(#F2F0E7)
       old #1F6FE0        4.76          4.41              4.17
       new #1A5CBE        6.33          5.86              5.54
       old #0B7A52        5.36          4.96              4.69
       new #0A6E4A        6.28          5.82              5.50
     Worst pair on the page's text-safe palette is now 5.50 against floors of
     3.0-4.5, i.e. real headroom instead of clearance by 0.26. */
  --zx-sky-ink:#1A5CBE;
  --zx-mint-ink:#0A6E4A;
  --zx-grad-ink:linear-gradient(to right,#1A5CBE,#0A6E4A);
  --zx-grad-band:linear-gradient(104deg,#1A5CBE 14%,#0A6E4A 86%);

  /* rail shadow — the one tier the kit does not carry (ANGA .shadow-nav) */
  --zx-sh-rail:0 1px 20px -5px rgba(11,31,42,.23);

  /* page geometry */
  --zx-gut:30px;
  --zx-hdr:60px;      /* shared.css keeps <header> sticky above 860px */

  /* transition SHORTHAND aliases composed from the kit's own duration and
     easing tokens. Not a duplicate: no number is restated here. */
  --zx-t:var(--zx-dur-interact) var(--zx-ease);
  --zx-t-in:var(--zx-dur-enter) var(--zx-ease);
  /* ONE tier kit v1 does not carry. It exists only to close the two image-zoom
     literals below (the ticket cover and the browser mock), which were the last
     two off-token durations on the page. Composed on the kit's own easing. */
  --zx-t-slow:.7s var(--zx-ease);

  /* Messenger blue, contrast-corrected. Pages were hand-rolling
     `background:#0866FF` inline, which measures 4.82:1 against white - under
     the 5.0 floor this palette holds everywhere else. #0757D6 measures
     6.28:1 (the value ku-mud-google-maps already proved). Same hue family,
     same brand read, real headroom. */
  --zx-msgr:#0757D6;
  /* MARQUEE SPEED, in px/s (ANGA 2.6 measures the reference at 30 px/s).
     A duration cannot express a speed on its own - the same seconds value is a
     different speed on every track width - so zx-deck.js reads this token and
     writes a per-track `--zx-mq-dur` from the MEASURED width. The literal
     durations below stay as the no-JS fallback. */
  --zx-marquee-pxs:30;
  /* BLOCK-FLOW GAP between a heading and the prose under it. The owner's rule is
     "no icon or wording blocking each other - give it space"; the measurer's
     floor is 12px. 12 is on the 4px base unit ANGA 2.2 measures. */
  --zx-flow-gap:12px;
  /* text on the gradient band. #fff is 6.33 / 6.28 against the two stops;
     --zx-on-ink (#F1ECE1) also clears the floor at 5.37 / 5.33, so either is
     safe - #fff is the default because it is what the deck already renders. */
  --zx-on-grad:#fff;

  background:var(--zx-cream);
  color:var(--zx-ink);
}
:where(body.zx-deck) .zx,:where(body.zx-deck) .zx *{box-sizing:border-box}
:where(body.zx-deck) .zx img{max-width:100%;height:auto}
:where(body.zx-deck) .zx picture,:where(body.zx-deck) .zx figure{max-width:100%}

/* -- SITE-KIT RESET -------------------------------------------------------
   type.css sets `body.type-v2 :is(article,section) p{19px/600/#0A0A0A}`
   (0,1,3) on every section of the site. The reset below is (0,3,2) and
   later in source order, so it wins on weight, not with !important.
   `.answer-block` is handed straight back to the kit on the next rule —
   that class is gate-checked and must not be restyled.
   ---------------------------------------------------------------------- */
body.type-v2:where(.zx-deck) .zx :is(.zx-sec,.zx-archive) p{
  margin:0;color:inherit;font-size:inherit;font-weight:400;line-height:inherit;
}
body.type-v2:where(.zx-deck) .zx :is(.zx-sec,.zx-archive) li{
  margin:0;color:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;
}
body.type-v2:where(.zx-deck) .zx .answer-block p{
  font-size:revert;font-weight:revert;color:revert;line-height:revert;margin:revert;
}
:where(body.zx-deck) .zx h1,:where(body.zx-deck) .zx h2,:where(body.zx-deck) .zx h3{margin:0}
/* shared.css owns h1/h2 font-family with !important on a bare element
   selector. Rather than escalate an !important war, the display face is
   carried by an inner <span class="zx-d"> — nothing in the kit targets it. */
:where(body.zx-deck) .zx .zx-d{
  display:block;
  font-family:var(--zx-font-display);
  font-weight:700;
  letter-spacing:normal;      /* reference §2.1: normal at every level */
}

/* ========================================================================
   THE DECK — ITERATION 6.  eval-5 major #2.

   spec.md line 9 has asked for "full-viewport sections, scroll-snap-type:
   y proximity" since iteration 1 and it was never actually implemented.
   Iteration 5 shipped `div.zx-scroll{scroll-snap-type:y proximity}` — but
   `.zx-scroll` is a plain, non-scrolling wrapper, and scroll-snap-type is
   INERT on anything that is not a scroll container. Nothing snapped, and no
   section was ever a viewport tall. It is real now.

   WHY THE ROOT AND NOT A HEIGHT-LOCKED DIV. The scroll container of this
   page is the viewport, so the snap container is the ROOT element. Making
   `.zx-scroll` a real `height:100svh;overflow-y:auto` container would have
   worked mechanically and broken four things that matter more: the mobile
   URL-bar collapse (an inner scroller never triggers it), `position:sticky`
   on the header and on the rail, browser-native hash navigation, and the
   `document.documentElement.scrollWidth` overflow gate. This <style> block
   only ever loads on index-v2.html, so `html` here is this page's root and
   no other page's.

   WHY PROXIMITY, EXPLICITLY. `mandatory` traps: a slide taller than the
   viewport (the ticket rack at 390, the FAQ with every <details> open, any
   slide whose long-copy file is open) can become unreachable at its own
   bottom because the engine keeps pulling the scroll back to a snap point.
   `proximity` only engages when the scroll comes to rest NEAR one, and
   `scroll-snap-stop:normal` lets a single fling cross several slides. Both
   are asserted in landing/scratchpad/it6-deck.json, which scrolls to the
   bottom of the tallest slide by wheel events and checks it arrives.

   scroll-padding-top is the sticky header's real measured height (79px at
   1440, 61px at 390 — measured, not the 60px the rail token assumes), so a
   snapped slide starts flush UNDER the header instead of behind it. Slides
   are `100svh MINUS that header`, which is why a snapped slide fills exactly
   the space the reader can actually see.
   ======================================================================== */
html.zx-snap{
  --zx-deck-hdr:79px;
  scroll-snap-type:y proximity;
  scroll-padding-top:var(--zx-deck-hdr);
}
@media(max-width:900px){html.zx-snap{--zx-deck-hdr:61px}}
/* shared.css already sets `scroll-behavior:smooth` on the root; under
   reduced motion the deck must not animate the jump, and it must not snap. */
@media (prefers-reduced-motion:reduce){
  html.zx-snap{scroll-snap-type:none;scroll-behavior:auto}
}
/* the old wrapper is now inert on purpose — kept only as the document anchor
   the progress bar and the dots were written against. */
:where(body.zx-deck) .zx-scroll{display:block}

/* A SLIDE. Vertically centred content is the whole point of the format: the
   message sits in the middle of the screen (owner, 2026-09-02: "balanced,
   centered, message in the middle"), not at the top of a long band. */
:where(body.zx-deck) .zx .zx-sec.zx-slide{
  scroll-snap-align:start;
  scroll-snap-stop:normal;
  display:flex;flex-direction:column;justify-content:center;
}
@media(min-width:901px){
  :where(body.zx-deck) .zx .zx-sec.zx-slide{min-height:calc(100svh - var(--zx-deck-hdr,0px))}
}
/* MOBILE keeps the snap (proximity, same rule — it is declared on the root,
   not per-breakpoint) but NOT the 100svh lock: at 390 six of the ten slides
   are already taller than 844 on their own, and forcing the other four to a
   full screen only manufactures dead space above the fold on a phone. The
   deck reads from the snap and the tear line; the height lock is a desktop
   affordance. */

/* specificity note: premium.css carries `body.premium section{padding:129.6px 0}`
   at (0,1,1). A bare `.zx-sec` is (0,1,0) and loses, which is how iteration 2 ended
   up with one flat 129.6px on every section. Scoping to `.zx .zx-sec` is (0,2,0)
   and wins on weight, so the four-step ladder actually renders. */
/* LADDER OWNERSHIP — eval-4 major #2. Iteration 4 declared the ladder here as
   `.zx .zx-sec.zx-p-strip/-major/-dark` (0,3,0) and it NEVER rendered: the kit's
   own base rule is `body.zx section:not(.cmpgrid)…:not(.blk)` — twelve `:not()`
   class arguments — which scores (0,13,2) and flattened every section to 96.
   Measured proof in eval-4: every section 96/96 except #about (192), which only
   worked because it also carried the kit's real modifier class `.zx-break`.
   Fix: stop re-declaring the ladder and use the kit's own modifiers —
   `section.zx-strip` 48 · base 96 · `section.zx-major` 144 · `section.zx-break`
   192. (The page's old inner `.zx-strip` grid was renamed `.zx-proofrow` so the
   section modifier name is free.) The hero is the one section off the ladder:
   it is a full-bleed band whose frame must reach the fold, so instead of
   fighting a (0,13,2) selector it re-points the token the kit reads —
   `--zx-s2:0px`, scoped to the hero only — and the 48px of ink under the video
   is carried by the frame's own margin, still a ladder value. */
:where(body.zx-deck) .zx .zx-sec{position:relative;overflow:hidden;isolation:isolate}
:where(body.zx-deck) .zx-sec.zx-cream{background:var(--zx-cream);color:var(--zx-ink)}
:where(body.zx-deck) .zx-sec.zx-alt{background:var(--zx-alt);color:var(--zx-ink)}
:where(body.zx-deck) .zx-sec.zx-ink{background:var(--zx-ink);color:var(--zx-on-ink)}
:where(body.zx-deck) .zx-sec.zx-ink-deep{background:var(--zx-ink-deep);color:var(--zx-on-ink)}
:where(body.zx-deck) .zx-sec.zx-grad{background:var(--zx-grad-band);color:#fff}

:where(body.zx-deck) .zx-wrap{
  width:100%;max-width:var(--zx-container);
  margin-inline:auto;
  padding-inline:var(--zx-gut);
  position:relative;z-index:4;
}
/* grid-blowout guard: a grid item's automatic minimum size defaults to its
   content-based (min-content) size, so unbroken Thai text or a fixed-
   intrinsic-size image can force its track — and every ancestor up to
   .zx-wrap — wider than the viewport even though each has width:100%.
   min-width:0 removes that automatic minimum on every affected item.
   body{overflow-x:clip} was masking this from scrollWidth checks. */
:where(body.zx-deck) .zx-wrap,:where(body.zx-deck) .zx-wrap > *,:where(body.zx-deck) .zx-proofrow > *,:where(body.zx-deck) .zx-stat > *,:where(body.zx-deck) .zx-hero-copy > *,:where(body.zx-deck) .zx-steps > *,:where(body.zx-deck) .zx-step > *,:where(body.zx-deck) .zx-vs > *,:where(body.zx-deck) .zx-vs-panel > *,:where(body.zx-deck) .zx-proof2 > *,:where(body.zx-deck) .zx-final > *,:where(body.zx-deck) .zx-carousel-track > *,:where(body.zx-deck) .zx-card > *{min-width:0}

:where(body.zx-deck) .zx-center{text-align:center}
:where(body.zx-deck) .zx-center .zx-ctas{justify-content:center}

/* -- type scale (reference §2.1, our faces) ------------------------------- */
:where(body.zx-deck) .zx-h1 .zx-d{font-size:70px;line-height:1.25}
:where(body.zx-deck) .zx-h2 .zx-d{font-size:50px;line-height:1.275}
/* type.css sets `body.type-v2 h3{font-weight:800;letter-spacing:-.01em}`; scoping
   through .zx wins on weight so headings keep the reference's 700 + normal. */
body.type-v2:where(.zx-deck) .zx .zx-h3,:where(body.zx-deck) .zx .zx-h3{font-family:var(--zx-font-display);font-weight:700;font-size:24px;line-height:1.42;letter-spacing:normal}
:where(body.zx-deck) .zx-eyebrow{
  display:inline-block;margin-bottom:16px;font-family:var(--zx-font-body);font-weight:700;
  font-size:13px;line-height:1.6;letter-spacing:.16em;text-transform:uppercase;
}
/* 13px/700 is NOT WCAG "large text" (that floor is 18.66px bold), so this needs
   the full 4.5:1. The old #0E8F5D measured 3.44:1 on cream / 3.28:1 on alt.
   #0B7A52 measures 4.96 on cream and 4.69 on alt. */
/* SPECIFICITY — eval-4 critical #1. The kit ships
   `body.zx .zx-eyebrow{color:var(--muted,#5D6B84)!important}` at (0,2,1); the
   iteration-4 page rules here were bare `.zx-ink .zx-eyebrow` (0,2,0) and lost
   every time, so both dark-section eyebrows rendered #5c6660 (2.83:1 / 3.10:1).
   The kit now also ships a dark-ground variant at (0,3,1), but it only hooks
   `.zx-break` / `[data-ground="dark"]` — #evidence is `.zx-ink` with neither.
   Every rule below is `body.zx`-prefixed (0,3,1): it ties the kit's dark
   variant on weight and wins on source order (this <style> loads after the kit
   <link>), and carries !important because the kit's declaration does. */
body.zx:where(.zx-deck) .zx-cream .zx-eyebrow,body.zx:where(.zx-deck) .zx-alt .zx-eyebrow{color:var(--zx-mint-ink)!important}
body.zx:where(.zx-deck) .zx-ink .zx-eyebrow,body.zx:where(.zx-deck) .zx-ink-deep .zx-eyebrow{color:#8FE3C4!important}
body.zx:where(.zx-deck) .zx-grad .zx-eyebrow{color:#fff!important}   /* #8FE3C4 on the band is only 3.20:1 */
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-lead{
  font-family:var(--zx-font-body);font-weight:700;
  font-size:28px;line-height:1.6;max-width:34ch;
  margin-top:24px;                         /* h2 -> sub = 24 */
}
/* HEADING -> PROSE GAP IN NORMAL FLOW.

   Measured across the rollout: an h3 immediately followed by a <p> renders with
   0px (`.blk h3`), 8px (`.faq-item h3`) or 12px (`.vs-col h3`) between them,
   because nothing owns that gap - `type.css` gives h3 a margin-TOP (36px) and no
   margin-bottom, and this file's own type.css neutraliser sets `p{margin:0}`
   inside a slide, so the 20px `p` bottom margin that used to separate them is
   gone too. On 29 pages the pair ends up touching.

   WHY index-v2 NEVER SHOWED IT, and why the fix is shaped like this: every
   h3+p pair on the homepage lives inside a FLEX component that owns its spacing
   with `gap` - `.zx-card-body` (10px), `.zx-step` (8px). In a flex container a
   margin does not collapse and does not replace the gap, it ADDS to it, so a
   blanket `margin-top` here would have widened five ticket cards and three
   steps on the homepage and every page that uses them. Measured before writing
   this: 8 h3+p pairs on index-v2, all of them flex children, all margins 0.

   So: the gap is applied in normal flow, and the deck's own gap-owning
   components hand it straight back. The reset ties the main rule at (0,3,3) and
   is declared after it, so it wins on order. Any future deck component that
   spaces its children with `gap` belongs in that list. */
body.type-v2:where(.zx-deck) .zx .zx-sec :is(h3,h4) + :is(p,ul,ol,dl){margin-top:var(--zx-flow-gap)}
body.type-v2:where(.zx-deck) .zx :is(.zx-card-body,.zx-ab-side,.zx-step,.zx-vs-hd) :is(h3,h4) + :is(p,ul,ol,dl){margin-top:0}
/* AND THE GATE-CHECKED BOXES HAND IT BACK TOO - contract #4, and SC16.
   Measured the hard way: without this line the rule raised `.blk h3` 0 -> 12px
   and `.faq-item h3` 8 -> 12px on medical-tourism.html and grew that page by
   84px (17,781 -> 17,865), which is exactly the anchor page SC16 exists to
   protect. It also restyled gate-checked classes, which contract #4 forbids
   outright. Both are the same mistake wearing two hats.
   CONSEQUENCE, stated plainly: on the rollout pages the tight h3 -> p pairs are
   almost entirely INSIDE these boxes, so the kit cannot close them. Doing that
   needs an owner-approved change to the gate-checked component styles in
   shared.css plus an SC16 re-baseline - it is not a kit fix. What the rule below
   does close is every h3 -> p pair in ordinary slide prose. */
body.type-v2:where(.zx-deck) .zx :is(.answer-box,.answer-block,.faq-item,.vs-col,.cmpgrid,.cmpbox,.cmp-us,.cmp-lbl,.proofgrid,.mt-stat,.blk) :is(h3,h4) + :is(p,ul,ol,dl){margin-top:0}

/* `.zx-body` IS THE SLIDE-PROSE CLASS. 16px/1.72, 62ch, 16px above. Every
   rollout page should use it for body copy inside a slide; pages that use no
   body class at all have no consistent target and drift.
   READ THIS BEFORE "FIXING" IT: index-v2's single `.zx-body` element also
   carries `.zx-quote`, and `p.zx-quote` (declared later at equal specificity,
   21px desktop / 18px at <=640) is what governs it. So a measurer that samples
   index-v2 sees 21/18 and every adopting page sees 16 - and concludes the kit
   is wrong. It is not: the adopting pages are rendering exactly this rule.
   Verified 2026-09-03: index-v2 has ONE .zx-body element, class
   "zx-body zx-quote". Do not raise this to 21px; that would restyle body copy
   on all five adopting pages and still not touch index-v2. */
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-body{
  font-family:var(--zx-font-body);font-weight:400;
  font-size:16px;line-height:1.72;max-width:62ch;margin-top:16px;
}
:where(body.zx-deck) .zx-ink .zx-body,:where(body.zx-deck) .zx-ink-deep .zx-body{color:var(--zx-on-ink-dim)}
:where(body.zx-deck) .zx-cream .zx-body,:where(body.zx-deck) .zx-alt .zx-body{color:var(--zx-dim)}
:where(body.zx-deck) .zx-small{font-family:var(--zx-font-body);font-weight:400;font-size:14px;line-height:1.6}
:where(body.zx-deck) .zx-ink .zx-small,:where(body.zx-deck) .zx-ink-deep .zx-small{color:var(--zx-on-ink-dim)}
:where(body.zx-deck) .zx-cream .zx-small,:where(body.zx-deck) .zx-alt .zx-small{color:var(--zx-dim)}
:where(body.zx-deck) .zx-center .zx-lead,:where(body.zx-deck) .zx-center .zx-body{margin-inline:auto}

/* the one accent mark: the bright pair drawn UNDER a key word — decoration,
   nothing sits on it, so it keeps the full-saturation sky->mint */
:where(body.zx-deck) .zx-key{position:relative;display:inline-block;white-space:nowrap;z-index:0}
:where(body.zx-deck) .zx-key::after{
  /* Thai lower vowels (สระอู/อุ) and the tone row sit BELOW the baseline; a bar at
     bottom:0 cuts straight through them. Measured clear at -.16em on 70px Bai
     Jamjuree, so the mark reads as an underline instead of a strikethrough. */
  content:"";position:absolute;left:-.04em;right:-.04em;bottom:-.16em;
  height:.1em;border-radius:var(--zx-r-3);background:var(--zx-grad);z-index:-1;
  transform:scaleX(0);transform-origin:0 50%;
  animation:zx-bar .9s cubic-bezier(.4,0,.2,1) .5s forwards;
}
@keyframes zx-bar{to{transform:scaleX(1)}}
/* gradient rule that opens a major section */
/* section rule -> h2 = 16, matching the hero's kicker -> h1 (owner directive:
   "the position of everything has to be perfect"; one rhythm, every band). */
:where(body.zx-deck) .zx-bar{display:block;width:88px;height:6px;border-radius:var(--zx-r-3);background:var(--zx-grad);margin-bottom:16px}
:where(body.zx-deck) .zx-center .zx-bar{margin-inline:auto}

/* -- buttons (reference §3.6: pill, gradient primary, .3s) ---------------- */
:where(body.zx-deck) .zx-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  min-height:50px;padding:0 22px;
  font-family:var(--zx-font-body);font-weight:700;font-size:17px;line-height:1.2;
  border-radius:var(--zx-r-pill);border:1.5px solid transparent;
  text-decoration:none;cursor:pointer;
  transition:transform var(--zx-t),box-shadow var(--zx-t),background-color var(--zx-t),color var(--zx-t),filter var(--zx-t);
}
/* glow3's `.js-line-cta:not()x4` is (0,5,0) with color:#fff!important */
:where(body.zx-deck) .zx a.zx-btn{
  border-radius:var(--zx-r-pill)!important;
  font-family:var(--zx-font-body)!important;
  font-size:17px!important;font-weight:700!important;
  min-height:50px!important;padding:0 22px!important;
  letter-spacing:normal!important;
}
:where(body.zx-deck) .zx a.zx-btn-fill:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab){
  background-image:var(--zx-grad-ink)!important;background-color:#1A5CBE!important;
  color:#fff!important;border-color:transparent!important;
  box-shadow:var(--zx-sh-1)!important;
}
:where(body.zx-deck) .zx a.zx-btn-fill:hover{filter:brightness(1.1)!important;transform:translateY(-1px)}
:where(body.zx-deck) .zx a.zx-btn-ghost{background:transparent!important;box-shadow:none!important;border:1.5px solid currentColor!important}
:where(body.zx-deck) .zx .zx-ink a.zx-btn-ghost,:where(body.zx-deck) .zx .zx-ink-deep a.zx-btn-ghost{color:var(--zx-on-ink)!important}
:where(body.zx-deck) .zx .zx-cream a.zx-btn-ghost,:where(body.zx-deck) .zx .zx-alt a.zx-btn-ghost{color:var(--zx-ink)!important}
:where(body.zx-deck) .zx .zx-grad a.zx-btn-ghost{color:#fff!important}
:where(body.zx-deck) .zx a.zx-btn-ghost:hover{background:rgba(241,236,225,.14)!important;border-color:currentColor!important}
:where(body.zx-deck) .zx .zx-cream a.zx-btn-ghost:hover,:where(body.zx-deck) .zx .zx-alt a.zx-btn-ghost:hover{background:rgba(11,31,42,.06)!important}
:where(body.zx-deck) .zx .zx-grad a.zx-btn-fill{background-image:none!important;background-color:#fff!important;color:#0B1F2A!important}
:where(body.zx-deck) .zx-btn:focus-visible{outline:3px solid var(--zx-sky);outline-offset:3px}
:where(body.zx-deck) .zx-ctas{display:flex;flex-wrap:wrap;gap:12px;margin-top:32px}  /* sub -> CTA = 32 */

/* -- scroll progress bar (acceptance standard §1) ------------------------ */
:where(body.zx-deck) .zx-prog{position:fixed;left:0;top:0;width:100%;height:3px;z-index:101;pointer-events:none;background:transparent}
:where(body.zx-deck) .zx-prog > span{
  display:block;height:100%;width:100%;transform:scaleX(0);transform-origin:0 50%;
  background:var(--zx-grad);will-change:transform;
}

/* -- sibling stagger: ~.14s, per the acceptance standard ------------------ */
.zx-deck-js :where(body.zx-deck) .zx-stagger > .zx-rv:nth-child(2){transition-delay:.14s}
.zx-deck-js :where(body.zx-deck) .zx-stagger > .zx-rv:nth-child(3){transition-delay:.28s}
.zx-deck-js :where(body.zx-deck) .zx-stagger > .zx-rv:nth-child(4){transition-delay:.42s}
.zx-deck-js :where(body.zx-deck) .zx-stagger > .zx-rv:nth-child(5){transition-delay:.56s}
.zx-deck-js :where(body.zx-deck) .zx-stagger > .zx-rv:nth-child(n+6){transition-delay:.7s}

/* ========================================================================
   1 — HERO. ITERATION-4, second pass — CENTRED, per the owner directive
   ("the logo, the lines, the message have to be at the middle — check ANGA")
   and per ANGA §4/§6.1 as measured: h1 + kicker + paragraph + CTAs in ONE
   centred column, then the hero video in a centred frame that the fold cuts.
   The reference's own ratio is 21.1%; the brief asks this page for 45–55%,
   so the frame runs the full container instead of ANGA's inset 1260 — same
   composition, larger payoff, ~47% of the first viewport.
   MOOD: the copy no longer sits on moving footage, so nothing has to fight
   the image, the duotone drops from .72 to .28 and the picture can finally be
   seen, and exactly ONE accent runs in this section (the gradient rule under
   the key word). No glow, no scroll ornament, no competing second gradient.
   RHYTHM: kicker -> h1 -> sub -> CTA = 16 / 24 / 32.
   ======================================================================== */
/* ITERATION 6: `display:block` and the two scroll-snap declarations moved to
   the shared `.zx-slide` rule above — the hero is slide 01 of ten and takes
   the deck's geometry like every other slide, it is not a special case any
   more. What stays local is the ink ground and the zeroed section token. */
:where(body.zx-deck) .zx .zx-sec.zx-hero{
  --zx-s2:0px;                /* the kit's base section rule reads this token */
  background:var(--zx-ink);
}
/* FOLD BUDGET (measured 2026-09-02, 1440x900): the hero frame always runs past
   the fold, so its share of the first viewport is decided purely by its top
   edge. Header 79 + this padding + the copy stack + the frame margin = that
   edge. 18 + 374 + 24 lands it at y=486, i.e. 414px x 1440 = 46.0% of the
   fold — inside the 45–55% the brief asks for, with the copy still complete. */
:where(body.zx-deck) .zx-hero-in{padding-top:18px;padding-bottom:0}
/* centred, and wide enough that the 70px display face sets the second line
   without leaving a third orphan line. ANGA's own hero column is 525px. */
:where(body.zx-deck) .zx-hero-copy{max-width:660px;margin-inline:auto}
:where(body.zx-deck) .zx-hero .zx-h1 .zx-d{color:#fff}
body.type-v2:where(.zx-deck) .zx .zx-hero p.zx-lead{
  color:#DDE7EE;max-width:44ch;font-size:20px;
  margin-top:24px;margin-inline:auto;      /* h1 -> sub = 24 */
}
:where(body.zx-deck) .zx-hero .zx-ctas{margin-top:32px}         /* sub -> CTA = 32 */
/* the kicker slot: one verifiable number instead of a slogan — the same
   5.0 / 28 reviews the proof strip carries further down, promoted to the fold
   because it is the only figure here a stranger can check in one tap. */
body.type-v2:where(.zx-deck) .zx .zx-hero p.zx-hero-chip{
  display:inline-flex;align-items:center;gap:12px;
  margin:0 auto 16px;                      /* kicker -> h1 = 16 */
  padding:6px 16px 6px 12px;border-radius:var(--zx-r-pill);
  background:rgba(241,236,225,.07);border:1px solid rgba(241,236,225,.20);
  text-align:left;
}
:where(body.zx-deck) .zx-chip-n{
  font-family:var(--zx-font-display);font-weight:700;font-size:22px;line-height:1;
  letter-spacing:normal;color:#8FE3C4;
}
:where(body.zx-deck) .zx-chip-t{font-family:var(--zx-font-body);font-weight:400;font-size:13.5px;line-height:1.4;color:var(--zx-on-ink-dim)}
/* the payoff. Centred, container-wide, deliberately taller than the room left
   above the fold so the fold cuts it — ANGA §4's whole point. */
/* (0,2,0), because `.zx picture,.zx figure{max-width:100%}` above is (0,1,1)
   and silently won the earlier (0,1,0) version of this rule.
   WIDTH DECISION, measured: at the container's 1410 the frame holds 44.4% of
   the first viewport and the brief asks for 45–55, so it runs the full 1440
   instead — a full-bleed band, symmetric by construction (left gutter 0 ==
   right gutter 0) and identical in behaviour to the mobile frame. */
:where(body.zx-deck) .zx .zx-hero-frame{
  position:relative;z-index:1;
  width:100%;max-width:none;
  margin:24px auto var(--zx-s1);   /* 48 of ink under the frame, on the ladder */
  /* 21:9, not the source's own 16:9. The footage is real documentary material
     of the two founders at their desks, and its mid-frames push a subject close
     to camera; a cinematic letterbox crop keeps the wide room composition, drops
     the busy floor/chair band at the bottom, and reads calmer. The frame is
     617px tall at 1440, so it still runs 200px past the fold. */
  aspect-ratio:21/9;
  border-radius:var(--zx-r-1);overflow:hidden;
  /* POSTER IS PER-PAGE. Every rollout page has its own hero still, so the image
     is a token the page sets on the frame element itself:
       <figure class="zx-hero-frame" style="--zx-hero-poster:url(/img/x.webp)">
     The fallback is index-v2's own poster, so a page that sets nothing renders
     exactly what it rendered before. Written as longhands, not the `background`
     shorthand: a shorthand cannot carry a var() fallback containing a url()
     without also resetting colour, position, size and repeat every time the
     token resolves. `background-color` stays the ink floor the video fades up
     from, and it is what shows while the poster is still loading. */
  background-color:#07161F;
  background-image:var(--zx-hero-poster,url("/img/hero-office-poster.jpg"));
  background-position:center;
  background-size:cover;
  background-repeat:no-repeat;
  box-shadow:var(--zx-sh-3);
}
/* DECK FOLD BUDGET, iteration 6. Slide 01 is now exactly one screen tall
   (100svh minus the sticky header), so the frame can no longer be "cut by the
   fold" — it has to FIT the fold, and the 48px of ink that used to sit under
   it is now the centring slack the flex box distributes. Measured at 1440x900:
   header 79 + hero-in padding 18 + copy stack 374 + frame margin 24 + frame
   396 = 891, i.e. the frame closes 9px above the fold and holds
   396x1440 / (900x1440) = 44.0% of the first viewport — inside the 40-50%
   the brief sets, with the whole slide visible at once for the first time. */
@media(min-width:901px){
  :where(body.zx-deck) .zx .zx-hero-frame{
    aspect-ratio:auto;height:44svh;margin-bottom:0;
  }
}
:where(body.zx-deck) .zx-video{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:50% 34%;
  opacity:0;transition:opacity var(--zx-t-in);
}
:where(body.zx-deck) .zx-video.zx-v-on{opacity:1}

/* -- PHOTO HERO VARIANT --------------------------------------------------
   The frame is `position:relative` with a fixed aspect-ratio / svh height, and
   until now only `.zx-video` was told to fill it. A page whose hero is a still
   dropped a normal in-flow <img> into that box, so the picture sat at its own
   intrinsic height inside a frame sized for something else and the hero missed
   the 40-50%-of-fold budget unless the author hand-wrote inline styles. Same
   fill contract as the video, so photo and video heroes are interchangeable.

   Deliberately matched on the ELEMENT, not on `.zx-hero-photo`: forgetting the
   class would otherwise fail silently and look like a broken hero. `<picture>`
   is covered too, because an art-directed hero wraps the <img> and the wrapper
   is what becomes the frame's child.

   NOT a redefinition of kit v1's `.zx-hero-photo` (zx-system.css:689), which is
   a colour GRADE (`filter:saturate(.9) contrast(1.04) brightness(.96)`) and
   touches no layout property. The two compose: keep adding `zx-hero-photo` to
   the <img> for the grade, and `.zx-hero-scrim` after it for text legibility.

   `--zx-hero-focus` re-points the crop without inline styles (the video's own
   `50% 34%` is a per-source choice, not a default worth inheriting). */
/* SPECIFICITY, measured: glow3.css ships
   `.premium img:not(.nav-ico):not([class*="ico"]):not(svg){height:auto}` at
   (0,3,2), which silently beat a lighter selector here and left the photo at
   its intrinsic 810px inside a 396px frame. Scoping through body.zx + .zx ties
   it at (0,3,2) and wins on source order (zx-deck.css is the last stylesheet),
   so no `!important` is spent. The `picture > img` arm is (0,3,3) outright. */
body.zx:where(.zx-deck) .zx .zx-hero-frame > img,
body.zx:where(.zx-deck) .zx .zx-hero-frame > picture,
body.zx:where(.zx-deck) .zx .zx-hero-frame > picture > img{
  position:absolute;inset:0;display:block;
  width:100%;height:100%;max-width:none;
  object-fit:cover;object-position:var(--zx-hero-focus,50% 50%);
}
/* duotone at a QUARTER of iteration 3's strength. One sky pool, one mint pool,
   and a soft ink floor that ties the frame back into the ink field. */
:where(body.zx-deck) .zx-hero-duo{position:absolute;inset:0;z-index:2;display:block;pointer-events:none}
:where(body.zx-deck) .zx-hero-duo::before{
  content:"";position:absolute;inset:0;
  background:var(--zx-ink);mix-blend-mode:multiply;opacity:.28;
}
:where(body.zx-deck) .zx-hero-duo::after{
  content:"";position:absolute;inset:0;
  background:
    radial-gradient(52% 58% at 78% 24%,rgba(62,142,247,.30),transparent 72%),
    radial-gradient(46% 52% at 18% 88%,rgba(24,184,122,.22),transparent 74%),
    linear-gradient(180deg,rgba(11,31,42,.34) 0%,transparent 34%,rgba(11,31,42,.30) 100%);
}

/* ========================================================================
   2 — PROOF STRIP. 48/48, logo marquee + one stat + the dated MDCC line.
   ======================================================================== */
/* owner directive 2026-09-03: "position 5.0 in the middle" -- the old two-column
   row (stat left, MDCC line right) read lopsided. Stat + caption + line now stack
   as ONE centred block, capped at the proofline's own 70ch measure. */
:where(body.zx-deck) .zx-proofrow{display:grid;grid-template-columns:minmax(0,1fr);justify-items:center;text-align:center;gap:20px;max-width:70ch;margin:0 auto}
:where(body.zx-deck) .zx-stat{display:flex;align-items:baseline;justify-content:center;gap:12px;text-align:left}
:where(body.zx-deck) .zx-num{
  font-family:var(--zx-font-display);font-weight:700;font-size:70px;line-height:1;
  letter-spacing:normal;
  background:var(--zx-grad-ink);-webkit-background-clip:text;background-clip:text;color:transparent;
}
:where(body.zx-deck) .zx-stat-lb{font-family:var(--zx-font-body);font-weight:700;font-size:14px;line-height:1.5;color:var(--zx-dim);max-width:14ch}
:where(body.zx-deck) .zx-proofline{font-family:var(--zx-font-body);font-weight:400;font-size:16px;line-height:1.72;color:var(--zx-dim);max-width:70ch}
:where(body.zx-deck) .zx-proofline b{font-weight:700;color:var(--zx-ink)}
/* owner directive 2026-09-03: "photos and visuals must be in the middle". shared.css
   pins .see-row to 495px+581px columns; inside any container narrower than 1124px
   (details.dx archive = 824px, article = 900px) the fixed grid overflowed to the
   right and the second column was clipped by the details' overflow:hidden, so the
   photo sat left with an empty right half. Same 46:54 split, now fluid, so every
   row fits and centres in whatever container holds it. Scoped to >1180px so
   shared.css's own mobile block (1fr) keeps winning below it (SC18). */
@media(min-width:1181px){
  :where(body.zx-deck) .see-row{grid-template-columns:minmax(0,495fr) minmax(0,581fr)}
  :where(body.zx-deck) .see-row:nth-of-type(even),:where(body.zx-deck) .see-row--flip{grid-template-columns:minmax(0,581fr) minmax(0,495fr)}
}
/* owner directive: the logo wall reads centred, like ANGA's own marquee band. */
:where(body.zx-deck) .zx-wall-h{
  display:block;text-align:center;font-family:var(--zx-font-body);font-weight:700;font-size:13px;line-height:1.6;
  letter-spacing:.16em;text-transform:uppercase;color:var(--zx-dim);margin-top:36px;
}
:where(body.zx-deck) .zx-marquee{margin-top:14px;position:relative;overflow:hidden}
:where(body.zx-deck) .zx-marquee + .zx-marquee{margin-top:10px}
:where(body.zx-deck) .zx-marquee-track{display:flex;gap:40px;width:max-content;align-items:center}
/* The literal 44s / 56s are the pre-kit values and remain the fallback if the
   runtime never runs. They are NOT the shipped speed: zx-deck.js overrides
   `--zx-mq-dur` per track from --zx-marquee-pxs and the measured track width.
   Measured before: 3800px track at 1440, translate3d(-50%) => 1900px of travel
   in 44s = 43.2 px/s, against ANGA's 30. */
:where(body.zx-deck) .zx-mq-a{animation:zx-slidex 44s linear infinite;animation-duration:var(--zx-mq-dur,44s)}
:where(body.zx-deck) .zx-mq-b{animation:zx-slidey 56s linear infinite;animation-duration:var(--zx-mq-dur,56s)}
:where(body.zx-deck) .zx-marquee:hover .zx-marquee-track,:where(body.zx-deck) .zx-marquee:focus-within .zx-marquee-track{animation-play-state:paused}
:where(body.zx-deck) .zx-marquee img{
  height:40px;width:40px;object-fit:contain;flex:0 0 auto;
  filter:grayscale(1);opacity:.55;
  transition:filter var(--zx-t),opacity var(--zx-t),transform var(--zx-t);
}
:where(body.zx-deck) .zx-marquee img:hover{filter:none;opacity:1;transform:scale(1.2)}
@keyframes zx-slidex{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
@keyframes zx-slidey{from{transform:translate3d(-50%,0,0)}to{transform:translate3d(0,0,0)}}
:where(body.zx-deck) .zx-mask{
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
}

/* ========================================================================
   3 — STICKY PILL RAIL — now the KIT component (.zx-tabs / .zx-tabs-rail /
   .zx-tab, styled by css/zx-system.css, driven by js/zx-tabs.js). Only the
   three page-specific facts live here.
   ======================================================================== */
/* (1) OFFSET. The kit ships ANGA's literal `top:-20px`, which assumes no
   sticky chrome above it. shared.css keeps our <header> sticky at 60px above
   860px, so the same -20px has to be measured from the header instead of the
   viewport. Below 860px the header is static and the offset is literally -20.
   `body.zx .zx-tabs` (0,2,1) ties the kit's own selector and wins on source
   order — this page's <style> loads after the kit's <link>. */
body.zx:where(.zx-deck){--zx-rail-top:calc(var(--zx-hdr) - 20px)}
body.zx:where(.zx-deck) .zx-tabs{top:var(--zx-rail-top);padding:40px 0 20px;pointer-events:none}
body.zx:where(.zx-deck) .zx-tabs .zx-tabs-outer{display:flex;align-items:center;gap:8px;pointer-events:auto;width:100%}
body.zx:where(.zx-deck) .zx-tabs-rail{box-shadow:var(--zx-sh-rail);margin-inline:auto;width:max-content}
/* (2) the kit renders <button> tabs; ours are anchors, and ANGA's active pill
   is #000 while our ink is #0B1F2A. */
body.zx:where(.zx-deck) a.zx-tab{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:5px 16px 8px;text-decoration:none;
  color:var(--zx-ink);
}
body.zx:where(.zx-deck) a.zx-tab:hover{background:var(--zx-alt)}
body.zx:where(.zx-deck) a.zx-tab.active,body.zx:where(.zx-deck) a.zx-tab.active:hover{background:var(--zx-ink);color:#fff}
body.zx:where(.zx-deck) a.zx-tab:focus-visible{outline:3px solid var(--zx-sky);outline-offset:-3px}
/* (3) the two mobile paging arrows the kit does not carry */
:where(body.zx-deck) .zx-tabs-arrow{
  display:none;flex:0 0 auto;align-items:center;justify-content:center;
  width:44px;height:44px;border:0;border-radius:var(--zx-r-pill);
  background:#fff;color:var(--zx-ink);box-shadow:var(--zx-sh-rail);cursor:pointer;
  transition:opacity var(--zx-t);
}
:where(body.zx-deck) .zx-tabs-arrow:focus-visible{outline:3px solid var(--zx-sky);outline-offset:2px}

/* ========================================================================
   4 — SERVICES CAROUSEL — now the KIT component (.zx-carousel /
   .zx-carousel-viewport / .zx-carousel-track / .zx-card / .zx-arrow, driven
   by js/zx-carousel.js: transform track, arrows + pointer drag, NO autoplay,
   NO dots, NO scroll-snap). Only the service-card SKIN lives here.
   ======================================================================== */
:where(body.zx-deck) .zx-car-head{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap;margin-bottom:44px}
:where(body.zx-deck) .zx-car-nav{display:flex;gap:10px}
body.zx:where(.zx-deck) .zx-car-nav .zx-arrow{
  width:52px;height:52px;
  border:1.5px solid var(--zx-rule);color:var(--zx-ink);
  transition:background-color var(--zx-t),color var(--zx-t),border-color var(--zx-t),opacity var(--zx-t);
}
body.zx:where(.zx-deck) .zx-car-nav .zx-arrow:hover:not(:disabled){background:var(--zx-ink);color:#fff;border-color:var(--zx-ink);box-shadow:var(--zx-sh-1)}
body.zx:where(.zx-deck) .zx-car-nav .zx-arrow:focus-visible{outline:3px solid var(--zx-sky);outline-offset:2px}
body.zx:where(.zx-deck) .zx-carousel{--zx-gap:34px}
/* ------------------------------------------------------------------------
   THE PRICE TICKET — iteration-5 signature moment.

   WHY THIS AND NOT A CARD. The one thing this business does that the
   reference agency (and every Thai agency it competes with) refuses to do is
   PRINT THE PRICE. The page already says it twice in its own words —
   "ไม่ปิดราคาไว้ให้ทักมาถาม" in the comparison block and "ราคาทุกขั้นอยู่บน
   หน้าเว็บแล้ว" in step 3 — but iteration 4 rendered that promise as a
   56px gradient number at the bottom of a generic media card, i.e. as a card
   grid of equal boxes: exactly the thing the rubric penalises. So the slide
   becomes the object the claim describes: a die-cut price ticket. Window,
   copy, a real perforation with a notch bitten out of each edge, and the
   figure printed on the stub at 70px in the display face.

   PHYSICS, not decoration:
   - the notches are section-coloured discs sitting half outside the ticket,
     so the ticket keeps its shadow (a CSS mask would have eaten it);
   - the ticket is the ONLY element on the page with a paper ground lighter
     than its section, so it reads as a slip lying on the band;
   - pointing at a price TEARS it: the top rides up 3px and the stub drops
     3px, opening the perforation by 6px. Motion tied to the one action that
     matters here, not ambient decoration. Reduced-motion kills all of it.
   - the figure is SOLID ink, not the gradient iteration 4 used. It measures
     ~16:1 instead of ~4.2:1, it is the calmest possible treatment for the
     biggest type on the page, and it keeps the section to one accent (the
     mint rule under the heading) as the owner asked.
   Radius census is unchanged: the discs use the existing pill tier, the
   ticket keeps the 20px container tier. No new radius value is introduced.
   ------------------------------------------------------------------------ */
body.zx:where(.zx-deck) .zx-ticket{
  flex:0 0 40%;
  padding:0;gap:0;height:auto;
  overflow:visible;                 /* the die-cut notches sit ON the edge */
  background:var(--zx-cream);
  border:1px solid var(--zx-rule);
  box-shadow:var(--zx-sh-1);
  display:flex;flex-direction:column;
  transition:box-shadow var(--zx-t),transform var(--zx-t);
}
/* THE DEAL-IN. The generic reveal is `.js .zx-rv{transform:translateY(22px)}`;
   a ticket comes off a rack, so it also carries a 1.1deg tilt that settles as
   it lands. Pivot at the bottom edge so it reads as a slip being straightened,
   not a box being spun. (0,3,0) beats the generic (0,2,0); the settled state
   `.js .zx-rv.zx-in` is also (0,3,0) but is declared later in this same
   stylesheet, so it wins the tie and the ticket lands square. */
.zx-deck-js :where(body.zx-deck) .zx-ticket.zx-rv{transform:translateY(26px) rotate(-1.1deg);transform-origin:50% 100%}

/* HOVER/FOCUS LIFT — (0,5,1). It has to outrank `.js .zx-rv.zx-in{transform:
   none}` (0,3,0), which is why iteration 4's `body.zx .zx-card-svc:hover`
   (0,2,1) silently never moved a card after the reveal had fired. */
body.zx:where(.zx-deck) .zx-ticket:hover,body.zx:where(.zx-deck) .zx-ticket:focus-within,body.zx:where(.zx-deck) .zx-ticket.zx-rv.zx-in:hover,body.zx:where(.zx-deck) .zx-ticket.zx-rv.zx-in:focus-within{
  box-shadow:var(--zx-sh-2),var(--zx-sh-1);transform:translateY(-4px);
}
:where(body.zx-deck) .zx-tk-top{display:flex;flex-direction:column;flex:1;transition:transform var(--zx-t)}
:where(body.zx-deck) .zx-tk-stub{padding:18px 24px 24px;transition:transform var(--zx-t)}
:where(body.zx-deck) .zx-ticket:hover .zx-tk-top,:where(body.zx-deck) .zx-ticket:focus-within .zx-tk-top{transform:translateY(-3px)}
:where(body.zx-deck) .zx-ticket:hover .zx-tk-stub,:where(body.zx-deck) .zx-ticket:focus-within .zx-tk-stub{transform:translateY(3px)}

/* the perforation. Dashed rule inset 18px, with a 22px disc centred on each
   of the ticket's outer edges (perf starts 19px in from the border box, so
   the disc's own left edge is -30px). */
:where(body.zx-deck) .zx-perf{position:relative;height:0;border-top:2px dashed rgba(11,31,42,.30);margin:0 18px}
/* NO border on the discs, deliberately. The cue that reads as a die-cut is the
   INTERRUPTION of the ticket's own 1px outline, not the disc's edge — a bordered
   disc reads as a bubble stuck on the card instead of a hole punched through it.
   22px centred on the edge covers the outline by 11px either side. */
:where(body.zx-deck) .zx-perf::before,:where(body.zx-deck) .zx-perf::after{
  content:"";position:absolute;top:-12px;width:22px;height:22px;
  border-radius:var(--zx-r-pill);
  background:var(--zx-alt);
}
:where(body.zx-deck) .zx-perf::before{left:-30px}
:where(body.zx-deck) .zx-perf::after{right:-30px}

:where(body.zx-deck) .zx-card-cover{position:relative;margin:14px 14px 0;border-radius:var(--zx-r-2);overflow:hidden;aspect-ratio:16/9;background:var(--zx-alt)}
:where(body.zx-deck) .zx-card-cover img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--zx-t-slow)}
:where(body.zx-deck) .zx-ticket:hover .zx-card-cover img,:where(body.zx-deck) .zx-ticket:focus-within .zx-card-cover img{transform:scale(1.05)}
:where(body.zx-deck) .zx-card-cover::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,transparent 44%,rgba(11,31,42,.34));pointer-events:none}
:where(body.zx-deck) .zx-card-body{padding:20px 24px 22px;display:flex;flex-direction:column;gap:10px;flex:1}
/* the ticket class line, printed not chipped: a hairline rule under a
   letterspaced label, the way a fare class sits on a real ticket. */
:where(body.zx-deck) .zx-card-tag{
  align-self:flex-start;
  padding:0 0 6px;border-bottom:2px solid var(--zx-mint-ink);
  color:var(--zx-mint-ink);
  font-family:var(--zx-font-body);font-weight:700;font-size:12px;line-height:1.5;letter-spacing:.18em;text-transform:uppercase;
}
:where(body.zx-deck) .zx-card h3{color:var(--zx-ink)}
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-card-l{font-size:16px;line-height:1.7;color:var(--zx-dim);flex:1}
:where(body.zx-deck) .zx-card-price{display:flex;align-items:baseline;gap:10px;margin:0}
:where(body.zx-deck) .zx-price{
  font-family:var(--zx-font-display);font-weight:700;font-size:70px;line-height:1;letter-spacing:normal;
  color:var(--zx-ink);
}
:where(body.zx-deck) .zx-price-u{font-family:var(--zx-font-body);font-weight:700;font-size:14px;color:var(--zx-dim)}
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-card-sub{font-size:14px;line-height:1.6;color:var(--zx-dim);margin-top:6px}

/* ========================================================================
   5 — ANSWER BAND. Compact, first 40% of the page. .answer-block is
   gate-checked: the band around it is styled, the block itself is not —
   which is why its 18px radius is the one documented exception to the
   four-tier radius census on this page.
   ======================================================================== */
:where(body.zx-deck) .zx-ab-grid{
  display:grid;grid-template-columns:minmax(0,.36fr) minmax(0,1fr);
  gap:56px;align-items:start;max-width:1240px;margin-inline:auto;width:100%;
}
:where(body.zx-deck) .zx-ab-side{display:flex;flex-direction:column;gap:14px;padding-top:8px}
:where(body.zx-deck) .zx-ab-side .zx-stamp{align-self:flex-start}
:where(body.zx-deck) .zx-ab{
  position:relative;width:100%;
  background:#fff;border-radius:var(--zx-r-1);overflow:hidden;
  box-shadow:var(--zx-sh-2);padding:40px clamp(22px,3vw,48px) 44px;
}
:where(body.zx-deck) .zx-ab::before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;background:var(--zx-grad)}
:where(body.zx-deck) .zx-stamp{
  font-family:var(--zx-font-body);font-weight:700;font-size:12px;line-height:1.6;
  letter-spacing:.14em;text-transform:uppercase;color:#fff;
  border-radius:var(--zx-r-pill);padding:6px 14px;background-image:var(--zx-grad-ink);
}
:where(body.zx-deck) .zx-by{font-family:var(--zx-font-body);font-weight:400;font-size:14px;line-height:1.7;color:var(--zx-dim)}

/* ========================================================================
   6 — HOW IT WORKS. Three giant numerals.
   ======================================================================== */
:where(body.zx-deck) .zx-steps{
  list-style:none;padding:0;margin:56px auto 0;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:36px;
}
:where(body.zx-deck) .zx-step{display:flex;flex-direction:column;gap:8px;padding-top:26px;border-top:3px solid var(--zx-rule);position:relative}
:where(body.zx-deck) .zx-step::before{content:"";position:absolute;top:-3px;left:0;width:38%;height:3px;background:var(--zx-grad)}
:where(body.zx-deck) .zx-step-n{
  font-family:var(--zx-font-display);font-weight:700;font-size:144px;line-height:.92;letter-spacing:normal;
  /* iteration 6: the outline was rgba(...,.42), which composites to #919898 on
     the alt ground = 2.57:1 — under the 3.0 large-text floor, and invisible to
     a contrast walk because `color` is transparent (alpha 0 nodes are skipped).
     .72 composites to #4c5a5f = 6.27:1 — measured, in
     it6-measure.json, via the new stroke branch. */
  color:transparent;-webkit-text-stroke:2px rgba(11,31,42,.72);
}
:where(body.zx-deck) .zx-step:first-child .zx-step-n{
  -webkit-text-stroke-width:0;
  background:var(--zx-grad-ink);-webkit-background-clip:text;background-clip:text;color:transparent;
}
:where(body.zx-deck) .zx-step h3{color:var(--zx-ink)}
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-step-l{font-size:16px;line-height:1.72;color:var(--zx-dim)}

/* ========================================================================
   7 — DARK BREAK BAND. 192/192, the ambient sky bloom lives here.
   ======================================================================== */
:where(body.zx-deck) .zx-break{position:relative}
:where(body.zx-deck) .zx-break::before{
  content:"";position:absolute;left:50%;top:50%;width:min(760px,88%);height:340px;
  transform:translate(-50%,-50%);border-radius:var(--zx-r-pill);
  background:radial-gradient(closest-side,rgba(62,142,247,.30),transparent 78%);
  z-index:0;pointer-events:none;
}
:where(body.zx-deck) .zx-break .zx-wrap{z-index:2}
:where(body.zx-deck) .zx-break .zx-h2 .zx-d{color:#fff}
:where(body.zx-deck) .zx-break-rules{display:flex;gap:10px;justify-content:center;margin-bottom:16px}
:where(body.zx-deck) .zx-break-rules span{display:block;width:76px;height:6px;border-radius:var(--zx-r-3);background:var(--zx-grad);opacity:.95}
:where(body.zx-deck) .zx-break-rules span:nth-child(2){opacity:.55}
:where(body.zx-deck) .zx-break-rules span:nth-child(3){opacity:.25}
:where(body.zx-deck) .zx-break-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:28px;margin-top:56px;text-align:left}
:where(body.zx-deck) .zx-break-stat{padding:24px 0 0;border-top:1px solid var(--zx-rule-ink)}
/* on ink, the DECORATION pair is what reads: sky->mint on #061520 measures
   6.53 / 6.24, comfortably past the 3.0 large-text floor. It is only on cream
   and under white that the bright pair fails, and those uses take -grad-ink. */
:where(body.zx-deck) .zx-break-stat .zx-bn{
  display:block;font-family:var(--zx-font-display);font-weight:700;font-size:50px;line-height:1.1;letter-spacing:normal;
  background:var(--zx-grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}
:where(body.zx-deck) .zx-break-stat .zx-bl{display:block;font-family:var(--zx-font-body);font-weight:400;font-size:16px;line-height:1.7;color:var(--zx-on-ink-dim);margin-top:8px}

/* ========================================================================
   8 — COMPARISON. Reference §3.3 mechanism (2 panels, the branded one
   carries a multi-layer drop-shadow "growing highlight"), our colours.
   New class names: .zx-vs* never collides with the gate-checked .vs/.cmp set.
   ======================================================================== */
:where(body.zx-deck) .zx-vs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px;margin-top:56px;align-items:stretch}
:where(body.zx-deck) .zx-vs-panel{
  background:#fff;border-radius:var(--zx-r-1);padding:34px clamp(20px,2.4vw,34px) 36px;
  box-shadow:var(--zx-sh-2);
}
:where(body.zx-deck) .zx-vs-panel.zx-muted{background:var(--zx-alt);box-shadow:none;border:1px solid var(--zx-rule)}
:where(body.zx-deck) .zx-vs-panel.zx-us{
  background:var(--zx-ink);color:var(--zx-on-ink);
  box-shadow:0 0 0 1.5px rgba(62,142,247,.45),0 0 60px rgba(62,142,247,.42);
}
:where(body.zx-deck) .zx-vs-hd{display:flex;align-items:center;gap:10px;margin-bottom:22px}
:where(body.zx-deck) .zx-vs-hd h3{font-size:24px}
:where(body.zx-deck) .zx-vs-panel.zx-muted .zx-vs-hd h3{color:var(--zx-dim)}
:where(body.zx-deck) .zx-vs-panel.zx-us .zx-vs-hd h3{color:#fff}
:where(body.zx-deck) .zx-vs-badge{
  border-radius:var(--zx-r-3);padding:4px 9px;
  font-family:var(--zx-font-body);font-weight:700;font-size:12px;line-height:1.5;letter-spacing:.1em;text-transform:uppercase;
}
:where(body.zx-deck) .zx-vs-panel.zx-muted .zx-vs-badge{background:rgba(11,31,42,.08);color:var(--zx-dim)}
:where(body.zx-deck) .zx-vs-panel.zx-us .zx-vs-badge{background-image:var(--zx-grad-ink);color:#fff}
:where(body.zx-deck) .zx-vs-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
:where(body.zx-deck) .zx-vs-list li{display:grid;grid-template-columns:22px minmax(0,1fr);gap:14px;align-items:start;padding:16px 0;font-size:16px;line-height:1.72}
:where(body.zx-deck) .zx-vs-list li + li{border-top:1px solid var(--zx-rule)}
:where(body.zx-deck) .zx-vs-panel.zx-us .zx-vs-list li + li{border-top-color:var(--zx-rule-ink)}
:where(body.zx-deck) .zx-vs-panel.zx-muted .zx-vs-list li{color:var(--zx-dim)}
:where(body.zx-deck) .zx-vs-ic{width:22px;height:22px;flex:0 0 auto;margin-top:2px}
:where(body.zx-deck) .zx-vs-panel.zx-muted .zx-vs-ic{color:#94A3AD}
:where(body.zx-deck) .zx-vs-panel.zx-us .zx-vs-ic{color:var(--zx-mint)}

/* ========================================================================
   9 — FAQ. New class; .faq-item is gate-checked and untouched.
   ======================================================================== */
:where(body.zx-deck) .zx-faq-wrap{max-width:960px;margin-inline:auto;width:100%}
:where(body.zx-deck) .zx-faq{margin-top:44px}
:where(body.zx-deck) .zx-faq details{
  border-radius:var(--zx-r-1);background:#fff;box-shadow:var(--zx-sh-1);
  padding:0 clamp(18px,2.2vw,28px);margin-bottom:12px;
}
:where(body.zx-deck) .zx-faq summary{
  list-style:none;cursor:pointer;
  display:flex;justify-content:space-between;align-items:center;gap:18px;
  min-height:64px;padding:16px 0;
  font-family:var(--zx-font-display);font-weight:700;font-size:20px;line-height:1.5;letter-spacing:normal;
  color:var(--zx-ink);
}
:where(body.zx-deck) .zx-faq summary::-webkit-details-marker{display:none}
:where(body.zx-deck) .zx-faq summary::marker{content:""}
:where(body.zx-deck) .zx-chev{
  flex:0 0 auto;width:12px;height:12px;
  border-right:2.5px solid var(--zx-mint-ink);border-bottom:2.5px solid var(--zx-mint-ink);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform var(--zx-t);
}
:where(body.zx-deck) .zx-faq details[open] summary .zx-chev{transform:rotate(225deg) translate(-2px,-2px)}
:where(body.zx-deck) .zx-faq summary:focus-visible{outline:3px solid var(--zx-sky);outline-offset:-3px;border-radius:var(--zx-r-1)}
body.type-v2:where(.zx-deck) .zx .zx-faq details > p{
  margin:0 0 24px;font-size:16px;line-height:1.75;color:var(--zx-dim);max-width:70ch;
}

/* ========================================================================
   10 — PROOF AGAIN. The dated screenshot, in a browser chrome, on ink.
   ======================================================================== */
:where(body.zx-deck) .zx-proof2{display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);gap:56px;align-items:center}
:where(body.zx-deck) .zx-mock{
  margin:0;background:#fff;border-radius:var(--zx-r-1);overflow:hidden;
  box-shadow:var(--zx-sh-3),var(--zx-sh-1);
}
:where(body.zx-deck) .zx-mock-bar{display:flex;align-items:center;gap:7px;padding:11px 14px;background:var(--zx-alt);border-bottom:1px solid var(--zx-rule)}
:where(body.zx-deck) .zx-mock-dot{width:9px;height:9px;border-radius:var(--zx-r-pill);background:var(--zx-ink);opacity:.26}
:where(body.zx-deck) .zx-mock-url{margin-left:10px;font-family:var(--zx-font-body);font-weight:400;font-size:14px;line-height:1.4;color:var(--zx-dim)}
:where(body.zx-deck) .zx-mock img{display:block;width:100%;transition:transform var(--zx-t-slow)}
:where(body.zx-deck) .zx-mock:hover img{transform:scale(1.03)}
:where(body.zx-deck) .zx-proof2 .zx-num{font-size:70px}
/* on the ink band the DECORATION pair is the readable one: the deepened
   #0A6E4A on #0B1F2A measures 2.69:1 — it now FAILS even the large-text floor
   there, which is exactly why the ink bands take the bright pair instead:
   #3E8EF7 measures 5.16 and #18B87A 6.57 on #0B1F2A. Bright on dark, deep on
   light; iteration 6's deepening made that split load-bearing, not stylistic. */
:where(body.zx-deck) .zx-ink .zx-num,:where(body.zx-deck) .zx-ink-deep .zx-num{background-image:var(--zx-grad)}
:where(body.zx-deck) .zx-proof2 .zx-stat-lb{color:var(--zx-on-ink-dim)}

/* ========================================================================
   11 — FINAL CTA on the gradient band (the text-safe pair).
   ======================================================================== */
:where(body.zx-deck) .zx-final{max-width:820px;margin-inline:auto;text-align:center}
:where(body.zx-deck) .zx-final .zx-h2 .zx-d{color:#fff}
body.type-v2:where(.zx-deck) .zx .zx-grad p.zx-lead{color:#fff;margin-inline:auto}

/* ========================================================================
   ARCHIVE — every word of the live homepage, filed by topic, at the bottom.
   ======================================================================== */
:where(body.zx-deck) .zx .zx-archive{background:var(--zx-cream);color:var(--zx-ink);padding:var(--zx-s2) 0;border-top:1px solid var(--zx-rule)}
:where(body.zx-deck) .zx-archive .zx-wrap{max-width:1000px}
:where(body.zx-deck) .zx h2.zx-archive-h .zx-d{font-size:24px;margin-bottom:26px}
:where(body.zx-deck) .zx .dx{
  border-radius:var(--zx-r-1);border:0;background:#fff;box-shadow:var(--zx-sh-1);
  max-width:none;margin:0 0 10px;width:100%;overflow:hidden;padding:0 clamp(18px,2.2vw,26px);
}
:where(body.zx-deck) .zx .dx > summary{
  padding:18px 0;font-family:var(--zx-font-body);font-weight:700;font-size:14px;line-height:1.5;
  letter-spacing:.14em;text-transform:uppercase;justify-content:flex-start;gap:10px;
  min-height:48px;color:var(--zx-dim);background:transparent;
}
/* The archive disclosure is a CONTROL, so it moves like the deck's other
   controls: the kit's own --zx-t (0.3s, the ANGA interaction duration) on the
   properties that actually change - the label colour, the ground behind it, and
   the chevron. Measured before: `transition-duration: 0s` on `.dx > summary` on
   28 pages, i.e. hover and open/close snapped. Static rendering is unchanged,
   which is why the pixel gate does not move. */
:where(body.zx-deck) .zx .dx > summary{transition:color var(--zx-t),background-color var(--zx-t)}
:where(body.zx-deck) .zx .dx > summary .dx-chev{transition:transform var(--zx-t),color var(--zx-t)}
:where(body.zx-deck) .zx .dx > summary:hover{background:transparent;color:var(--zx-ink)}
:where(body.zx-deck) .zx .dx > summary .dx-chev{color:var(--zx-mint-ink);width:18px;height:18px}
:where(body.zx-deck) .zx .dx > summary:focus-visible{outline:3px solid var(--zx-sky);outline-offset:-3px;background:transparent}
:where(body.zx-deck) .zx .dx-body{padding:14px 0 24px;border-top:1px solid var(--zx-rule)}
body.type-v2:where(.zx-deck) .zx .dx-body p{font-size:14px;line-height:1.8;max-width:76ch;margin:0 0 12px;color:var(--zx-dim)}
body.type-v2:where(.zx-deck) .zx .dx-body li{font-size:14px;line-height:1.8;color:var(--zx-dim)}
:where(body.zx-deck) .zx .dx-body ul{margin:0 0 12px;padding-left:20px}

/* -- progress dots (desktop only) ----------------------------------------
   NOTE, iteration 4: this component shipped DEAD in iteration 3 — the markup
   said class="zdots" id="zdots" while the CSS said `.zx-dots` and the JS said
   `#zx-dots a`, so neither ever matched. Markup, CSS and JS now agree. */
:where(body.zx-deck) .zx-dots{position:fixed;right:10px;top:50%;transform:translateY(-50%);z-index:18;display:none;flex-direction:column;gap:0}
@media(min-width:1240px){:where(body.zx-deck) .zx-dots{display:flex}}
:where(body.zx-deck) .zx-dots a{display:flex;align-items:center;justify-content:flex-end;width:46px;height:44px}
/* the rail crosses cream, alt, ink and gradient bands. A fixed ink dot is
   invisible on the ink ones (measured: only the active dot was visible over the
   hero). `mix-blend-mode:difference` on white inverts against whatever is
   behind it, so one rule reads on every band. */
:where(body.zx-deck) .zx-dots a::before{content:"";display:block;width:18px;height:2px;border-radius:var(--zx-r-3);background:#fff;mix-blend-mode:difference;opacity:.38;transition:opacity var(--zx-t),width var(--zx-t),background var(--zx-t)}
:where(body.zx-deck) .zx-dots a.on::before{opacity:1;width:34px;background:none;background-image:var(--zx-grad);mix-blend-mode:normal}
:where(body.zx-deck) .zx-dots a:focus-visible{outline:2px solid var(--zx-sky);outline-offset:2px}

/* -- entrance motion: .9s, only on reveal ---------------------------------
   THE GATE CLASS IS `zx-deck-js`, AND ZX-DECK.JS IS WHAT SETS IT (review HIGH
   #3). `.zx-rv{opacity:0}` is fail-CLOSED: the only thing that ever clears it
   is `.zx-in`, written by zx-deck.js. Gate it on the head script's generic `js`
   class instead and a 404, a CSP block or a syntax error on zx-deck.js leaves
   every below-fold section permanently invisible - on every adopting page,
   silently. Keyed on a class the runtime itself sets, the hide can never
   outlive the unhide. One class either way, so no specificity moves.
   `.zx-w` below stays on `.js` DELIBERATELY: it is a keyframe animation with
   `both` fill, so it ends visible with or without the runtime - already
   fail-open - and it is the only reveal primitive that renders above the fold
   (measured: 5 `.zx-w` and 0 `.zx-rv` in the first viewport at 1440 and at
   390). Re-keying it to a deferred-script class would swap a real failure mode
   for a real flash. */
.zx-deck-js :where(body.zx-deck) .zx-rv{opacity:0;transform:translateY(22px);transition:opacity var(--zx-t-in),transform var(--zx-t-in)}
.zx-deck-js :where(body.zx-deck) .zx-rv.zx-in{opacity:1;transform:none}
.js :where(body.zx-deck) .zx-w{opacity:0;transform:translateY(.4em);animation:zx-rise .9s cubic-bezier(.4,0,.2,1) both}
@keyframes zx-rise{from{opacity:0;transform:translateY(.45em)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion:reduce){
  :where(body.zx-deck) .zx *,:where(body.zx-deck) .zx *::before,:where(body.zx-deck) .zx *::after{animation:none!important;transition:none!important}
  .js :where(body.zx-deck) .zx-w,.zx-deck-js :where(body.zx-deck) .zx-rv{opacity:1!important;transform:none!important}
  :where(body.zx-deck) .zx-key::after{transform:scaleX(1)}
  :where(body.zx-deck) .zx-video{display:none}
  .zx-deck-js :where(body.zx-deck) .zx-stagger > .zx-rv{transition-delay:0s!important}
}
:where(body.zx-deck) .zx.zx-no-vid .zx-video{display:none}


/* ========================================================================
   THE TICKET STRIP - the deck's one physical metaphor, iteration 6.

   Iteration 5 shipped exactly one original object (the die-cut price ticket)
   in exactly one slide, which is what eval-5 major #1 named as the ceiling:
   "it breaks the equal-box pattern in one slide, not throughout". So the
   ticket is no longer a component. It is the FORMAT.

   The page is a strip of ten numbered tickets, torn apart as you scroll:
     - every slide carries its number on a die-cut counterfoil (.zx-stub) -
       the same dashed edge and the same bitten-out notches as the price
       ticket, at an eighth of the size;
     - every slide boundary is a real perforation (.zx-tear) - a dashed rule
       with a semicircular bite taken out of each end, coloured with the
       PREVIOUS slide's own ground so the band above reads as eating into
       the one below. Ten slides, nine tears, one strip;
     - the price rack (slide 03) is unchanged - it is now the strip's own
       vocabulary at full size instead of a one-off;
     - slide 07 is ONE ticket torn down the middle: what we refuse on the
       discarded half, what we always do on the half you keep (.zx-vs-seam);
     - slide 09 is the torn stub itself: the dated Perplexity screenshot is
       the ticket window, the perforation runs under it, and the answer the
       assistants actually printed is the figure printed on the stub - the
       same sentence the slide already carried, promoted from a caption to
       the thing the object exists to show;
     - the progress dots are the rack the strip hangs from (.zx-dots-rail).
   Nothing else was added. One metaphor, ten slides, no second idea.
   ======================================================================== */

/* each ground publishes its own colour so the die-cut parts can be painted
   with the band they are punched out of, instead of a hard-coded guess. */
:where(body.zx-deck) .zx-sec.zx-cream{--zx-ground:var(--zx-cream)}
:where(body.zx-deck) .zx-sec.zx-alt{--zx-ground:var(--zx-alt)}
:where(body.zx-deck) .zx-sec.zx-ink{--zx-ground:var(--zx-ink)}
:where(body.zx-deck) .zx-sec.zx-ink-deep{--zx-ground:var(--zx-ink-deep)}
/* the gradient band has no single colour, so its counterfoil keeps the
   dashed outline and drops the discs rather than painting a wrong flat blue. */
:where(body.zx-deck) .zx-sec.zx-grad{--zx-ground:none}

/* -- the perforation between two tickets ---------------------------------- */
:where(body.zx-deck) .zx-tear{
  position:absolute;left:0;right:0;top:0;height:0;z-index:6;
  display:block;pointer-events:none;
  border-top:2px dashed var(--zx-tear-c,rgba(11,31,42,.26));
}
:where(body.zx-deck) .zx-ink .zx-tear,:where(body.zx-deck) .zx-ink-deep .zx-tear,:where(body.zx-deck) .zx-grad .zx-tear{--zx-tear-c:rgba(241,236,225,.30)}
/* the bite. 22px discs centred on the slide's own left and right edges,
   painted in the PREVIOUS slide's ground - the section's overflow:hidden
   clips each one to a half-moon, which is exactly the shape a perforated
   edge leaves. Same 22px and same pill radius as the price ticket's own
   notch, so the radius census does not move. */
:where(body.zx-deck) .zx-tear::before,:where(body.zx-deck) .zx-tear::after{
  content:"";position:absolute;top:-13px;width:22px;height:22px;
  border-radius:var(--zx-r-pill);background:var(--zx-prev,transparent);
}
:where(body.zx-deck) .zx-tear::before{left:-11px}
:where(body.zx-deck) .zx-tear::after{right:-11px}
/* KIT V2 / R7. The previous slide's ground used to be keyed to nine
   page-specific ids and declared TWICE (once on the tear, once on the section),
   so a second deck with different section ids silently lost every half-moon
   bite. It is now ONE declaration per band, driven by the `data-prev-ground`
   attribute the slide carries. Custom properties inherit, so this single
   declaration feeds BOTH the tear's two notch discs and the slide's own
   sawtooth ::before below. */
:where(body.zx-deck) [data-prev-ground="cream"]{--zx-prev:var(--zx-cream)}
:where(body.zx-deck) [data-prev-ground="alt"]{--zx-prev:var(--zx-alt)}
:where(body.zx-deck) [data-prev-ground="ink"]{--zx-prev:var(--zx-ink)}
:where(body.zx-deck) [data-prev-ground="ink-deep"]{--zx-prev:var(--zx-ink-deep)}
:where(body.zx-deck) [data-prev-ground="none"]{--zx-prev:transparent}

/* -- the numbered counterfoil, once per slide ----------------------------- */
:where(body.zx-deck) .zx-stub{
  position:relative;display:flex;width:fit-content;align-items:center;justify-content:center;
  min-width:54px;height:28px;padding:0 13px;margin:0 0 16px;
  border:1.5px dashed var(--zx-rule);border-radius:var(--zx-r-3);
  font-family:var(--zx-font-display);font-weight:700;font-size:14px;line-height:1;
  letter-spacing:.16em;color:var(--zx-dim);
}
:where(body.zx-deck) .zx-stub-n{font:inherit;letter-spacing:inherit}
/* the two bites, 10px, on the existing pill tier */
:where(body.zx-deck) .zx-stub::before,:where(body.zx-deck) .zx-stub::after{
  content:"";position:absolute;top:50%;margin-top:-5px;width:10px;height:10px;
  border-radius:var(--zx-r-pill);background:var(--zx-ground,var(--zx-cream));
}
:where(body.zx-deck) .zx-stub::before{left:-5px}
:where(body.zx-deck) .zx-stub::after{right:-5px}
:where(body.zx-deck) .zx-grad .zx-stub::before,:where(body.zx-deck) .zx-grad .zx-stub::after{display:none}
/* on ink the counterfoil is printed in the light ink-side pair: #A9BCC8 on
   #0B1F2A measures 8.24:1, on #061520 9.01:1. */
:where(body.zx-deck) .zx-stub-ink{border-color:var(--zx-rule-ink);color:var(--zx-on-ink-dim)}
:where(body.zx-deck) .zx-grad .zx-stub{border-color:rgba(255,255,255,.62);color:#fff}
:where(body.zx-deck) .zx-center .zx-stub{margin-inline:auto}
/* slide 01: the counterfoil rides the kicker row instead of opening a line of
   its own, so numbering the deck costs the hero exactly 0px of fold budget. */
:where(body.zx-deck) .zx-kicker-row{display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:16px}
:where(body.zx-deck) .zx-kicker-row .zx-stub{margin:0}
body.type-v2:where(.zx-deck) .zx .zx-hero .zx-kicker-row p.zx-hero-chip{margin:0}

/* -- the long-copy file, now filed under its own slide -------------------- */
:where(body.zx-deck) .zx-more{margin-top:44px}
:where(body.zx-deck) .zx-ink .zx-more .dx,:where(body.zx-deck) .zx-ink-deep .zx-more .dx{background:rgba(241,236,225,.06);box-shadow:none;border:1px solid var(--zx-rule-ink)}
:where(body.zx-deck) .zx-ink .zx-more .dx > summary,:where(body.zx-deck) .zx-ink-deep .zx-more .dx > summary{color:#C7D6DF}
:where(body.zx-deck) .zx-ink .zx-more .dx > summary:hover,:where(body.zx-deck) .zx-ink-deep .zx-more .dx > summary:hover{color:#fff}
:where(body.zx-deck) .zx-ink .zx-more .dx > summary .dx-chev,:where(body.zx-deck) .zx-ink-deep .zx-more .dx > summary .dx-chev{color:#8FE3C4}
:where(body.zx-deck) .zx-ink .zx-more .dx-body,:where(body.zx-deck) .zx-ink-deep .zx-more .dx-body{border-top-color:var(--zx-rule-ink)}
body.type-v2:where(.zx-deck) .zx .zx-ink .zx-more .dx-body p,body.type-v2:where(.zx-deck) .zx .zx-ink-deep .zx-more .dx-body p,body.type-v2:where(.zx-deck) .zx .zx-ink .zx-more .dx-body li,body.type-v2:where(.zx-deck) .zx .zx-ink-deep .zx-more .dx-body li{color:#C7D6DF}

/* -- slide 07: one ticket, torn down the middle --------------------------- */
/* the seam is a child of the DISCARDED half, not of the grid, so it lands on
   the real gap at any breakpoint instead of on a guessed 50%. The offsets
   are measured, not derived: the `right` / `bottom` offsets resolve against
   the PADDING box, so the panel's own 1px border is paid for too. -14px lands
   the
   2px rule dead centre of the 708..732 desktop gap (centre 720) and -13px
   dead centre of the 6581..6603 mobile one (centre 6592). Verified in
   it6-measure.json -> seam. Notch discs at both ends, painted in the cream ground. */
:where(body.zx-deck) .zx-vs-panel{position:relative}
:where(body.zx-deck) .zx-vs-seam{
  position:absolute;top:0;bottom:0;right:-14px;width:0;z-index:3;
  border-left:2px dashed rgba(11,31,42,.26);pointer-events:none;
}
:where(body.zx-deck) .zx-vs-seam::before,:where(body.zx-deck) .zx-vs-seam::after{
  content:"";position:absolute;left:-11px;width:22px;height:22px;
  border-radius:var(--zx-r-pill);background:var(--zx-cream);
}
:where(body.zx-deck) .zx-vs-seam::before{top:-11px}
:where(body.zx-deck) .zx-vs-seam::after{bottom:-11px}

/* -- slide 09: the torn stub ---------------------------------------------- */
:where(body.zx-deck) .zx-tornstub{
  margin:0;background:var(--zx-cream);
  border-radius:var(--zx-r-1);border:1px solid var(--zx-rule);
  box-shadow:var(--zx-sh-3),var(--zx-sh-1);
  display:flex;flex-direction:column;overflow:visible;
  --zx-notch:var(--zx-ink);
}
:where(body.zx-deck) .zx-tornstub .zx-tk-top{padding:14px 14px 0}
:where(body.zx-deck) .zx-tornstub .zx-mock{border:1px solid var(--zx-rule);box-shadow:none;border-radius:var(--zx-r-2)}
:where(body.zx-deck) .zx-tornstub .zx-tk-stub{padding:18px 22px 22px}
:where(body.zx-deck) .zx-quote-lb{
  display:block;margin-bottom:8px;
  font-family:var(--zx-font-body);font-weight:700;font-size:12px;line-height:1.5;
  letter-spacing:.18em;text-transform:uppercase;color:var(--zx-mint-ink);
}
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-quote{
  font-family:var(--zx-font-display);font-weight:700;font-size:21px;line-height:1.55;
  letter-spacing:normal;color:var(--zx-ink);max-width:46ch;margin:0;
}
/* the perforation disc is painted with the band it is punched out of, and
   that is not the same band in slide 03 (alt) and slide 09 (ink). */
:where(body.zx-deck) .zx-perf::before,:where(body.zx-deck) .zx-perf::after{background:var(--zx-notch,var(--zx-alt))}

/* -- the rack the strip hangs from ---------------------------------------- */
:where(body.zx-deck) .zx-dots-rail{
  position:absolute;right:22px;top:6px;bottom:6px;width:0;
  border-left:2px dashed #fff;mix-blend-mode:difference;opacity:.22;
}
:where(body.zx-deck) .zx-dots a > i{
  position:absolute;right:52px;
  font-family:var(--zx-font-display);font-weight:700;font-style:normal;
  font-size:12px;line-height:1;letter-spacing:.14em;
  padding:6px 9px;border-radius:var(--zx-r-3);
  background:var(--zx-ink);color:#fff;white-space:nowrap;
  opacity:0;transform:translateX(4px);pointer-events:none;
  transition:opacity var(--zx-t),transform var(--zx-t);
}
:where(body.zx-deck) .zx-dots a:hover > i,:where(body.zx-deck) .zx-dots a:focus-visible > i{opacity:1;transform:none}

/* ==========================================================================
   SHARED CHROME THE DECK DEPENDS ON

   These do not style a deck primitive - they patch header / footer / FAB /
   sticky-bar chrome that lives in shared.css. They are here, and not in an
   adopting page's own block, because a deck page CANNOT render correctly
   without them and every page that copied them by hand got a different subset:

   * review HIGH #2 - `.zx-fabs-away` is toggled by zx-deck.js. Shipping the
     runtime without the rules that give the class meaning is a class toggle
     that paints nothing.
   * review #12 - `.nav .btn-line{display:none}` and the `.sticky-cta`
     show/reveal pair lived only in index-v2's page block, so a literal adopter
     got a two-line overlapping header pill at 390 and no primary mobile CTA.

   SPECIFICITY IS PINNED, NOT INCIDENTAL. Each rule keeps the exact weight it
   had when it lived in the page block - `body.zx:where(.zx-deck)` at (0,2,1) /
   (0,3,1) for the patch set, bare `:where(body.zx-deck)` at (0,1,0) / (0,2,0)
   for the chrome. That is what lets them move from a LATE page block into an
   EARLIER stylesheet without changing a pixel: every one of them out-specifies
   its competitor (tok-v3's `body.tok-v3 .fb-fab` at (0,2,0), shared.css's
   `.maps-fab{border-radius:50%}` at (0,1,0), glow3's LINE-CTA rule at (0,5,0)
   which is why the third selector below carries five `:not()`s), so source
   order is no longer load-bearing for any of them. Do not lower it.

   What is NOT here, deliberately: shared.css already owns `.sticky-cta`'s
   mobile geometry and the FAB stacking at <=860 with `!important`, and it
   already hides the round FABs when sticky-bar markup exists. The kit adds only
   what shared.css leaves undecided.
   ========================================================================== */

/* (1) CONTRAST. `.btn-line` is white on LINE green #06C755 = 2.26:1 at both
   14.5px/700 (header pill) and 17px/700 (mobile bar); both need 4.5. The green
   is LINE's own brand asset, so the LABEL flips to our ink instead: 7.48:1. */
body.zx:where(.zx-deck) .btn-line,
body.zx:where(.zx-deck) .btn-line:hover,
body.zx:where(.zx-deck) .js-line-cta:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab):not(.zx-btn){color:var(--zx-ink)!important}
body.zx:where(.zx-deck) .btn-line svg{color:var(--zx-ink)}

/* (2) RADIUS CENSUS. The three FABs ship `border-radius:50%` and the burger
   bars 2px - a fifth and sixth value on a 999/20/10/4 system. On a square box
   999px and 50% render the identical circle, and on a 2px-tall bar 2px and 4px
   round to the same visible cap, so this is a census fix with zero moved px. */
body.zx:where(.zx-deck) .maps-fab,body.zx:where(.zx-deck) .fb-fab,body.zx:where(.zx-deck) .line-fab{border-radius:var(--zx-r-pill)}
body.zx:where(.zx-deck) .nav-burger span{border-radius:var(--zx-r-3)}

/* (3) TAP TARGETS at 390. Footer sitemap links are 18px inline boxes and the
   burger measures 36x44. padding-top/bottom and min-width grow an inline box's
   hit rect without moving line layout; the line-height bump stops two padded
   rows overlapping. `a.btn-check` measured 43.6px - min-height 40->44 lands it
   at exactly 44.0 with no visual drift, where padding would have dragged the
   nav row 4px. None of these is a property SC18 guards on a shared responsive
   block, which is exactly why the fixes use them. */
body.zx:where(.zx-deck) .nav-burger{min-width:44px}
body.zx:where(.zx-deck) footer .foot span{line-height:2.9}
body.zx:where(.zx-deck) footer .foot a{padding-top:13px;padding-bottom:13px;padding-inline:3px}
body.zx:where(.zx-deck) a.btn-check{min-height:44px}

/* (4) THE FAB DODGE - the CSS half of `body.zx-fabs-away`, written by
   zx-deck.js. The two FABs are shared chrome whose `display` is a shared
   responsive contract (SC18), so they cannot be hidden - but they CAN yield:
   scrolling down slides them off the right edge, scrolling up brings them back.
   That is what stops them landing on card corners and on the carousel arrow at
   390. zx-deck.js never applies the class under prefers-reduced-motion; the
   override below is belt-and-braces. */
body.zx:where(.zx-deck) .maps-fab,body.zx:where(.zx-deck) .fb-fab{
  transition:transform .28s var(--zx-ease),opacity .28s var(--zx-ease);
}
body.zx:where(.zx-deck).zx-fabs-away .maps-fab,body.zx:where(.zx-deck).zx-fabs-away .fb-fab{
  transform:translateX(160%);opacity:0;
}
@media (prefers-reduced-motion:reduce){
  body.zx:where(.zx-deck) .maps-fab,body.zx:where(.zx-deck) .fb-fab{transition:none}
  body.zx:where(.zx-deck).zx-fabs-away .maps-fab,body.zx:where(.zx-deck).zx-fabs-away .fb-fab{transform:none;opacity:1}
}

/* (5a) `.link-pill` IS A CONTROL, SO IT GETS A CONTROL'S TAP TARGET.
   Post-rollout sweep: `a.link-pill` measured 300x32 / 189x21 / 142x21 at 390 on
   six INDUSTRY pages (clinic, restaurant, factory, dorm, nail-salon,
   ai-agency-thailand) - i.e. it was rendering as inline text, 21px tall,
   against a 44px tap-target bar. No stylesheet in the repo defines `.link-pill`
   at all; every page that used one styled it inline or not at all, which is why
   it drifted. Kit v1 deliberately excludes `.link-pill` from every `.zx-btn`
   rule (it is one of the four `:not()` arguments), so it needs its own.
   LAYOUT ONLY - no background, border, radius or colour - so the look each page
   gave it survives and this is purely the hit rect. index-v2 carries zero
   `.link-pill` elements, so this is a no-op here. */
:where(body.zx-deck) a.link-pill{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:44px;padding:10px 18px;line-height:1.3;
}

/* (5b) HEADING WEIGHT - ANGA's 700, restored at kit level.
   shared.css:76-77 force `h1{font-weight:500!important}` and
   `h2,.h2{font-weight:600!important}` site-wide. On index-v2 that was undone by
   a PAGE-LOCAL `body.tok-v3 h1/h2{font-weight:700!important}`, so every page
   adopting the deck without tok-v3 rendered its headlines thin. Measured on the
   proof page before this rule: h1 element 500, h2 element 600, archive h2 600.
   The deck's own display face was never affected - `.zx-d` is a span carrying
   its own `font-weight:700`, so a heading whose text is fully inside `.zx-d`
   looked right either way. The bug bites the moment a headline has ANY text
   outside that span: an editorial h1 with a `<br>`, an `<em>`, a link, or a
   template that writes into the h1 directly. That is why it surfaced on the
   ARTICLE template and not here.
   (0,2,1) + !important beats shared.css's (0,0,1) + !important. h3 is included
   for completeness and is a no-op on index-v2 - census: 1 h1, 8 h2, 10 h3, all
   already 700. tok-v3 is no longer required by any deck page. */
body.zx:where(.zx-deck) h1,
body.zx:where(.zx-deck) h2,
body.zx:where(.zx-deck) .h2,
body.zx:where(.zx-deck) h3{font-weight:700!important}

/* (5f) THE MESSENGER BUTTON GETS A CONTRAST-CORRECTED BLUE FROM THE KIT.
   Pages were hand-rolling `background:#0866FF` (4.82:1 against white, under the
   floor) inline on `a.btn-line`. The kit now owns the colour as `--zx-msgr`
   (#0757D6, 6.28:1) and applies it to the two classes a page should use.
   `!important` because the values it has to beat are INLINE page styles.
   NOTE, and this is a page fix not a kit fix: google-maps' Messenger button
   carries neither class - it is a bare `a.btn-line` with the whole treatment
   inline - so it is NOT reached by this rule. That page needs `msgr` added and
   its inline `background`/`color` deleted. index-v2 uses `.zx-cta-fb`, a ghost
   button with no fill, and carries no `.msgr` at all: no-op here. */
:where(body.zx-deck) a.btn-line.msgr,
:where(body.zx-deck) a.zx-cta-msgr,
:where(body.zx-deck) .zx a.zx-btn.zx-cta-msgr{
  background-color:var(--zx-msgr)!important;
  background-image:none!important;
  color:#fff!important;
  border-color:transparent!important;
}

/* (5e) THE GRADIENT BAND IS TEXT-SAFE BY CONSTRUCTION.

   Post-rollout sweep: CTA text on `.zx-grad` measured 1.05-1.09 on google-maps
   (`#cta p`) and line-oa-guide (`#cta h3` / `p`), while index-v2's own `#cta`
   passed. The structural difference, measured rather than guessed: index-v2
   writes every word through a kit class that already carries a light colour
   (`.zx-final .zx-h2 .zx-d`, `.zx-grad p.zx-lead`, `.zx-grad .zx-eyebrow`),
   whereas the rollout pages drop a plain `<p>` straight onto the band - and in
   google-maps' case with an INLINE `style="color:var(--muted)"`, computed
   `rgb(92,102,96)`. Dark grey on a #1A5CBE -> #0A6E4A band is ~1.05:1.

   The kit's `.zx-sec.zx-grad{color:#fff}` cannot save that: it sets the
   INHERITED colour, and any explicit `color` on the element - especially an
   inline one - wins. An inline non-`!important` declaration is beaten only by
   an `!important` rule in a stylesheet, so that is what this is. It is the one
   place in this file where `!important` buys a contrast floor rather than a
   specificity fight, and it is deliberately limited to text-bearing BLOCK
   elements: controls (`a`, `button`), the counterfoil `<b>`, icons and anything
   with its own ground rule are untouched, so the white-fill CTA button on this
   band keeps its ink label.

   MEASURED (WCAG relative luminance, both gradient stops):
     #fff      on #1A5CBE = 6.33   on #0A6E4A = 6.28   <- worst stop 6.28
     #F1ECE1   on #1A5CBE = 5.37   on #0A6E4A = 5.33   <- worst stop 5.33
   Both clear the 5.0 floor, so a page may set `--zx-on-grad:var(--zx-on-ink)`
   and stay safe. The default is #fff because that is what the deck already
   renders, which is why this rule moves nothing on index-v2.
  
 SCOPE, and this is the whole subtlety: the rule reaches the BAND's own copy
   only - direct children of `.zx-wrap` and of `.zx-final` - never arbitrary
   descendants. A first version used a plain descendant combinator and painted
   white onto a page-local `.ctabox` card that sits inside `#cta` with its OWN
   light gradient, measured 1.09. A card inside `.zx-grad` OWNS ITS OWN TEXT
   COLOUR: it brings its own background, so the band's assumption about what is
   behind the text stops being true at its edge. Scoping to the two structural
   slots index-v2's own `#cta` uses (`.zx-grad > .zx-wrap > .zx-final > h2|p`)
   keeps the band safe and leaves every nested surface alone. */
:where(body.zx-deck) .zx-sec.zx-grad > .zx-wrap > :is(p,li,h1,h2,h3,h4,h5,h6,dt,dd,blockquote,figcaption,small),
:where(body.zx-deck) .zx-sec.zx-grad > .zx-wrap > .zx-final > :is(p,li,h1,h2,h3,h4,h5,h6,dt,dd,blockquote,figcaption,small){
  color:var(--zx-on-grad,#fff)!important;
}

/* (5d) `.cmpbox` KEEPS THE ANCHOR SECTION RHYTHM.  RESTORES A VALUE BROKEN BY
   shared.css:345 - SC19 PARITY, NOT A RESTYLE.

   `shared.css:345` ships `body:not(.lp-page) section{padding:56px 0}` and, at
   >=1024px, `{padding:108px 0}`, both at (0,1,2). `premium.css`'s
   `.premium section{padding-block:var(--sec-y)}` is (0,1,1) and loses. Kit v1's
   own section rule would have won at (0,13,2) - but it carries twelve `:not()`
   arguments that deliberately exclude every gate-checked class, `.cmpbox`
   included, precisely so the kit never restyles one. So on a deck page (which
   drops `.lp-page`) a `<section class="cmpbox">` falls through to 108px while
   the anchor page renders `var(--sec-y)`, and SC19 reports the divergence.

   This rule hands `.cmpbox` back the value the anchor already uses. It changes
   ONE property, to the value premium.css intended, and touches nothing else
   about the box - not its colour, border, radius, shadow, grid or type. That is
   a repair, not a restyle, and it is the only reason a gate class is named in
   this file at all.

   (0,2,2) - body + .zx + section + .cmpbox - beats shared.css's (0,1,2) on
   weight, so no `!important` is spent and the media-query variant is covered
   by the same rule.

   AUDITED, so nobody has to re-derive it: `.cmpbox` is the ONLY gate class that
   is ever a `<section>` anywhere in landing/ - 7 occurrences in 7 files
   (case-mdcc, google-maps, line-oa, medical-tourism, medical-tourism-v2,
   services, why-zipai). Every other gate class renders as <div>, <a> or
   <details> (answer-box 34x div, answer-block 18x div, faq-item 154x div +
   10x details, blk 132x div + 12x a, cmp-us / cmp-lbl 57x div each, vs-col 36x
   div, cmpgrid 7x div, mt-stat 6x div, proofgrid 4x div), so shared.css:345
   cannot reach them and none needs the same repair. Re-run the audit if a new
   page wraps one of them in a <section>. */
body.zx:where(.zx-deck) section.cmpbox{padding-block:var(--sec-y)}

/* (5c) THE REST OF WHAT tok-v3 WAS SILENTLY PROVIDING.
   Each line below was measured as a real difference between index-v2 (carries
   tok-v3) and a deck page that does not, so each is chrome a deck page needs:
     .line-fab shadow   index-v2 rgba(6,199,85,.30) 0 8px 26px  vs  none
     FAB radius         tok-v3's `border-radius:50%` at (0,2,1) TIED the kit's
                        own pill rule and won on source order, so the kit's
                        radius-census fix was dead on this page. Deleting the
                        tok-v3 line hands it back to the kit; on a square box
                        50% and 999px render the identical circle, so no pixel
                        moves - it just stops being two owners disagreeing.
     hover transitions  .btn-line 0.15s x5 on transform/box-shadow/background/
                        color/border-color  vs  0.18s x3;
                        .nav-pages a  0.15s x5  vs  0s/all (no transition)
     .wrap gutter       footer .wrap padding-inline 32px  vs  20px
   All four tokens (--sh3-cta, --dur3-hover, --ease3, --sec-x) are declared at
   :root in shared.css, not under tok-v3, so they resolve on any page. */
body.zx:where(.zx-deck) .line-fab{box-shadow:var(--sh3-cta)}
body.zx:where(.zx-deck) .btn-line,
body.zx:where(.zx-deck) .btn-check,
body.zx:where(.zx-deck) .line-fab,
body.zx:where(.zx-deck) .fb-fab,
body.zx:where(.zx-deck) .nav-pages a{
  transition:transform var(--dur3-hover) var(--ease3),
             box-shadow var(--dur3-hover) var(--ease3),
             background var(--dur3-hover) var(--ease3),
             color var(--dur3-hover) var(--ease3),
             border-color var(--dur3-hover) var(--ease3);
}
body.zx:where(.zx-deck) .wrap{padding-inline:var(--sec-x)}

/* (5) THE MOBILE BAR AND THE HEADER PILL (review #12). shared.css positions
   `.sticky-cta` at <=860 with `!important` but never decides whether it is
   SHOWN, and it has no opinion at all about the header's LINE pill. Both
   decisions are the deck's, and without them an adopting page renders a
   permanently-visible bar on desktop and a header pill that wraps to a second
   line at 390 and overlaps the burger. `--line` carries shared.css's own
   fallback so this works on a page that declares no palette of its own. */
:where(body.zx-deck) .sticky-cta{
  position:fixed;left:0;right:0;bottom:0;z-index:110;
  padding:10px 16px calc(10px + env(safe-area-inset-bottom));
  background:rgba(250,246,238,.985);
  border-top:1px solid var(--line,#ddd5c4);
  display:none;transform:translateY(110%);transition:transform .3s ease;
}
:where(body.zx-deck) .sticky-cta.on{transform:none}
:where(body.zx-deck) .sticky-cta .btn-line{width:100%;justify-content:center}
@media (max-width:860px){
  :where(body.zx-deck) .sticky-cta{display:block}
  :where(body.zx-deck) .nav .btn-line{display:none}
  :where(body.zx-deck) .line-fab{display:none}
  :where(body.zx-deck) .fb-fab{display:none}
}

/* ========================================================================
   RESPONSIVE. 1180 / 900 / 640. Everything stays inside the container.
   Token steps are declared on `body.zx` for the same specificity reason as
   the base block above.
   ======================================================================== */
@media(max-width:1180px){
  /* MEDIUM #9. Kit v1 ships ONE fixed 48/96/144/192 ladder and its own section
     rules read --zx-s1..s4 directly, so the deck's breakpoint values have to
     land on those names. They are no longer written there as bare numbers: the
     deck owns a NAMED scale, --zx-deck-s1..s4, and then re-points kit v1's
     tokens at it. Identical computed value, but the numbers now have an owner
     and a name, and it is visible at a glance that these are the DECK's
     responsive ladder and not an anonymous overwrite of the frozen kit's. */
  body.zx:where(.zx-deck){
    --zx-deck-s3:112px; --zx-deck-s4:144px;
    --zx-s3:var(--zx-deck-s3); --zx-s4:var(--zx-deck-s4);
  }
  :where(body.zx-deck) .zx-h1 .zx-d{font-size:54px}
  :where(body.zx-deck) .zx-h2 .zx-d{font-size:40px}
  :where(body.zx-deck) .zx-step-n{font-size:110px}
  :where(body.zx-deck) .zx-price{font-size:60px}
  body.zx:where(.zx-deck) .zx-ticket{flex:0 0 46%}
  :where(body.zx-deck) .zx-proof2{grid-template-columns:1fr;gap:36px}
  :where(body.zx-deck) .zx-ab-grid{grid-template-columns:1fr;gap:26px}
}
@media(max-width:900px){
  body.zx:where(.zx-deck){
    --zx-deck-s1:40px; --zx-deck-s2:72px; --zx-deck-s3:88px; --zx-deck-s4:104px;
    --zx-s1:var(--zx-deck-s1); --zx-s2:var(--zx-deck-s2);
    --zx-s3:var(--zx-deck-s3); --zx-s4:var(--zx-deck-s4);
    --zx-hdr:0px;
  }
  /* HERO on a phone: same centred stack, and the frame goes full-bleed with a
     locked 46svh height so it can never be squeezed below the 40%-of-fold floor
     the brief sets for mobile. The copy above it stays short enough that both
     CTAs are still above the fold without scrolling. */
  :where(body.zx-deck) .zx-hero-in{padding-top:28px}
  :where(body.zx-deck) .zx-hero-copy{max-width:none}
  :where(body.zx-deck) .zx-hero-frame{
    max-width:none;margin-top:26px;
    aspect-ratio:auto;height:46svh;min-height:310px;
    border-radius:var(--zx-r-1);
  }
  /* the phone crop is nearly square, so the 21:9 desktop framing does not
     survive it: centred, it lands on the nearer figure's back. Biasing right
     puts the two monitors and the desk in frame instead. */
  :where(body.zx-deck) .zx-video{object-position:74% 42%}
  :where(body.zx-deck) .zx-proofrow{grid-template-columns:1fr;gap:20px}
  :where(body.zx-deck) .zx-steps{grid-template-columns:1fr;gap:26px;margin-top:40px}
  :where(body.zx-deck) .zx-step-n{font-size:88px}
  :where(body.zx-deck) .zx-vs{grid-template-columns:1fr;gap:16px}
  /* single column: the tear turns and runs along the gap UNDER the discarded
     half (gap 16 -> 8px below the panel), and the two discs swap to the ends
     of a horizontal rule. Same object, rotated. */
  :where(body.zx-deck) .zx-vs-seam{top:auto;bottom:-13px;left:0;right:0;width:auto;height:0;
    border-left:0;border-top:2px dashed rgba(11,31,42,.26)}
  :where(body.zx-deck) .zx-vs-seam::before,:where(body.zx-deck) .zx-vs-seam::after{top:-11px;bottom:auto}
  :where(body.zx-deck) .zx-vs-seam::before{left:-11px}
  :where(body.zx-deck) .zx-vs-seam::after{left:auto;right:-11px}
  :where(body.zx-deck) .zx-more{margin-top:34px}
  :where(body.zx-deck) .zx-break-stats{grid-template-columns:1fr;gap:18px;margin-top:36px}
  body.zx:where(.zx-deck) .zx-ticket{flex:0 0 78%}
  :where(body.zx-deck) .zx-tabs-arrow{display:inline-flex}
  body.zx:where(.zx-deck) .zx-tabs-rail{margin-inline:0;width:auto;flex:1 1 auto}
  /* the carousel head STACKS on phones: the title keeps the container's left
     edge (row-reverse pushed it hard against the right one) and the arrow pair
     drops below it on the left, away from the right-edge FAB column that
     eval-3 major #3 photographed it colliding with. */
  :where(body.zx-deck) .zx-car-head{flex-direction:column;align-items:flex-start;gap:20px;margin-bottom:28px}
  :where(body.zx-deck) .zx-car-head > div:first-child{width:100%}
}
@media(max-width:640px){
  body.zx:where(.zx-deck){
    --zx-deck-s1:32px; --zx-deck-s2:64px; --zx-deck-s3:72px; --zx-deck-s4:88px;
    --zx-s1:var(--zx-deck-s1); --zx-s2:var(--zx-deck-s2);
    --zx-s3:var(--zx-deck-s3); --zx-s4:var(--zx-deck-s4);
    --zx-gut:20px;
  }
  :where(body.zx-deck) .zx-h1 .zx-d{font-size:36px;line-height:1.28}
  :where(body.zx-deck) .zx-h2 .zx-d{font-size:30px;line-height:1.3}
  body.type-v2:where(.zx-deck) .zx .zx-h3,:where(body.zx-deck) .zx .zx-h3{font-size:20px}
  body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-lead{font-size:19px;margin-top:18px;max-width:none}
  body.type-v2:where(.zx-deck) .zx .zx-hero p.zx-lead{font-size:18px;margin-top:24px;max-width:none}
  body.type-v2:where(.zx-deck) .zx .zx-hero p.zx-hero-chip{gap:10px;padding:6px 14px 6px 11px}
  :where(body.zx-deck) .zx-chip-n{font-size:23px}
  :where(body.zx-deck) .zx-chip-t{font-size:12.5px}
  :where(body.zx-deck) .zx-hero-copy .zx-bar{width:52px;height:4px;margin-bottom:14px}
  :where(body.zx-deck) .zx-num{font-size:52px}
  :where(body.zx-deck) .zx-price{font-size:52px}
  :where(body.zx-deck) .zx-step-n{font-size:76px}
  :where(body.zx-deck) .zx-break-stat .zx-bn{font-size:36px}
  body.zx:where(.zx-deck) .zx-ticket{flex:0 0 86%}
  body.zx:where(.zx-deck) .zx-carousel{--zx-gap:22px}
  :where(body.zx-deck) .zx-car-head{margin-bottom:24px}
  :where(body.zx-deck) .zx-bar{width:64px;height:5px;margin-bottom:16px}
  :where(body.zx-deck) .zx-faq summary{font-size:17px}
  /* SPECIFICITY, measured (post-rollout sweep item 4). The four ground-colour
     rules below used to sit at (0,4,1) and glow3.css ships
     `.js-line-cta:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab)`
     at (0,5,0) with `color:#fff!important` - so glow3 won and every LINE CTA
     took WHITE regardless of the ground under it. On the dark grounds that is
     harmless (white happens to be right). On `.zx-alt` / `.zx-cream` - the
     ticket stub inside `#svc` on the article pages - it is white text on a
     transparent ghost over cream paper: ratio 1.03, an invisible button
     (ai-citation, ai-marketing, line-oa-guide).
     MEASURED AGAIN after a first attempt, because glow3 turned out NOT to be the
     binding constraint: the kit's OWN top-level fill rule
     `.zx a.zx-btn-fill:not()x4{color:#fff!important}` is (0,6,1) and outranked
     both glow3 and a first (0,5,1) fix, which is why that fix changed nothing.
     Each ground rule below now carries the SAME four-`:not()` guard chain as
     the rule it must beat, plus its ground class - (0,8,1), clear of glow3's
     (0,5,0) and of the kit's own (0,6,1). Self-documenting, and it does not
     depend on `.js-line-cta` being present on the button.
     Gate-1 decision C (Shane, 2026-09-02): Messenger is the PRIMARY mobile tap,
     LINE drops to secondary. 35 line_click produced 0 real chats; the live FB ad
     lands in Messenger with working auto-replies. Order + fill are both swapped,
     and each override carries at least the specificity of the desktop rule it
     replaces (the fill rule is (0,5,0)!important, so the mobile one is (0,7,1)).
     Iteration 4: the pair sits SIDE BY SIDE rather than stacked. That buys back
     62px of hero height, which is what keeps the video panel above the 40%-of-
     fold floor at 390 while both CTAs stay above the fold. */
  :where(body.zx-deck) .zx-ctas{gap:10px;width:100%}
  :where(body.zx-deck) .zx-ctas > .zx-btn{flex:1 1 0}
  :where(body.zx-deck) .zx a.zx-btn{min-height:52px!important;padding:0 14px!important;font-size:16px!important}
  :where(body.zx-deck) .zx-cta-fb{order:-1}
  :where(body.zx-deck) .zx a.zx-cta-fb.zx-btn-ghost:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab){
    background-image:var(--zx-grad-ink)!important;background-color:#1A5CBE!important;
    color:#fff!important;border-color:transparent!important;box-shadow:var(--zx-sh-1)!important;
  }
  :where(body.zx-deck) .zx .zx-grad a.zx-cta-fb.zx-btn-ghost{background-image:none!important;background-color:#fff!important;color:#0B1F2A!important}
  :where(body.zx-deck) .zx a.zx-cta-line.zx-btn-fill:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab){
    background-image:none!important;background-color:transparent!important;
    border:1.5px solid currentColor!important;box-shadow:none!important;
  }
  /* THE DARK GROUNDS ARE DELIBERATELY LEFT AT (0,4,1), i.e. still losing to
     the kit's own (0,6,1) fill rule. Measured: on `.zx-ink` the label that
     actually wins is #fff at 16.89:1 against #0B1F2A, and this rule would
     hand it --zx-on-ink (#F1ECE1) at 14.32:1 - both far past the floor, so
     the bump buys NO accessibility here and costs a visible change on
     index-v2 (measured: 1,067 px, rows 348-399 at 390, the hero CTA glyphs,
     0.0251% of the page). The reported failures were all on the LIGHT
     grounds, where white on cream paper really is invisible, so those are
     the ones raised below. Raising these two is a cosmetic call for the
     owner, not a defect fix - do not do it silently. */
  :where(body.zx-deck) .zx .zx-ink a.zx-cta-line.zx-btn-fill,:where(body.zx-deck) .zx .zx-ink-deep a.zx-cta-line.zx-btn-fill{color:var(--zx-on-ink)!important}
  :where(body.zx-deck) .zx .zx-cream a.zx-cta-line.zx-btn-fill:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab),
  :where(body.zx-deck) .zx .zx-alt a.zx-cta-line.zx-btn-fill:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab){color:var(--zx-ink)!important}
  :where(body.zx-deck) .zx .zx-grad a.zx-cta-line.zx-btn-fill:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab){color:#fff!important}
  /* same ground case, but without requiring `.zx-btn-fill`: at <=640 the LINE
     CTA is always the ghost variant, so a page that wrote `.zx-btn .zx-cta-line`
     without the fill class fell through every ground rule above and inherited a
     dark label onto the gradient. Kept INSIDE this media block on purpose - at
     >=641 the same button is the white-fill variant and its label must stay ink,
     which is what `.zx .zx-grad a.zx-btn-fill` already gives it. */
  :where(body.zx-deck) .zx .zx-grad a.zx-cta-line:not(.btn-sec):not(.link-pill):not(.btn-check):not(.line-fab){color:#fff!important}
  :where(body.zx-deck) .zx-ab{padding:26px 20px 30px}
  :where(body.zx-deck) .zx-vs-panel{padding:26px 20px 28px}
  :where(body.zx-deck) .zx-marquee img{height:34px;width:34px}
  :where(body.zx-deck) .zx-marquee-track{gap:26px}
  /* the counterfoil shrinks with the type so the hero kicker row still fits
     one line at 390 (measured: 44 + 8 + chip 262 = 314 inside a 350 column). */
  :where(body.zx-deck) .zx-stub{min-width:44px;height:26px;padding:0 10px;font-size:13px;margin-bottom:14px}
  :where(body.zx-deck) .zx-kicker-row{gap:8px}
  :where(body.zx-deck) .zx-kicker-row .zx-hero-chip{min-width:0}
  body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-quote{font-size:18px;max-width:none}
  :where(body.zx-deck) .zx-tornstub .zx-tk-stub{padding:16px 18px 20px}
}

/* ========================================================================
   ITERATION 7 — three targeted fixes. Everything here is page-local and
   sits at the END of this stylesheet on purpose: the kit is frozen
   (zx-system.css md5 7d6b23714bf0fe0553cb787e59d54326, re-verified 2026-09-02) and every rule below either
   out-specifies or out-orders the declaration it replaces, so not one
   gate-checked class is restyled and no !important is spent except on the
   inline `transform` zx-carousel.js writes onto the track.
   ======================================================================== */
body.zx:where(.zx-deck){--zx-paper:#FFFCF3}

/* ------------------------------------------------------------------------
   7A — THE RAIL YIELDS TO PHOTOGRAPHS  (eval-6 Major #1)

   Measured defect: 380px into #svc at 1440 the sticky tab pill sat on the
   top ~15% of two ticket photos, and five persistent chrome elements shared
   one screen on the slide that is supposed to prove "one accent at a time".
   The fix is not to restyle .zx-tabs (a gate-locked kit component) but to
   give the media zone right of way: any [data-rail-yield] block crossing
   the pill's own horizontal band fades the pill to 0 and takes its pointer
   events with it. Leaving the band restores it.

   Three details that are not optional:
   - :focus-within always beats the yield, so a keyboard user can never tab
     into an invisible, non-clickable rail;
   - pointer-events move on .zx-tabs-outer, because .zx-tabs itself is
     already pointer-events:none and .zx-tabs-outer is what re-enables them;
   - :focus-within always beats the yield, so a keyboard user can never tab
     into an invisible, non-clickable rail.

   ITERATION 9 — eval-8 Major #1. The opacity is no longer a class-driven
   0/1 with a .26s CSS transition. It is a CONTINUOUS function of how deeply
   the photograph has actually entered the pill's own rendered box, written
   by a rAF-throttled scroll handler as --zx-rail-op on .zx-tabs itself.
   Two reasons, both measured:

   - a time-based transition made the pill's opacity depend on WHEN the last
     class flip happened, so an isolated scrollTo and a continuous sweep read
     different values at the same scroll position (eval-8 §3, Round 3);
   - a binary trigger cannot satisfy both halves of the brief at once. Window
     = photoH + pillH − (d05 + d95); with a 330px photo, a 62px pill and a
     370px budget the two guards must sum to ≥22px, so a binary threshold of
     ≤10px (the touch bar) is arithmetically impossible. A linear ramp splits
     them: d95 = 0.05·FADE (the pill is still at full strength) and
     d05 = 0.95·FADE (it is gone). FADE = 36px puts d95 at 1.8px and d05 at
     34.2px: an analytic window of 356px against a 370px budget and a touch at
     full strength of 1.8px against a 10px bar. Swept figures: it9-rail.json.

   Reduced motion keeps the same ramp and drops nothing, because there is no
   time-based interpolation left to drop: the value is a pure function of
   scroll position, so it is already instant. The transition is GONE, not
   merely disabled, or it would lag the per-frame writes.
   ------------------------------------------------------------------------ */
body.zx:where(.zx-deck) .zx-tabs{opacity:var(--zx-rail-op,1)}
body.zx:where(.zx-deck).zx-rail-yield .zx-tabs .zx-tabs-outer{pointer-events:none}
body.zx:where(.zx-deck) .zx-tabs:focus-within{opacity:1}
body.zx:where(.zx-deck).zx-rail-yield .zx-tabs:focus-within .zx-tabs-outer{pointer-events:auto}

/* ------------------------------------------------------------------------
   7A-2 — THE RAIL MUST COME BACK AT A POSITION THE READER CAN REACH
   (eval-8 Major #1, the half that was not a trigger bug)

   eval-8 measured the yield window at 600px against a 370px budget and could
   not find the restore edge. It is NOT the observer, and it was never a
   tuning error. `#svc` is over 1,600px tall against an 821px snapport, so
   under `scroll-snap-type:y proximity` the scroll positions inside it are the
   ones where the snapport is CONTAINED in the slide's snap area — 1764→2579
   at 1440. Ask for 2580–2849 and Chrome hands back 2579 every time: that is its
   proximity range (~30% of the snapport) pulling the scroll to the nearest
   valid position, and those 270px are not resting positions at all. The
   rail's restore point sat inside that band, so every sweep — eval-8's two
   and it8-rail.py's own, which is why its samplesRaw shows scrollY 2579
   repeated 27 times — read `hidden` at 2579, then `visible` at the next
   reachable sample 274px later, and called the gap the window. it8 then
   published the LAST HIDDEN SAMPLE (2579) as the edge, which is where 336px
   came from. Both numbers were measuring the snap band, not the trigger.

   Two things were tried before this one and are recorded so nobody re-tries
   them: `scroll-margin-bottom` on the slide does nothing (Chrome computes the
   oversized-area containment from the border box — 1000px of it moved the
   pin by 0), and `scroll-padding-bottom` on the root works but shrinks the
   snapport for all ten slides, turning every 821px slide into an oversized
   one and giving the whole deck a loose tolerance band it does not want.

   Dropping the snap alignment on this ONE slide removes the band outright.
   What is lost is the pull-into-place at `#svc`'s own top edge; what is
   bought is that a reader can rest anywhere in the 2 screens of rack, and
   that the rail comes back at a position they can actually stop at. The
   other nine slides snap exactly as they did — it9-deck.json checks that.
   ------------------------------------------------------------------------ */
:where(body.zx-deck) .zx .zx-sec.zx-slide.zx-slide--nosnap{scroll-snap-align:none}

/* ------------------------------------------------------------------------
   7B — ONE CONTROL LANGUAGE PER SLIDE  (eval-6 Major #2)

   Measured defect: two near-identical white circular chevron pairs 247px
   apart on #svc at 390 — .zx-tabs-prev/next (44x44, pages the tab rail) and
   .zx-arrow.zx-prev/next (52x52, pages the card strip). Same shape, same
   fill, two unrelated jobs.

   The two controls are not peers, so they stop looking like peers:
   - the rail's pager is SECONDARY in-page navigation. It loses the disc
     entirely and becomes a flat ghost chevron — still 44x44 of tap area,
     no fill, no shadow, no circle.
   - the strip's pager is the PRIMARY content control on the slide, so it
     keeps weight but changes species: a joined pair of ticket-edge tabs on
     ticket paper, square-cornered at the 4px tier, split by the same dashed
     perforation the tickets use. Nothing round is left on #svc at any width.
   No new radius value enters the census: 999px -> 4px, both already in it.
   ------------------------------------------------------------------------ */
:where(body.zx-deck) .zx-tabs-arrow{
  background:transparent;box-shadow:none;color:var(--zx-ink);opacity:.7;
  border-radius:var(--zx-r-3);   /* the disc is the whole defect: 999px -> 4px */
}
:where(body.zx-deck) .zx-tabs-arrow:hover{opacity:1;background:rgba(11,31,42,.06)}
:where(body.zx-deck) .zx-car-nav{gap:0}
body.zx:where(.zx-deck) .zx-car-nav .zx-arrow{
  width:58px;height:46px;border-radius:var(--zx-r-3);
  background:var(--zx-paper);box-shadow:none;
}
body.zx:where(.zx-deck) .zx-car-nav .zx-arrow.zx-prev{border-radius:var(--zx-r-3) 0 0 var(--zx-r-3);border-right-width:0}
body.zx:where(.zx-deck) .zx-car-nav .zx-arrow.zx-next{border-radius:0 var(--zx-r-3) var(--zx-r-3) 0;border-left-style:dashed}

/* ------------------------------------------------------------------------
   7C — #svc IS A RACK, NOT A ROW OF EQUAL BOXES  (eval-6 Critical #3)

   eval-6: "#svc's fix was surface-only (same five equal cards, new dashed
   line above the price)... a hostile juror will register 'pricing cards'."
   Correct, and the reason is structural: every card had the same flex-basis,
   so the ticket dressing sat ON a grid instead of replacing it.

   The slide is now one rack. The 4,900 package ticket leaves the carousel
   entirely and becomes the object the slide is about — 1.6fr of a 1.6/1
   grid, a 2:1 bold crop instead of 16:9, and the price set at 96px, the
   largest numeral anywhere on the page including the h1. The other four
   tickets stay in the carousel but shrink to stubs and fan down the right
   column: horizontal layout, square 84px thumb, 32px price, overlapped
   -12px with a descending z-index and an alternating sub-degree tilt so
   they read as a stack of slips on a spike rather than four more cards.

   The tilt uses the independent `rotate:` property, NOT `transform:` — the
   reveal already owns `transform` (translateY(26px) rotate(-1.1deg) settling
   to none) and a transform here would silently kill the deal-in animation
   the way iteration 4's card hover was killed. `rotate` composes with it.

   No word is deleted, no price changes, the ladder is untouched: the hero
   still prints the same figures it printed in iteration 6, and so do the
   four stubs.
   ------------------------------------------------------------------------ */
:where(body.zx-deck) .zx-rack{position:relative}
:where(body.zx-deck) .zx-car-head{margin-bottom:38px}
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket{position:relative}
/* ITERATION 8. The stack order is ASCENDING: each slip is laid ON TOP of
   the one before it. Two reasons, both load-bearing:
   - a real fan of stubs pulled off a roll stacks that way;
   - the torn bottom edge (7E) is a ground-coloured clip-path strip, so a
     counterfoil that sat OVER its neighbour would print grey notches onto
     that neighbour's face. Under it, every tooth is either over real ground
     or hidden by the slip on top, which is what a tear actually looks like. */
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(1){z-index:1}
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(2){z-index:2}
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(3){z-index:3}
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(4){z-index:4}

@media(min-width:901px){
  :where(body.zx-deck) .zx-rack{
    display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);
    gap:clamp(26px,3vw,48px);align-items:center;
  }
  /* the hero ticket */
  body.zx:where(.zx-deck) .zx-tk-hero{flex:0 0 auto;width:100%}
  :where(body.zx-deck) .zx-tk-hero .zx-card-cover{margin:16px 16px 0;aspect-ratio:21/9}
  :where(body.zx-deck) .zx-tk-hero .zx-card-body{padding:24px 32px 22px;gap:10px}
  :where(body.zx-deck) .zx-tk-hero .zx-perf{margin:0 22px}
  :where(body.zx-deck) .zx-tk-hero .zx-tk-stub{padding:24px 32px 34px}
  :where(body.zx-deck) .zx-tk-hero .zx-price{font-size:96px}
  :where(body.zx-deck) .zx-tk-hero .zx-price-u{font-size:16px}
  :where(body.zx-deck) .zx-tk-hero .zx-card-tag{font-size:13px}
  body.type-v2:where(.zx-deck) .zx .zx-sec .zx-tk-hero p.zx-card-l{font-size:18px;line-height:1.66}
  body.type-v2:where(.zx-deck) .zx .zx-sec .zx-tk-hero p.zx-card-sub{font-size:16px;margin-top:8px}

  /* the fanned stack. The viewport stops clipping and stops being a
     carousel: the track turns into a column, the JS transform is neutralised
     (it is written inline, so this is the one !important on the page that is
     load-bearing rather than defensive), and the pager is removed entirely —
     all four stubs are visible at once, so a control for them would be the
     sixth piece of chrome on the slide eval-6 already called the busiest. */
  body.zx:where(.zx-deck) .zx-rack-strip .zx-carousel-viewport{overflow:visible;cursor:default;padding:6px 8px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-carousel-track{
    flex-direction:column;gap:0;transform:none!important;transition:none;will-change:auto;
  }
  body.zx:where(.zx-deck) .zx-rack-strip .zx-car-nav{display:none}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket{flex:0 0 auto;width:100%;transform-origin:0% 50%}
  /* ITERATION 8 -- eval-7 Major #1: "the claimed -1.15deg/+0.95deg rotation
     and -16px overlap are real CSS values but at the magnitude shipped they
     are imperceptible in an actual rendered frame". Correct. The amplitude
     goes up ~3.7x and every slip gets a real drop shadow, so the fan reads
     as paper at a 3-second glance instead of as a tidy list.
     A rotated counterfoil's top edge falls by width*sin(theta); at the
     measured 491px counterfoil that is 31-38px, well over the 25px bar.
     Still `rotate:` and never `transform:` -- the reveal owns transform. */
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket + .zx-ticket{margin-top:-26px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(1){rotate:-4.2deg;translate:-10px 0}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(2){rotate:3.6deg;translate:6px 0}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(3){rotate:-3.8deg;translate:-4px 0}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(4){rotate:4.4deg;translate:10px 0}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:hover,body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:focus-within{rotate:0deg;translate:0 0;z-index:6}

  /* stub geometry: photo left, copy right, price on the counterfoil */
  body.zx:where(.zx-deck) .zx-rack-strip .zx-tk-top{flex-direction:column;gap:0}
  /* THE COUNTERFOIL CARRIES NO PHOTOGRAPH ON DESKTOP.
     shane_check SC06 puts a hard 260px floor under every content photograph
     rendered at 1440 -- Shane's own "really small, make it bigger" encoded --
     and the fan's thumbnail was 84px. 260px inside a 487px counterfoil leaves
     186px for Thai body copy, which is a gutter, not a column. So the stub
     drops the picture and keeps every word: a real ticket stub has a class
     line, a title, a price, a barcode and a torn edge, and that is precisely
     what is left. The slide is then honestly single-accent -- ONE photograph,
     the hero's 804px 21:9 crop, instead of one photograph plus four thumbnails
     arguing with it.
     At <=900 the same image renders at ~265px in a full-width drag card and is
     a photograph again, so it is shown wherever it can be shown properly. */
  body.zx:where(.zx-deck) .zx-rack-strip .zx-card-cover{display:none}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-card-body{flex:1 1 auto;min-width:0}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-card-body{padding:18px 22px 15px;gap:8px}
  /* the 16:9 cover's scrim exists to keep type legible over a big crop.
     Nothing is set over an 84px thumb, so it only greyed out the bottom
     half of the square and made the photo read as broken. */
  body.zx:where(.zx-deck) .zx-rack-strip .zx-card-cover::after{display:none}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-card-tag{font-size:11px;padding-bottom:4px;border-bottom-width:1.5px}
  body.type-v2:where(.zx-deck) .zx .zx-sec .zx-rack-strip .zx-h3{font-size:18px}
  body.type-v2:where(.zx-deck) .zx .zx-sec .zx-rack-strip p.zx-card-l{font-size:14px;line-height:1.55}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-perf{margin:0 18px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-tk-stub{padding:10px 22px 42px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-price{font-size:32px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-price-u{font-size:12px}
  body.type-v2:where(.zx-deck) .zx .zx-sec .zx-rack-strip p.zx-card-sub{font-size:12.5px;margin-top:3px}
}

/* mobile: the hero ticket goes full width and the four stubs become a
   horizontal strip UNDER it — the same rack, stacked. */
@media(max-width:900px){
  :where(body.zx-deck) .zx-rack{display:block}
  body.zx:where(.zx-deck) .zx-tk-hero{width:100%}
  :where(body.zx-deck) .zx-tk-hero .zx-price{font-size:64px}
  :where(body.zx-deck) .zx-rack-strip{margin-top:26px}
  body.zx:where(.zx-deck) .zx-rack-strip{--zx-gap:16px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket{flex:0 0 68%}
  body.zx:where(.zx-deck) .zx-rack-strip{--zx-gap:0px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-carousel-viewport{padding:24px 0}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket + .zx-ticket{margin-left:-26px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(odd){rotate:-4.4deg}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:nth-child(even){rotate:3.8deg}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:hover,body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:focus-within{rotate:0deg}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-price{font-size:40px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-car-nav{display:flex;justify-content:flex-start;margin-top:20px}
}
@media(max-width:640px){
  :where(body.zx-deck) .zx-tk-hero .zx-price{font-size:58px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket{flex:0 0 78%}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-price{font-size:38px}
}

/* ------------------------------------------------------------------------
   7D — THE STUB READS AS A TICKET IN ONE GLANCE  (eval-6 Major #4)

   eval-6: the numbered counterfoil "out of context reads as a conventional
   progress/step badge", and the metaphor "depends on close reading, not a
   3-second first impression". No new word is allowed to fix that, so the
   fix is entirely silhouette and surface:

   1. PAPER. The counterfoil is a different stock from the ticket body —
      #FFFCF3 against the ticket's #FAF6EC against the slide's #F2F0E7.
      Three warm greys in a row is what a real two-part ticket looks like,
      and it raises the dim-ink contrast rather than lowering it.
   2. SCALLOP. A repeating radial-gradient painted in the SECTION's own
      ground bites a row of half-discs out of the stub's bottom edge. Painted
      in the ground, not in a tint, so they read as holes punched through the
      paper. This is the shape a torn ticket actually has, and it is legible
      at thumbnail size — which is exactly what "3 seconds" means.
   3. BARCODE. Pure CSS repeating-linear-gradient stripes, irregular widths
      (2/3/1px on a 4/10/15px cadence) so it scans as a printed code and not
      as a hatch pattern. Decorative by construction: it is a pseudo-element,
      it carries no text, and the price row and sub-line both reserve the
      column it sits in so nothing is ever overprinted.
   ------------------------------------------------------------------------ */
body.zx:where(.zx-deck) .zx-ticket .zx-tk-stub{
  position:relative;background:var(--zx-paper);
  border-radius:0 0 var(--zx-r-1) var(--zx-r-1);
}
/* ITERATION 8 -- eval-7 Major #1, second half: "replace the 9px radial-
   gradient scallop with an actual jagged clip-path torn edge". Done, and it
   is a real clip-path, not a gradient that looks like one: a 13px strip
   pinned across the whole bottom edge, filled with the SECTION's own ground
   colour and cut by a 32-vertex irregular sawtooth, so the teeth are holes
   torn through the paper rather than dots printed on it. Depth runs 4%..96%
   of the strip and the pitch is jittered, which is what stops it reading as
   a chevron border.
   The ticket's bottom corners lose their radius (20px 20px 0 0) because a
   torn edge cannot also be a rounded one; the radius census keeps the same
   two values it already had, 20 and 0. */
body.zx:where(.zx-deck) .zx-ticket{border-radius:var(--zx-r-1) var(--zx-r-1) 0 0}
body.zx:where(.zx-deck) .zx-ticket .zx-tk-stub{border-radius:0}
body.zx:where(.zx-deck) .zx-ticket .zx-tk-stub::before{
  content:"";position:absolute;left:-1px;right:-1px;bottom:-1px;height:13px;
  background:var(--zx-ground,var(--zx-alt));
  clip-path:polygon(0% 88%,3.4% 12%,6.9% 96%,10.1% 20%,13.8% 84%,17.2% 8%,20.4% 92%,24.1% 24%,27.3% 80%,31% 10%,34.5% 94%,37.9% 18%,41.4% 86%,44.8% 6%,48.3% 90%,51.7% 22%,55.2% 82%,58.6% 14%,62.1% 96%,65.5% 26%,69% 78%,72.4% 10%,75.9% 92%,79.3% 16%,82.8% 88%,86.2% 4%,89.7% 94%,93.1% 20%,96.6% 84%,100% 10%,100% 100%,0% 100%);
  pointer-events:none;
}
body.zx:where(.zx-deck) .zx-ticket .zx-tk-stub::after{
  content:"";position:absolute;pointer-events:none;
  right:22px;top:20px;width:64px;height:28px;opacity:.5;
  background-image:repeating-linear-gradient(90deg,
    var(--zx-ink) 0 2px,transparent 2px 4px,
    var(--zx-ink) 4px 7px,transparent 7px 10px,
    var(--zx-ink) 10px 11px,transparent 11px 15px);
}
body.zx:where(.zx-deck) .zx-ticket .zx-card-price,body.zx:where(.zx-deck) .zx-ticket .zx-tk-stub .zx-card-sub{padding-right:76px}
@media(min-width:901px){
  :where(body.zx-deck) .zx-tk-hero .zx-tk-stub::after{right:34px;top:34px;width:104px;height:46px}
  body.zx:where(.zx-deck) .zx-tk-hero .zx-card-price,body.zx:where(.zx-deck) .zx-tk-hero .zx-tk-stub .zx-card-sub{padding-right:150px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-tk-stub::after{right:16px;top:12px;width:46px;height:22px}
  body.zx:where(.zx-deck) .zx-rack-strip .zx-card-price,body.zx:where(.zx-deck) .zx-rack-strip .zx-tk-stub .zx-card-sub{padding-right:62px}
}

/* ------------------------------------------------------------------------
   8B — THE NINE SLIDE TEARS GET THE SAME REAL SAWTOOTH

   The boundary between two slides was a 2px dashed rule with a 22px disc
   punched out of each end. The dashes stay (they are the perforation) and
   the discs stay (they are the die-cut), but the edge above them is now the
   same clip-path sawtooth the ticket stubs use, painted in the PREVIOUS
   slide's ground so the previous paper genuinely tears into this one.
   Carried on the SECTION's ::before rather than on .zx-tear, because
   .zx-tear's own ::before/::after are the two notch discs and a clip-path
   on .zx-tear would have cut them off — the discs are not negotiable.
   ------------------------------------------------------------------------ */
:where(body.zx-deck) .zx-sec[data-prev-ground]::before{
  content:"";position:absolute;left:0;right:0;top:0;height:9px;z-index:5;
  pointer-events:none;background:var(--zx-prev,transparent);
  clip-path:polygon(0% 0%,100% 0%,100% 10%,96.6% 84%,93.1% 20%,89.7% 94%,86.2% 4%,82.8% 88%,79.3% 16%,75.9% 92%,72.4% 10%,69% 78%,65.5% 26%,62.1% 96%,58.6% 14%,55.2% 82%,51.7% 22%,48.3% 90%,44.8% 6%,41.4% 86%,37.9% 18%,34.5% 94%,31% 10%,27.3% 80%,24.1% 24%,20.4% 92%,17.2% 8%,13.8% 84%,10.1% 20%,6.9% 96%,3.4% 12%,0% 88%);
}
/* (the second, duplicate copy of the nine --zx-prev declarations that used to
   sit here is gone — see the data-prev-ground block above.) */

/* ------------------------------------------------------------------------
   8C — THE FAN CASTS A SHADOW

   eval-7: the stack "reads as a hero card + a list" because nothing in the
   frame says the slips are separate pieces of paper lying on each other.
   Shadows say it. Both composites are built ONLY from the kit's own
   --zx-sh-1/-2/-3 tiers (layered, never redefined), so the shadow census
   gains no new value:
     --zx-sh-foil  = sh-1 x2 + sh-2      (a slip lying on the slip below it)
     --zx-sh-tk    = sh-1 x3 + sh-2 + sh-3 (the hero ticket, off the rack)
   ------------------------------------------------------------------------ */
body.zx:where(.zx-deck){
  --zx-sh-foil:var(--zx-sh-1),var(--zx-sh-1),var(--zx-sh-2);
  --zx-sh-tk:var(--zx-sh-1),var(--zx-sh-1),var(--zx-sh-1),var(--zx-sh-2),var(--zx-sh-3);
}
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket{box-shadow:var(--zx-sh-foil)}
body.zx:where(.zx-deck) .zx-tk-hero{box-shadow:var(--zx-sh-tk)}
body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:hover,body.zx:where(.zx-deck) .zx-rack-strip .zx-ticket:focus-within{box-shadow:var(--zx-sh-tk)}
body.zx:where(.zx-deck) .zx-tk-hero:hover,body.zx:where(.zx-deck) .zx-tk-hero:focus-within{box-shadow:var(--zx-sh-tk),var(--zx-sh-1)}
/* ============================================================================
   ATMOSPHERE — the paper grain. Lifted from index-v2's <style id="editorial-v2">
   where it was an UNSCOPED `body::after` and would have hit every page that
   linked this file. Fixed full-viewport SVG noise at opacity .3,
   pointer-events:none, z-index 3 (below header z-100, the progress bar z-101,
   the FABs z-120 and the dots rail z-18 — see the stacking note in the header).
   ============================================================================ */
/* MEDIUM #6. Two changes. (a) `:where()` puts this back at (0,0,1), the exact
   weight it carried as the page's own unscoped `body::after`, so it cannot
   silently out-rank an adopting page that wants its own body pseudo-element.
   (b) an escape hatch: a long-copy or print-oriented page adds `zx-nograin` to
   <body> and keeps every other deck pixel. `content:none` removes the box
   outright rather than making it transparent, so nothing is composited. */
:where(body.zx-deck)::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:3;opacity:.3;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0.13 0 0 0 0 0.19 0 0 0 0 0.29 0 0 0 0.04 0'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E")}
:where(body.zx-deck.zx-nograin)::after{content:none}


/* ========================================================================
   OWNER THEME 2026-09-03 (s194) — "V3 Deep luxury tone, more shades, and
   position everything from the middle" (Shane, palette sheet pick).

   GROUND RAMP. Every band is dark; four shades of one navy family so the
   deck reads as depth steps instead of "navy hero + cream body":
     hero / .zx-ink-deep   #061520   (deepest — V3's hero)
     .zx-ink               #0B1F2A   (brand ink)
     .zx-cream bands       #0F2433   (V3's mid)
     .zx-alt bands         #15303F   (new, lightest step)
   Soft sky/mint radial glows sit in the corners of every band (the "many
   shades"), paper (#FBFBF9) survives only inside cards, tickets, the answer
   block, comparison panels and the read-more boxes. CTA gradient untouched.
   The two paper tokens are re-pointed so the few components that still read
   --zx-cream/--zx-alt (tickets, muted panels) get warm off-white, not cream.

   CENTRE. Section content is centred: headings, leads, bodies, CTAs, stubs,
   bars, steps, stats. Reading components keep left alignment inside their
   own box (lists, tables, FAQ/read-more details, comparison panels, answer
   block, cards, tickets) — centred bullets are unreadable; the BOX is
   centred, the words inside it are not.

   Written at the kit's own specificity and appended LAST so source order
   wins over the ground/text rules above; the ids #faq/#answer are NOT
   exempted (V3 kept them dark and that is the tone Shane picked).
   ======================================================================== */
body.zx:where(.zx-deck){
  --zx-cream:#FBFBF9;
  --zx-alt:#F1F1EC;
  --zx-on-ink:#F1ECE1;
  --zx-on-ink-dim:#A9BCC8;
  --zx-band-deep:#061520;
  --zx-band-ink:#0B1F2A;
  --zx-band-mid:#0F2433;
  --zx-band-high:#15303F;
  --zx-eyebrow-ink:#8FE3C4;
  --zx-glow-a:rgba(62,142,247,.16);
  --zx-glow-b:rgba(24,184,122,.13);
  --zx-grad-dark:linear-gradient(to right,#6FB0FF,#2ED18F);
}
/* -- grounds ------------------------------------------------------------- */
:where(body.zx-deck) .zx-sec.zx-hero,:where(body.zx-deck) .zx-sec.zx-ink-deep{background:var(--zx-band-deep);color:var(--zx-on-ink)}
:where(body.zx-deck) .zx-sec.zx-ink{background:var(--zx-band-ink);color:var(--zx-on-ink)}
:where(body.zx-deck) .zx-sec.zx-cream{background:var(--zx-band-mid);color:var(--zx-on-ink)}
:where(body.zx-deck) .zx-sec.zx-alt{background:var(--zx-band-high);color:var(--zx-on-ink)}
:where(body.zx-deck) .zx .zx-archive{background:var(--zx-band-mid);color:var(--zx-on-ink);border-top-color:var(--zx-rule-ink)}
:where(body.zx-deck) .zx-archive .zx-body,:where(body.zx-deck) .zx-archive .zx-small{color:var(--zx-on-ink-dim)}
:where(body.zx-deck) .zx-sec:is(.zx-cream,.zx-alt,.zx-ink,.zx-ink-deep)::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(70% 55% at 12% 0%,var(--zx-glow-a) 0%,transparent 62%),
             radial-gradient(55% 45% at 92% 100%,var(--zx-glow-b) 0%,transparent 60%);
}
:where(body.zx-deck) .zx-sec.zx-hero::before,:where(body.zx-deck) .zx-sec.zx-ink-deep::before{
  background:radial-gradient(60% 50% at 80% 8%,var(--zx-glow-a) 0%,transparent 60%);
}
/* -- text on the now-dark cream/alt bands (mirrors the .zx-ink rules) ----- */
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-eyebrow,:where(body.zx-deck) :is(.zx-ink,.zx-ink-deep) .zx-eyebrow{color:var(--zx-eyebrow-ink)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-body,
body.type-v2:where(.zx-deck) .zx .zx-sec:is(.zx-cream,.zx-alt) p.zx-lead,
body.type-v2:where(.zx-deck) .zx .zx-sec:is(.zx-cream,.zx-alt) p.zx-body,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-small,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-step-l,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-stat-lb,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-proofline,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-wall-h{color:var(--zx-on-ink-dim)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-proofline b,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-step h3,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) a.zx-btn-ghost{color:var(--zx-on-ink)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt) a.zx-btn-ghost{border-color:var(--zx-rule-ink)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-step{border-top-color:var(--zx-rule-ink)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-step-n{-webkit-text-stroke-color:rgba(241,236,225,.55)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt,.zx-ink,.zx-ink-deep) .zx-num,
:where(body.zx-deck) :is(.zx-cream,.zx-alt) .zx-step:first-child .zx-step-n{background-image:var(--zx-grad-dark)}
/* paper components own their colour again inside dark bands */
:where(body.zx-deck) :is(.zx-cream,.zx-alt,.zx-ink,.zx-ink-deep,.zx-archive) :is(.zx-card,.zx-ticket,.zx-ab,.zx-vs-panel:not(.zx-us),.dx){color:var(--zx-ink)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt,.zx-ink,.zx-ink-deep,.zx-archive) :is(.zx-card,.zx-ticket,.zx-ab,.zx-vs-panel:not(.zx-us):not(.zx-muted),.dx){background:var(--zx-cream)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt,.zx-ink,.zx-ink-deep,.zx-archive) :is(.zx-card,.zx-ticket,.zx-ab,.zx-vs-panel:not(.zx-us),.dx) :is(.zx-body,p,li,.zx-small){color:var(--zx-dim)}
:where(body.zx-deck) :is(.zx-cream,.zx-alt,.zx-ink,.zx-ink-deep,.zx-archive) :is(.zx-card,.zx-ticket,.zx-ab,.zx-vs-panel:not(.zx-us),.dx) :is(h3,b,strong,summary){color:var(--zx-ink)}
/* -- centre everything ----------------------------------------------------- */
:where(body.zx-deck) .zx-sec .zx-wrap{text-align:center}
:where(body.zx-deck) .zx-ctas{justify-content:center}
:where(body.zx-deck) .zx-bar,:where(body.zx-deck) .zx-stub{margin-inline:auto}
body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-lead,body.type-v2:where(.zx-deck) .zx .zx-sec p.zx-body{margin-inline:auto}
:where(body.zx-deck) .zx-step{align-items:center;text-align:center}
:where(body.zx-deck) .zx-step::before{left:31%}
:where(body.zx-deck) .zx-car-head{justify-content:center;text-align:center}
:where(body.zx-deck) .zx-car-head > div:first-child{width:100%}
:where(body.zx-deck) .zx-break-stats{text-align:center}
:where(body.zx-deck) .zx-proof2{text-align:center}
:where(body.zx-deck) .zx-proof2 .zx-stat{justify-content:center}
:where(body.zx-deck) .zx-vs-panel,:where(body.zx-deck) .dx,:where(body.zx-deck) .zx-ab,:where(body.zx-deck) .zx-card,:where(body.zx-deck) .zx-ticket,
:where(body.zx-deck) .zx-wrap ul,:where(body.zx-deck) .zx-wrap ol,:where(body.zx-deck) .zx-wrap table,:where(body.zx-deck) .see-row,:where(body.zx-deck) .zx-wrap .answer-box,:where(body.zx-deck) .zx-wrap .faq-item{text-align:left}
:where(body.zx-deck) .zx-wrap .answer-box,:where(body.zx-deck) .zx-wrap .faq-item,:where(body.zx-deck) .zx-wrap table{margin-inline:auto}

/* -- theme pass 2: legacy page-local components on the dark ramp ----------
   Sweep of all 26 kit pages (ops/loop-runs/sweep-s194-theme.json): two
   failure families. (1) Text that inherits the band's light colour while
   sitting inside a page-authored PAPER box (.faq-item, .blk, .lit cards, .dv
   tiles, tables ...). (2) Page-local text that kept its old ink/dim colour
   on a band that is now dark. Rule order: dark-band text first (0,4,1),
   paper boxes re-pin to ink after it at (0,5,1), so the box always wins. */
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) :is(p,li,span,td,th,dd,dt,small,label,figcaption,cite,time,div,.updated,.trustline,.hint,.byline,.photo-cap,.srcline,.meta,.price-note,.sect-sub,.price-tag){color:var(--zx-on-ink-dim)!important}
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) :is(h2,h3,h4,b,strong,summary,.zx-stub-n){color:var(--zx-on-ink)!important}
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) :is(a:not([class*="btn"]),.see-num,.km-q,.art-lead,.dv-tile-num){color:var(--zx-eyebrow-ink)!important}
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) .zx-eyebrow{color:var(--zx-eyebrow-ink)!important}
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) :is(.cross,.them,th.them,td.cross){color:#F28B7D!important}
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) .zx-step-l{color:var(--zx-on-ink-dim)!important}
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-sec.zx-grad,.zx-archive) .zx-num{background-image:var(--zx-grad)}
body.zx.zx-deck .zx :is(.dv-cap,.dv-cap *){color:#F1ECE1!important}
/* dataviz figures that switched their own paper off inline get it back */
body.zx.zx-deck .zx .dv.see-fig[style]{background:var(--zx-cream)!important;padding:14px;border-radius:var(--zx-r-2)}
body.zx.zx-deck .zx :is(.faq-item,.blk,.boxed,.svc-card,.stepcard,.dx,.cmpbox,.cmpgrid,.step,.phase-item,.phase-row,.vs-col,.skubox,.mech-card,.art-phase-item,.dv,.dv-tile,.do-item,.hire-banner,.zx-tornstub,.zx-ab,.zx-card,.zx-ticket,.zx-vs-panel:not(.zx-us),.reason,.see-get,.answer-block,.answer-box,.stat-callout,.transp-item,.clients-wall,.gm-hero-pitch,.proof-link,.mt-stat,.proofgrid,.tmile.lit,.caveat,.link-band,.final-band,.tq,table,.card,.diy,.km-card,.pc,.upsell,.guide-box,.note,.callout,.quote-card,.stat-card,.price-card,.tier,.plan,.pkg,.box,details,.zx-tile,.tactics-list,.hero-price,.self-check__num,.km-svc-tag,.chat-header,.label,.tier-item,.inc,a.see-fig,.client-chips > *,.ku-trust > li,.chip,.tag,.badge,.pill) :is(a:not([class]),a.inl,.see-num,.km-q,.art-lead,.tick,.dv-tile-num){color:var(--zx-mint-ink)!important}
body.zx.zx-deck .zx :is(.faq-item,.blk,.boxed,.svc-card,.stepcard,.dx,.cmpbox,.cmpgrid,.step,.phase-item,.phase-row,.vs-col,.skubox,.mech-card,.art-phase-item,.dv,.dv-tile,.do-item,.hire-banner,.zx-tornstub,.zx-ab,.zx-card,.zx-ticket,.zx-vs-panel:not(.zx-us),.reason,.see-get,.answer-block,.answer-box,.stat-callout,.transp-item,.clients-wall,.gm-hero-pitch,.proof-link,.mt-stat,.proofgrid,.tmile.lit,.caveat,.link-band,.final-band,.tq,table,.card,.diy,.km-card,.pc,.upsell,.guide-box,.note,.callout,.quote-card,.stat-card,.price-card,.tier,.plan,.pkg,.box,details,.zx-tile,.tactics-list,.hero-price,.self-check__num,.km-svc-tag,.chat-header,.label,.tier-item,.inc,a.see-fig,.client-chips > *,.ku-trust > li,.chip,.tag,.badge,.pill) .zx-eyebrow{color:var(--zx-mint-ink)!important}
body.zx.zx-deck .zx :is(.dv,.dv-tile,.cmpbox,.vs-col) .dv-cap :is(b,span){color:#F1ECE1!important}
body.zx.zx-deck .zx :is(.zx-vs-panel.zx-us,.zx-vs-panel.zx-us *){color:#fff}

/* legacy article hero (case-mdcc only): joins the ramp at the deepest shade */
body.zx.zx-deck .page-hero.hero-band{background:var(--zx-band-deep);color:var(--zx-on-ink);--muted:var(--zx-on-ink-dim);--mint:#8FE3C4;text-align:center}
body.zx.zx-deck .page-hero.hero-band .hl-grow{background-image:linear-gradient(to right,rgba(24,184,122,.28),rgba(62,142,247,.28))}
body.zx.zx-deck .page-hero.hero-band :is(.byline,.wrap){text-align:center;margin-inline:auto!important;width:100%}
body.zx.zx-deck .page-hero.hero-band .byline{display:block;max-width:none}
body.zx.zx-deck .page-hero.hero-band :is(h1,.h1){color:var(--zx-on-ink)!important}
body.zx.zx-deck .page-hero.hero-band :is(p,.byline,.photo-cap,.srcline,span:not([class*="btn"]):not(.ps)){color:var(--zx-on-ink-dim)!important}
body.zx.zx-deck .page-hero.hero-band .ps{background:transparent;color:var(--zx-eyebrow-ink)!important;border-color:var(--zx-rule-ink)}
/* page-local "step" blocks: some pages draw them as paper cards, some leave them
   transparent — normalise to a paper card so the paper re-pin above is always true */
body.zx.zx-deck .zx .step.lit,body.zx.zx-deck .zx .steps > .step{background:var(--zx-cream);border-radius:var(--zx-r-2)}
/* components that are paper on some pages and bare on others: give them paper
   everywhere so the re-pin above is always true (double padding is accepted) */
body.zx.zx-deck .zx :is(.faq-item,.blk,.stepcard,.phase-item,.phase-row,.mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,.inc,.zx-tile,.tactics-list){background:var(--zx-cream);border-radius:var(--zx-r-2)}
body.zx.zx-deck .zx table{background:var(--zx-cream);color:var(--zx-ink)}
body.zx.zx-deck .zx :is(td,th){color:var(--zx-ink)!important}
body.zx.zx-deck .zx .dv{--dv-accent:#0A6E4A}
/* accent boxes (solid brand colour grounds): light text always */
body.zx.zx-deck .zx :is(th.us,td.us,.dv-bar span,.dv-box,.dv-box-lab,.dv-cap,.lo-dot,.svc-cta,.tk-hd,span.tick,b.tick,.lo-pin),
body.zx.zx-deck .zx :is(th.us,td.us,.dv-bar span,.dv-box,.dv-cap,.lo-dot,.svc-cta,.tk-hd,.lo-pin) :is(span,b,strong,p){color:#fff!important}
body.zx.zx-deck .zx .dv-bar.is-before span{color:var(--zx-ink)!important}
/* legacy article hero: the price chip is paper */
body.zx.zx-deck .page-hero .hero-price,body.zx.zx-deck .page-hero .hero-price *{color:var(--zx-ink)!important}
body.zx.zx-deck .page-hero .hero-price .hp-sep{color:var(--zx-dim)!important}
/* services tab strip on the dark band */
body.zx.zx-deck .zx :is(a.svc-tab,button.svc-tab){color:var(--zx-on-ink)!important;background:rgba(241,236,225,.08)}
body.zx.zx-deck .zx :is(a.svc-tab,button.svc-tab):is(.active,[aria-selected="true"]){color:var(--zx-ink)!important;background:var(--zx-cream)}
/* -- ground marks, set by zx-deck.js (see "ground marker") ------------------
   The marker decides each text element's role from its NEAREST marked ground;
   these are flat rules at (0,13,1) — above every :is(paper-list) guess (0,6,x)
   and every page-local !important — so nothing can fight them.
   .zx-t-i ink · .zx-t-d dim · .zx-t-m mint-ink · .zx-t-r red · .zx-t-w white
   (accent grounds) · .zx-gradtext keeps its gradient fill. */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-t-i{color:var(--zx-ink)!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-t-d{color:var(--zx-dim)!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-t-m{color:var(--zx-mint-ink)!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-t-r{color:#A82C20!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-t-w{color:#fff!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-gradtext{color:transparent!important}
/* CTA gradient band: everything white, dim = white at .85 */
body.zx.zx-deck .zx-sec.zx-grad :is(p,li,span,div,small,figcaption,.zx-body,.zx-small){color:rgba(255,255,255,.92)!important}
body.zx.zx-deck .zx-sec.zx-grad :is(h2,h3,b,strong,a:not([class*="btn"])){color:#fff!important}
/* -- FLOATING RAIL, owner directive 2026-09-04: "real logos, big, easy to click"
   Four 72px circles in brand colours, bottom-right, top->bottom: Maps (Google
   red) / Messenger (Meta blue) / LINE (LINE green) / phone (amber). Desktop
   only for Maps; Messenger + phone stay on mobile above the LINE bar. */
body.zx.zx-deck .zx-fab{position:fixed;right:20px;z-index:120;display:inline-flex;align-items:center;justify-content:center;width:72px;height:72px;border-radius:var(--zx-r-pill);border:0;text-decoration:none;box-shadow:0 10px 28px rgba(0,0,0,.28);transition:transform .18s ease,box-shadow .18s ease}
body.zx.zx-deck .zx-fab:hover{transform:scale(1.06)}
body.zx.zx-deck .zx-fab:active{transform:scale(.95)}
body.zx.zx-deck .zx-fab:focus-visible{outline:3px solid #fff;outline-offset:3px}
body.zx.zx-deck .maps-fab{background:#EA4335;bottom:282px}
body.zx.zx-deck .msgr-fab{background:#0084FF;bottom:194px}
body.zx.zx-deck .line-fab{display:inline-flex;align-items:center;justify-content:center;padding:0;width:72px;height:72px;right:20px;bottom:106px;background:#06C755;box-shadow:0 10px 28px rgba(0,0,0,.28)}
body.zx.zx-deck .line-fab svg{width:54px;height:54px;margin:0}
body.zx.zx-deck .line-fab .fab-tip{display:none}
body.zx.zx-deck .tel-fab{background:#F5A623;bottom:18px}
@media(max-width:860px){
  body.zx.zx-deck .maps-fab{display:none}
  body.zx.zx-deck .msgr-fab,body.zx.zx-deck .tel-fab{display:inline-flex;width:56px;height:56px;right:14px}
  body.zx.zx-deck .msgr-fab{bottom:154px}
  body.zx.zx-deck .tel-fab{bottom:88px}
  body.zx.zx-deck .msgr-fab svg{width:28px;height:28px}
  body.zx.zx-deck .tel-fab svg{width:26px;height:26px}
}
/* gradient-filled numerals on dark grounds: lighter stops so the first stop clears 5:1 */
body.zx.zx-deck :is(.zx-sec.zx-cream,.zx-sec.zx-alt,.zx-sec.zx-ink,.zx-sec.zx-ink-deep,.zx-archive) :is(.zx-num,.zx-step:first-child .zx-step-n){background-image:var(--zx-grad-dark)!important}
/* owner 2026-09-04: "floating maps gone when scrolling" — the FAB dodge must never hide the rail */
body.zx.zx-deck.zx-fabs-away :is(.maps-fab,.msgr-fab,.tel-fab,.line-fab){transform:none!important;opacity:1!important;visibility:visible!important;pointer-events:auto!important}



/* ========================================================================
   ANGA PARITY PASS — rows 1-8 of the merged gap table (2026-09-04, s194).
   Owner: Shane, "you see the balance, the looks, the fonts — copy it! ANGA",
   then "All CSS rows 1-8 now".

   Measured against anga.co.th by five blueprint agents; specs and the raw
   numbers live in design/ANGA-{B2B-BOXES,SERVICE-PAGES,CASE-STUDY,ARTICLE,
   ABOUT-FAQ-CHROME}-REFERENCE-SPEC-2026-09-04.md.

   COPY THE SYSTEM, NEVER THE BRAND. Nothing here imports ANGA's orange/white:
   every ground stays on our own 4-shade navy ramp, and where ANGA leans on a
   white card + black shadow we use a low-alpha light wash + a deeper black
   shadow, which is the same physics read against a dark page instead of a
   light one.

   ONE RULE ABOUT GROUNDS. zx-deck.js marks any box whose computed background
   is >0.98 alpha and hands its text a role class. Every wash below is kept at
   <=.06 alpha ON PURPOSE so the marker skips it and the text keeps the band's
   own on-ink colours. Do not raise those alphas without re-running the
   contrast sweep.
   ======================================================================== */

/* -- row 1 · TEXT INSIDE BOXES ------------------------------------------
   ANGA runs 16px flat inside every box against a 49.7ch measure; ours ran
   14px inside .dx-body against a 76ch measure, which is the literal "words
   in the box too small" Shane pointed at. 16px + 62ch is the ANGA balance
   at our body face. */
body.zx.zx-deck .zx .dx-body :is(p,li){font-size:16px;line-height:1.8;max-width:62ch}
body.zx.zx-deck .zx .dx-body li{margin-bottom:8px}
body.zx.zx-deck .zx :is(.faq-item,.blk,.tactics-list,.step,.stepcard,.phase-item,.phase-row,.reason,.transp-item,.do-item,.mech-card,.art-phase-item) :is(p,li){font-size:16px;line-height:1.75}
body.zx.zx-deck .zx :is(.see-txt,.see-get) p{font-size:16px;line-height:1.75}

/* -- row 2 · FEATURE ROWS BECOME A CARD ---------------------------------
   ANGA: one grid-2 box, radius 30, shadow 0 4px 50.7px @7%, gap 70, heading
   34px. Ours was a bare grid: radius 0, no shadow, gap 48, heading 24.
   Scoped >1180px so shared.css's own mobile 1fr block still wins (SC18). */
@media(min-width:1181px){
  body.zx.zx-deck .zx .see-row{
    gap:64px;padding:36px 40px;border-radius:24px;
    background:rgba(241,236,225,.04);
    box-shadow:0 4px 50px rgba(0,0,0,.28);
  }
}
body.zx.zx-deck .zx .see-row :is(h2,h3){font-size:30px;line-height:1.25}

/* -- row 3 · ONE SOFT SHADOW, NEVER A RING ------------------------------
   ANGA never rings a box; it carries one wide ambient blur. Ours stacked up
   to five layers plus a coloured glow ring on the FAQ/comparison/dataviz
   family, which is the single biggest cheap-vs-premium tell in the spec. */
body.zx.zx-deck .zx :is(.faq-item,.cmpbox,.cmpgrid,.dv,.blk,.boxed,.svc-card,.stepcard,.skubox,.mech-card,.zx-card,.zx-ab,.zx-tornstub,.answer-box,.answer-block,.tactics-list,.stat-callout,.transp-item,.do-item,.reason,.hire-banner,.zx-tile,.mdcc-tile,.mt-stat,.tmile.lit,.step,.phase-item,.phase-row){
  box-shadow:0 4px 50px rgba(0,0,0,.28);
}
body.zx.zx-deck .zx .zx-ticket.zx-tk-hero{box-shadow:0 10px 40px rgba(0,0,0,.30)}

/* -- row 4 · ONE RADIUS CONSTANT ----------------------------------------
   ANGA converges on 20-24px. Ours ran 0 / 10 / 12 / 14 / 16 / 20 across the
   same component family on different pages — a bug, not a style. */
body.zx.zx-deck .zx :is(.faq-item,.blk,.tactics-list,.mdcc-tile,.mt-stat,.dv,.boxed,.svc-card,.stepcard,.skubox,.mech-card,.stat-callout,.transp-item,.do-item,.reason,.zx-tile,.phase-item,.phase-row,.answer-box,.answer-block,.cmpbox){
  border-radius:20px;
}

/* -- row 5 · HEADING LADDER ---------------------------------------------
   ANGA demotes the h1 by page weight (home 70 -> service 60) and keeps a
   mid-tier 30px section head between the 50px major and the 24px card head.
   `.zx-h2` itself is NOT touched: 70/36/50 is the anchor scale Shane locked
   in Gate 1 C1 and SC16/SC19 measure against it. */
@media(min-width:861px){body.zx.zx-deck:not(.zx-home):not(.v2-deck) .zx .zx-hero .zx-h1 .zx-d{font-size:60px;line-height:1}} /* desktop only — the kit's 36px mobile step (SC01 hero fold) stays */
body.zx.zx-deck .zx .zx-h2--mid .zx-d{font-size:30px;line-height:1.3}

/* -- row 6 · COMPARISON BOX --------------------------------------------
   ANGA: three equal 360px columns, the "us" column lifted by a 200px-blur
   shadow. Ours: 369/321/321 with a 34px blur that reads flat beside it. */
body.zx.zx-deck .zx .cmpgrid{grid-template-columns:repeat(3,minmax(0,1fr))}
body.zx.zx-deck .zx .cmp-us{box-shadow:0 4px 40px rgba(24,184,122,.20)}
body.zx.zx-deck .zx .cmp-them{box-shadow:0 4px 40px rgba(0,0,0,.22)}

/* -- row 7 · STAT TILES -------------------------------------------------
   ANGA holds ONE stat scale sitewide (44px/700) and one tile geometry
   (pad 24/24/32, radius 20, gap 20). Ours ran two scales (36/800 vs 50/700)
   and three paddings. */
body.zx.zx-deck .zx :is(.mt-stat,.mdcc-tile){padding:24px 24px 32px}
body.zx.zx-deck .zx :is(.mt-stats,.mdcc-tiles,.mdcc-tiles-3,.mdcc-tiles-4,.mdcc-tiles-5,.mdcc-tiles-6){gap:20px}
body.zx.zx-deck .zx :is(.mt-stat .zx-t-i,.mt-stat b,.mdcc-num,.dv-tile-num){font-size:44px;font-weight:700;line-height:1.1}

/* -- row 8 · BREATHING ROOM AFTER THE HERO ------------------------------
   ANGA gives the first section under a hero 168px of air; the legacy article
   hero handed its next section 18px. 72px is the kit's own ladder step that
   lands closest without moving the snap deck. */
body.zx.zx-deck .zx .page-hero + :is(section,.zx-sec) .zx-wrap,
body.zx.zx-deck .zx .page-hero + :is(section,.zx-sec) > .wrap{padding-top:72px}

/* -- row 9 · GLOW (owner 2026-09-04: "check all boxes of anga has color glowing —
   we need it also, looks luxury; if not outside around, it's glowing inside the box")
   ANGA: the highlighted column carries an outer orange halo; feature cards carry a
   soft radial colour wash INSIDE the white. Ours, in our own hues: paper boxes get
   a sky (top-left) + mint (bottom-right) wash layered over the paper colour, and the
   three "hero" boxes (our comparison column, our vs-panel, the hero ticket) get an
   outer sky halo. Only background-IMAGE is added — background-color stays opaque
   paper, so zx-deck.js still marks these boxes as paper and text roles hold. */
body.zx.zx-deck .zx :is(.zx-card,.zx-ticket,.faq-item,.dv,.cmpbox,.blk,.svc-card,.stepcard,.skubox,.mech-card,.zx-ab,.zx-tornstub,.answer-box,.answer-block,.stat-callout,.mt-stat,.mdcc-tile,.tmile.lit,.step,.phase-item,.phase-row,.reason,.do-item,.transp-item,.zx-tile,.tactics-list,.zx-vs-panel:not(.zx-us),.zx-hero-box){
  background-image:radial-gradient(70% 60% at 0% 0%,rgba(62,142,247,.16) 0%,transparent 62%),
                   radial-gradient(60% 55% at 100% 100%,rgba(24,184,122,.14) 0%,transparent 62%);
}
body.zx.zx-deck .zx :is(.cmp-us,.zx-vs-panel.zx-us,.zx-ticket.zx-tk-hero,.gtee,.price-card.highlight,.zx-hero-box){
  box-shadow:0 0 0 1.5px rgba(62,142,247,.45),0 0 60px rgba(62,142,247,.42),0 4px 50px rgba(0,0,0,.28);
}
/* 🔴 SPECIFICITY LIFT — the halo above was INVISIBLE on every hero box that also
   carries `.lit`, which is why the owner asked "where's the growing shadow box ?!"
   on /ai-visibility.  Kit v1 `zx-system.css:417` ships
   `body.zx .lit.lit:not(.cmpgrid):not(.cmpbox):not(.cmp-us)…` — twelve :not()s, so
   it scores (0,15,1) and beats the rule above (0,5,1), repainting the box with the
   flat `--zx-sh-1`.  The anchor /medical-tourism only glowed because `.cmp-us` is one
   of the twelve classes that rule explicitly excludes.  zx-system.css is FROZEN
   (md5 7d6b23714bf0fe0553cb787e59d54326) so the fix cannot go there; the halo is
   restated here at (0,18,1) via repeated `.lit`, which is the smallest change that
   clears (0,15,1).  Deliberately NOT !important: `.zx-ticket:hover` and friends must
   still win on their own :hover specificity. */
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.cmp-us,.zx-vs-panel.zx-us,.zx-ticket.zx-tk-hero,.gtee,.price-card.highlight,.zx-hero-box){
  box-shadow:0 0 0 1.5px rgba(62,142,247,.45),0 0 60px rgba(62,142,247,.42),0 4px 50px rgba(0,0,0,.28);
}
body.zx.zx-deck .zx .cmp-them{box-shadow:0 4px 50px rgba(0,0,0,.24)}


/* -- row 10 - OFFER BOX PRESENCE (owner 2026-09-04: "make this part big like anga
   anchor ! too small check all the page").  The anchor's comparison block measures
   1012px at 1920 (.cmpwrap 1040 - 14px padding each side).  Primary offer cards ran
   680 (/seo) / 760 (.gtee) / 824 (the rest) -- all under the 900px the owner reads as
   "too small".  max-width only PERMITS width, so a card sitting in a narrow grid or
   flex track keeps its track width and nothing in a multi-column row moves. */
@media(min-width:1200px){
  body.zx.zx-deck .zx :is(.skubox,.gtee,.ctabox,.zx-hero-box,.pricing-grid){max-width:1012px;margin-inline:auto}
  /* /website and /geo-aeo hang their offer card off the legacy 888px `.wrap`, which
     caps the card at 824 no matter what max-width says.  A DIRECT child of `.wrap`
     is centred out to the same 1012 the anchor measures; the divisor is derived, not
     a magic number, so it stays correct if `.wrap` is ever re-sized.  Only direct
     children match, so a card nested in a grid/flex row keeps its track width. */
  body.zx.zx-deck .zx .wrap > :is(.skubox,.gtee,.ctabox,.zx-hero-box,.pricing-grid){
    width:1012px;max-width:none;margin-inline:calc((100% - 1012px) / 2)}
}

/* perf 2026-09-04: contain paint per section — shadows/glows never repaint neighbours */
body.zx.zx-deck .zx-sec{contain:paint}

/* ========================================================================
   ANGA PARITY PASS - rows 11-19  (2026-09-05, s195, GAN iteration-001)
   Owner: Shane, "make sure it looks like ANGA or better ! that all i want
   the whole site - in our theme."

   MEASURED, not guessed. ANGA b2b-marketing @1920 (design/ANGA-B2B-BOXES-
   REFERENCE-SPEC-2026-09-04.md 2.2): feature card 1240 wide, radius 30,
   ONE shadow `0 4px 50.7px rgba(0,0,0,.07)`, grid 585/585 gap 70, text pad
   72, h3 34/43.35/700, body 16/1.8. Ours before this pass (probe.py, same
   viewport): .see-row 1124 / r24 / pad 36x40 / gap 64 / h3 30 / body 16/28,
   and a ground of rgba(241,236,225,.04) which is invisible on BOTH the navy
   bands and the one cream band - i.e. the card ANGA leans on did not exist.

   GROUND FACT that drives every rule below: every zx-sec on this site
   computes to rgb(21,48,63) or rgb(15,36,51) (measured on ai-visibility,
   google-maps, seo, check, website, line-oa). The legacy `.zx-cream` /
   `.zx-alt` class names are NOT light any more. So ANGA's "white card on a
   light ground" becomes "light wash card on a navy ground": the same figure/
   ground separation, our palette, no ANGA white/orange anywhere.

   ALPHA DISCIPLINE. zx-deck.js marks a box `.zx-paper` only when its
   computed background-color alpha is > .98. Every wash below sits at .07,
   so the marker skips it and the text keeps the band's own on-ink colours -
   nothing re-roles, nothing loses contrast. Do not raise these to 1.0
   without re-running the contrast sweep.
   ======================================================================== */

/* -- row 11 - THE CARD ITSELF (ANGA's single biggest move) ---------------
   One ground + one radius + ONE soft wide shadow on every box block, so the
   whole site reads as one system instead of eight. Radius stays inside
   ANGA's measured 20-30 band; .see-row takes the 30 ANGA uses for its
   feature rows, everything else the 24 it uses for cards and panels. */
body.zx.zx-deck .zx :is(.see-row,.skubox,.gtee,.ctabox,.zx-hero-box,.qcard){
  background-color:#FBFBF9;
  border:0;
  box-shadow:0 4px 50px rgba(0,0,0,.28);
}
/* ...and on a paper band it takes ANGA's own alpha instead (see row 22). */
body.zx.zx-deck .zx :is(.see-row,.skubox,.gtee,.ctabox,.zx-hero-box,.qcard).zx-gl{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}
body.zx.zx-deck .zx :is(.skubox,.gtee,.ctabox,.zx-hero-box,.qcard){border-radius:24px}
body.zx.zx-deck .zx .see-row{border-radius:30px}

/* -- row 12 - ANGA AIR: 72px of text padding ----------------------------
   ANGA .info pad is 72px; ours ran 0 (.skubox), 40 (.gtee) and 36/40
   (.see-row). Below 1181px shared.css's own single-column block owns the
   layout, so the ANGA air is desktop-only and 390px is untouched. */
@media(min-width:1181px){
  body.zx.zx-deck .zx :is(.see-row,.skubox,.gtee,.ctabox,.zx-hero-box,.qcard){padding:64px 72px}
  body.zx.zx-deck .zx .see-row{gap:70px;align-items:center}
}
/* `.see-row` is deliberately ABSENT below: shared.css ships its own mobile
   block (`.see-row{padding:34px 0}`) and SC18 fails any kit rule that
   silently overrides a shared responsive block. The offer boxes have no
   such shared block, so they take the mobile pad here. */
@media(max-width:1180px){
  body.zx.zx-deck .zx :is(.skubox,.gtee,.ctabox,.zx-hero-box,.qcard){padding:28px 22px}
}

/* -- row 13 - FEATURE ROW WIDTH: 1240, ANGA's own number ----------------
   Only a DIRECT child of .see-rows is widened, and only above 1330px, so a
   row nested in a narrow grid/flex track keeps its track width and nothing
   can overflow the viewport. The archive keeps its panel width. */
@media(min-width:1330px){
  /* Every .see-row, not only a direct child of .see-rows: the population
     measured 1240/1124/888/824/772 because 9 of 12 rows sit inside an archive
     accordion. Row 24 widens that accordion first, so the derived margin here
     is small or zero and nothing bursts a clip. */
  body.zx.zx-deck .zx .see-row{width:1240px;max-width:none;margin-inline:calc((100% - 1240px) / 2)}
}

/* -- row 14 - TYPE INSIDE THE FEATURE ROW -------------------------------
   ANGA h3 34/43.35/700 with a 20px gap to the body, body 16/1.8 at a 32ch
   measure. Ours was h3 30 and body 16/1.75 unbounded. 62ch is the top of
   the spec's 32-62ch band and is what our Thai face needs to not read thin. */
@media(min-width:1181px){
  body.zx.zx-deck .zx .see-row :is(h2,h3,.zx-h3){font-size:34px;line-height:1.275;font-weight:700;margin-bottom:20px}
}
body.zx.zx-deck .zx .see-row :is(p,li){font-size:16px;line-height:1.8;max-width:62ch}
body.zx.zx-deck .zx :is(.skubox,.gtee,.ctabox,.zx-hero-box,.qcard) :is(p,li){font-size:16px;line-height:1.8}

/* -- row 15 - THE IMAGE SIDE SITS INSIDE THE CARD -----------------------
   ANGA's figure carries no border, no shadow and no second card of its own:
   the shared card shadow covers it. Ours shipped .dv/.see-fig at radius 0
   with its own paper ground and 14px pad, which read as a box-in-a-box. */
body.zx.zx-deck .zx .see-row :is(.see-fig,.see-slot,.dv){box-shadow:none;border-radius:20px}
body.zx.zx-deck .zx .see-row :is(.see-fig,.see-slot) img{border-radius:16px}

/* -- row 16 - COMPARISON TEXT PARITY (SC19) -----------------------------
   Shane's rule: every comparison box is the same on every page, and
   medical-tourism is the anchor. The anchor renders .cmpbox/.cmpgrid at
   18px/32.4px; ai-visibility, google-maps, line-oa and services rendered
   16.32px/26.52px, which is the live SC19 failure. Pinning the box to the
   anchor's own pair fixes the other pages and is a no-op on the anchor. */
body.zx.zx-deck .zx :is(.cmpbox,.cmpgrid){font-size:18px;line-height:1.8}

/* -- row 17 - FAQ IS A CARD, AND IT IS NOT 1350px WIDE ------------------
   ANGA's FAQ is a radius-20 panel with one soft shadow. Ours: `.faq
   details.lit` at radius 20 but 16x20 padding, and the whole list running
   the full 1350px content width - 84ch of measure, twice the anchor's own
   960px FAQ. 960 is taken from medical-tourism's .faq-item, so the FAQ now
   matches the anchor instead of the page container. */
body.zx.zx-deck .zx .faq{max-width:960px;margin-inline:auto}
body.zx.zx-deck .zx .faq > details{border-radius:20px;box-shadow:0 4px 50px rgba(0,0,0,.28)}
/* 30px 32px, not a round 24: SC19 measures .faq-item against the anchor and
   medical-tourism renders 30px top and bottom. Anything else fails parity. */
@media(min-width:861px){
  body.zx.zx-deck .zx .faq > details{padding:30px 32px}
}
body.zx.zx-deck .zx .faq > details :is(p,li){font-size:16px;line-height:1.8;max-width:62ch}

/* -- row 18 - FEATURE CARDS ARE 433 WIDE, NOT 311 -----------------------
   ANGA feature card = 433px (brief, Reference block). `.mech-grid` was
   minmax(220px,1fr) inside a 960px cap, which produced 311px cards - 28%
   under ANGA and the reason the mechanism row reads cramped. 1350 is the
   kit's own content width, so three tracks land at 434px. */
body.zx.zx-deck .zx .mech-grid{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));max-width:1350px;gap:24px}
body.zx.zx-deck .zx .mech-card{padding:32px}

/* -- row 19 - ONE SHADOW, NEVER A RING (finishes row 3) -----------------
   The 1.5px sky ring on the hero/offer boxes is the one thing both
   reference specs call a cheap-vs-premium tell ("reads as an outline, not a
   lift"). ANGA's own highlight is a brand-COLOURED spread with no hard
   edge, so the halo keeps its colour and loses its ring. `.cmp-us` is
   deliberately EXCLUDED: it lives on the SC16-guarded anchor and row 6
   already gives it its own shadow.
   The 13x `.lit` repeat is not decoration - zx-system.css:417 ships a
   (0,15,1) `.lit.lit:not(...)x12` rule that repaints these boxes, and
   zx-system.css is frozen (md5 7d6b2371...), so the only place to out-rank
   it is here. 13 repeats scores (0,18,1). Not !important, so :hover still
   wins on its own. */
body.zx.zx-deck .zx :is(.zx-vs-panel.zx-us,.zx-ticket.zx-tk-hero,.gtee,.price-card.highlight,.zx-hero-box),
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.zx-vs-panel.zx-us,.zx-ticket.zx-tk-hero,.gtee,.price-card.highlight,.zx-hero-box){
  box-shadow:0 4px 50px rgba(0,0,0,.28);
}
/* iteration-002: the brand halo `0 4px 90px rgba(62,142,247,.34)` that used to
   live here was measurably FLASHIER than the reference (11 boxes carrying a
   sky glow ANGA does not have) and it was a seventh distinct shadow value.
   Collapsed into the same two-value system as every other card. */
/* The vs-panels on the industry pages shipped with box-shadow:none, so they
   read as flat blocks next to every other card. Scoped to panels INSIDE a
   .zx-vs grid: the anchor medical-tourism carries a bare .zx-vs-panel.zx-muted
   with no .zx-vs wrapper, and SC16 measured a 3,414px regression when the
   shadow landed on it, so the anchor is left byte-for-byte alone.
   The .dx accordion keeps its own --zx-sh-1 tier for the same reason. */
body.zx.zx-deck .zx .zx-vs .zx-vs-panel{box-shadow:0 4px 50px rgba(0,0,0,.28)}

/* -- row 20 - THE `.lit` SPECIFICITY LIFT FOR THE CARD GROUND ------------
   Measured after row 11 landed: `.skubox.lit` on /ai-visibility still
   painted `rgba(11,31,42,.07) 0 4px 50px` - invisible on a navy band -
   because zx-system.css:417 `.lit.lit:not(...)x12` scores (0,15,1) and row
   11 scores (0,5,1). Same frozen-file trap documented at row 19; same
   minimum fix, restated at (0,18,1). */
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.see-row,.skubox,.ctabox,.qcard,.gtee,.zx-hero-box){
  box-shadow:0 4px 50px rgba(0,0,0,.28);
}
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.see-row,.skubox,.ctabox,.qcard,.gtee,.zx-hero-box).zx-gl{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}

/* -- row 21 - CAROUSEL FEATURE CARDS AT ANGA'S 433 ----------------------
   ANGA's feature card measures 433px. Ours ran a fixed 340px slide inside
   .zx-carousel-track on /services, /check and /seo - 21% narrower, which is
   why those rows read as chips rather than cards. Desktop only: the mobile
   carousel keeps its own slide width so nothing overflows at 390px. */
@media(min-width:1181px){
  body.zx.zx-deck .zx .zx-carousel-track > .zx-card.zx-paper{flex:0 0 433px;width:433px;max-width:433px}
}


/* ========================================================================
   ANGA PARITY PASS 2 - rows 22-31  (2026-09-05, s195, GAN iteration-002)
   Driven line-by-line by gan-harness/anga-box/feedback/feedback-001.md, which
   re-measured ANGA live at 1920 and re-measured us with Playwright:
     ANGA .service-content.-box = 1240 wide - r30 - rgba(0,0,0,.07) 0 4px 50.7px
     - bg #fff - text pad 72 - h3 34/43.35/700 - body 16/24 - NO border, NO ring.
   Ours failed four ways: the card was a 7% wash (1.00-1.23x contrast against
   its own ground, i.e. invisible), .see-row hit 1240 on 17% of instances and
   72px pad on 25%, seven distinct shadow values were live, and ~40 cards
   carried a 1px tan border UNDER a shadow.
   ======================================================================== */

/* -- row 22 - TWO SHADOW VALUES FOR THE WHOLE SITE, AND NO BORDER --------
   The census found 7 values (.34/.28/.25/.07/none/two 1.5px rings/a sky
   halo). ANGA runs one, because ANGA has one ground. We have two grounds, so
   we run exactly two, picked by the .zx-gl/.zx-gd tier that zx-deck.js
   resolves per card (see the "card GROUND TIER" block in that file):
     dark navy band -> 0 4px 50px rgba(0,0,0,.28)   (rubric cap on dark: .30)
     paper band     -> 0 4px 50px rgba(0,0,0,.07)   (ANGA's own alpha)
   The border goes with them: ANGA's feature card has none, and a 1px tan
   rule under a 50px shadow is the "cheap outline" read both reference specs
   name. .cmp-* keeps its borders - those are the comparison table's ROW
   separators, structure not decoration - and .zx-ticket keeps its 14%
   hairline because that is the torn-stub motif, not a card edge. */
body.zx.zx-deck .zx :is(.blk,.step,.dx,.mech-card,.tcard,.zp-tcard,.mt-stat,.dv-tile,.dv-fig,.proof,.pf,.mdcc-card,.see-get,.lo-qr,.dv){
  border:0;
}
body.zx.zx-deck .zx :is(.blk,.step,.dx,.mech-card,.tcard,.zp-tcard,.mt-stat,.dv-tile,.dv-fig,.proof,.pf,.mdcc-card,.see-get,.lo-qr,.dv).zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.blk,.step,.dx,.mech-card,.tcard,.zp-tcard,.mt-stat,.dv-tile,.dv-fig,.proof,.pf,.mdcc-card,.see-get,.lo-qr,.dv).zx-gd{
  box-shadow:0 4px 50px rgba(0,0,0,.28);
}
/* SC19 note: .faq-item / .blk / .answer-box / .mt-stat / .vs-col are
   anchor-locked gate classes - their computed style must be IDENTICAL on every
   page, so they are deliberately EXCLUDED from the light-ground variant and
   keep the .28 value the anchor renders. Ground-dependence is only for the
   card classes the gate does not lock. */
body.zx.zx-deck .zx :is(.step,.dx,.mech-card,.tcard,.zp-tcard,.dv-tile,.dv-fig,.proof,.pf,.mdcc-card,.see-get,.lo-qr,.dv).zx-gl,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.step,.dx,.mech-card,.tcard,.zp-tcard,.dv-tile,.dv-fig,.proof,.pf,.mdcc-card,.see-get,.lo-qr,.dv).zx-gl{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}

/* -- row 23 - THE LAST RING: .cmp-us ------------------------------------
   Row 19 excluded .cmp-us because a clean shadow moved 3,414 anchor pixels
   under SC16. Leaving a 3-layer 1.5px sky ring on three pages is not an
   option (feedback-001, fix #4), so it goes site-wide, including the anchor,
   and lands on ANGA's own measured comparison shadow:
   0 4px 200px rgba(0,0,0,.17) (B2B reference spec 2.1). The SC16 pixel delta
   is recorded in generator-state.md for Shane - NOT rebaselined here. */
body.zx.zx-deck .zx .cmpgrid .cmp-us,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .cmp-us{
  box-shadow:0 4px 200px rgba(0,0,0,.17);
}

/* -- row 24 - THE ARCHIVE GETS ANGA'S WIDTH SO ITS ROWS CAN BE 1240 -----
   Measured cause of the 17% width hit rate: 9 of 12 .see-rows live inside
   details.dx inside .zx-archive .zx-wrap{max-width:1000px}, which cascades
   down to 940 -> 888 -> 824 -> 772. Widening the row alone would burst a
   940px overflow:hidden panel; widening the PANEL first makes the derived
   margin in row 13 small and keeps every card inside its own container. */
@media(min-width:1330px){
  body.zx.zx-deck .zx .zx-archive .zx-wrap{max-width:1410px}
  body.zx.zx-deck .zx .zx-archive :is(.wrap,.sect,.dx-body,.see-rows,.see-sect,.svc-see,section,article,details.dx):has(.see-row){
    max-width:none;width:auto;overflow:visible;
  }
  body.zx.zx-deck .zx :is(article,.wrap,.see-rows,section):has(> .see-row){max-width:none;overflow:visible}
}

/* -- row 25 - .see-row PAD ON 100% OF INSTANCES -------------------------
   ANGA .info pad is 72. Ours measured 64px 72px on 3 rows and 32px on 9
   (the archive override deleted from row 12). One value now, every instance.
   The 20px .dv radius is here too: google-maps rendered .dv.see-fig at 0px
   because row 15 only reached the figure when it was a direct text sibling;
   the descendant form below reaches the nested case. */
@media(min-width:1181px){
  body.zx.zx-deck .zx .see-row{padding:64px 72px;gap:70px;align-items:center}
}
body.zx.zx-deck .zx .see-row :is(.dv,.see-fig,.see-slot),
body.zx.zx-deck .zx .see-row :is(.dv,.see-fig,.see-slot) > figure{border-radius:20px}
body.zx.zx-deck .zx .see-row :is(.dv,.see-fig,.see-slot) :is(img,svg,picture){border-radius:16px}

/* -- row 26 - 390px: AIR ON THE CARD, ONE COLUMN IN THE COMPARISON ------
   Measured at 390: .see-row computed padding 34px 0px - the card edge touched
   the text - while .skubox ran 28px 22px on the same page, and .cmpgrid
   stayed 124.656px 124.672px 124.656px, i.e. 111px of content at 14.5px Thai,
   about 7 characters a line.
   NOTE, stated openly for SC18: shared.css owns .see-row{padding:34px 0}
   inside its own mobile block. This rule DOES change the side air, on
   purpose, and uses padding-inline so shared.css keeps ownership of the
   vertical rhythm - the override is declared here, not silent. */
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row{padding-inline:16px}
  /* the dataviz figures must not lose those 32px: at 390 a .dv chart is a
     viewBox-scaled SVG, and 32px off its width dropped two date labels to
     8.63px, under the 10px floor. They span the card instead. */
  body.zx.zx-deck .zx .see-row > :is(.dv,.see-fig,.see-slot){margin-inline:-16px}
  body.zx.zx-deck .zx .cmpgrid{grid-template-columns:1fr}
  body.zx.zx-deck .zx .cmpgrid .cmp-lbl{text-align:left;padding-top:18px}
  body.zx.zx-deck .zx .cmpgrid .cmp-hd.cmp-lbl{display:none}
  body.zx.zx-deck .zx a.zx-t-m{display:inline-flex;align-items:center;min-height:44px}
}

/* -- row 27 - TYPE: ANGA'S OWN LADDER, IN OUR COLOURS -------------------
   The font question was answered by measurement, not by taste:
   getComputedStyle(document.body).fontFamily on the live ANGA page returns
   "IBM Plex Sans Thai" - the exact family, free on Google Fonts, and already
   fallback #2 in our own stack. Anuphan simply won the cascade. It is now
   first. FCSound (ANGA's display face) is commercial; Kanit is the closest
   free analogue - same geometric-grotesque skeleton, single-storey Latin a,
   tight Thai loops, taller x-height than Anuphan at the same optical size.
   Weights 600 and 700 only, so the extra download is two files. */
body.zx:where(.zx-deck){
  --zx-font-body:"IBM Plex Sans Thai","Anuphan",system-ui,sans-serif;
  --zx-font-display:"Kanit","Bai Jamjuree","Anuphan",system-ui,sans-serif;
}
body.zx.zx-deck,body.zx.zx-deck .zx{font-family:var(--zx-font-body)}
body.zx.zx-deck .zx :is(h1,h2,h3,h4,.zx-h1,.zx-h2,.zx-h3,.zx-d,.zx-btn,.zx-tab){font-family:var(--zx-font-display)}

/* h1: ANGA steps the SERVICE page down to 60/60 and keeps 70/87.5 for the
   home page only. index.html carries v2-deck on <body>; nothing else does. */
/* DESKTOP ONLY. The kit ships its own 36px/30px mobile step for the hero fold
   budget (zx-deck.css:1618-1619 at :where() weight); an unmediated rule here
   out-ranked it and rendered a 60px h1 at 390, which pushed .zx-hero-frame
   below the 40% fold floor on services. */
@media(min-width:861px){
  body.zx.zx-deck .zx .zx-h2 .zx-d{font-size:50px;line-height:1.2}
  body.zx.zx-deck .zx .zx-intro .zx-h2 .zx-d{font-size:48px;line-height:1.25}
}
body.zx.zx-deck .zx a.zx-btn,body.zx.zx-deck .zx button.zx-btn{font-size:18px;line-height:28px;font-weight:700}

/* -- row 28 - ONE BODY SIZE PER PAGE ------------------------------------
   google-maps alone rendered nine body sizes: 12.5 / 14 / 14.5 / 16 / 16.32 /
   17 / 18 / 18.5 / 20. ANGA runs four (12 / 14 / 16 / 18) and exactly one of
   them is body copy. So: body copy is 16/24 everywhere, small print is 14/21,
   and the ONLY exception is .cmpbox at 18/32.4 because SC19 locks every
   comparison box to the medical-tourism anchor, which renders that pair. */
body.zx.zx-deck .zx :is(p,li,dd,td):not(.zx-small):not(.photo-cap):not(.dv-src):not(.dv-sub):not(.price-note):not(.zx-lead):not(figcaption){
  font-size:16px;line-height:24px;
}
body.zx.zx-deck .zx :is(.zx-small,.photo-cap,.dv-src,.dv-sub,.price-note,figcaption,.gtee-cond){font-size:14px;line-height:21px}
/* desktop only: premium.css owns the comparison cell sizes inside its own
   mobile block (.cmp-lbl 16px) and SC18 fails any kit rule that outranks it. */
@media(min-width:861px){
  body.zx.zx-deck .zx :is(.cmpbox,.cmpgrid),
  body.zx.zx-deck .zx :is(.cmpbox,.cmpgrid) :is(p,li,div){font-size:18px;line-height:32.4px}
}
body.zx.zx-deck .zx .see-row :is(p,li){font-size:16px;line-height:24px;max-width:62ch}

/* -- row 29 - SECTION RHYTHM 96, NOT 144 -------------------------------
   ANGA measured 96/96 on every standard section and 192/192 on the one dark
   break band. Ours ran 96 on some sections and 144/144 on about five per
   page (plus 140, 48, 44/60, 22/26). The kit's own s4 token IS the 144. */
/* The 144 is the --zx-s3 token itself; zx-system.css:298 reads it behind a
   (0,12,1) :not() chain that no sane kit selector can outrank, so the token is
   where it has to be fixed. --zx-s4 stays 192 - that is ANGA's dark break. */
body.zx:where(.zx-deck){--zx-s3:96px;--zx-deck-s3:96px;--zx-s4:192px;--zx-deck-s4:192px}
body.zx.zx-deck .zx section.cmpbox{padding-block:96px}
body.zx.zx-deck .zx .zx-sec.zx-major{padding-top:96px;padding-bottom:96px}
body.zx.zx-deck .zx .zx-sec.zx-break{padding-top:192px;padding-bottom:192px}

/* -- row 30 - THE FIGURE SIDE IS NEVER A SECOND CARD --------------------
   ANGA's image shares the card: no ground, no shadow, no border of its own.
   Row 15 only cleared the shadow; the paper ground survived on .dv and read
   as a box-in-a-box now that the row itself is paper. */
body.zx.zx-deck .zx .see-row :is(.dv,.see-fig,.see-slot){background:transparent;box-shadow:none;border:0;padding:0}
body.zx.zx-deck .zx .see-row :is(.dv,.see-fig,.see-slot) :is(img,picture){display:block;width:100%;height:auto}

/* -- row 31 - RADIUS BAND 20-30, NO EXCEPTIONS -------------------------
   The census found 20px 20px 0px 0px on 25 .zx-tickets (the torn-stub motif)
   and 0px on 6 .dv.see-figs. The stub keeps its perforated edge but the
   corners come back inside ANGA's band. */
body.zx.zx-deck .zx .zx-ticket{border-radius:20px}

/* -- row 32 - THE ARCHIVE PANEL THAT HOSTS FEATURE ROWS IS A BAND, NOT A CARD
   Root cause of the "zero separation" reading in feedback-001 Axis 2: the
   archive read-more panel (.dx) is itself paper (#FBFBF9 via the owner-theme
   paper-component rule), so a paper .see-row inside it composited at 1.000x
   against its own ground. ANGA never nests a card in a card. A .dx that hosts
   feature rows therefore stops being a card and becomes what it actually is -
   a disclosure region on the navy archive band - and the rows inside it are
   the only cards, paper on navy, measured 13x+ contrast.
   Text roles: zx-deck.js marks the paper .see-row and re-inks everything
   inside it with !important, so these on-ink colours only reach the panel
   chrome (summary, section headings, captions) that now sits on navy. */
body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox){
  background:transparent;box-shadow:none;border:0;
}
body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox),
body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox) :is(summary,h2,h3,h4,b,strong,.zx-h2,.zx-h3){color:var(--zx-on-ink)}
body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox) :is(p,li,figcaption,.zx-small,.photo-cap){color:var(--zx-on-ink-dim)}

/* -- row 33 - LIGHT-GROUND CARDS, AND NEVER A CARD INSIDE A CARD --------
   On a light band ANGA's card is #fff on a near-white ground: the read comes
   from the 7% shadow, not from the fill. Two residual cases measured after
   row 32 landed: (a) 5 cards still composited at exactly 1.000 against their
   ground - google-maps' cream `.clients-wall` band and case-mdcc, which is a
   light article page, not a ramp page; (b) `check.html` nests `.ctabox`
   inside `.qcard.zx-hero-box`, a card inside a card.
   (a) gets pure white so the fill does a little work on top of the shadow
   (1.04-1.09x, ANGA's own figure); (b) stops being a second card. */
body.zx.zx-deck .zx :is(.see-row,.skubox,.gtee,.ctabox,.zx-hero-box,.qcard).zx-gl{background-color:#FFFFFF}
body.zx.zx-deck .zx :is(.zx-hero-box,.qcard,.gtee,.skubox) :is(.ctabox,.qcard,.skubox){
  background:transparent;box-shadow:none;border:0;
}

/* -- row 34 - THE LEGACY TYPE TOKENS POINT AT THE SAME TWO FAMILIES ------
   Measured after row 27: `.see-row h3` and `.cmpgrid .cmp-us` still rendered
   Anuphan because shared.css:90 sets `h3,h4,h5,summary,button,.logo` to
   `var(--font-display)!important` - a DIFFERENT token from --zx-font-display.
   Repointing the legacy pair is the only way past an !important in a file we
   do not own, and it keeps one display face and one body face site-wide. */
body.zx.zx-deck{
  --font-display:"Kanit","Anuphan",system-ui,sans-serif;
  --font-body:"IBM Plex Sans Thai","Sarabun",system-ui,sans-serif;
  font-size:16px;
}
/* the CTA pill: zx-deck.css:473 pins 17px with !important to clear glow3's
   (0,5,0) `.js-line-cta` rule, so ANGA's 18/28 has to answer in kind. */
/* desktop only. At 390 the kit already pins 16px/52px-min with !important for
   the hero fold budget, and forcing 18 there pushed .zx-hero-frame off the
   first viewport on services (43.3% at 1440 but 38.3% at 390, under the 40%
   floor loop_verify enforces). ANGA's mobile CTA is smaller too. */
@media(min-width:861px){
  body.zx.zx-deck .zx a.zx-btn,body.zx.zx-deck .zx button.zx-btn{
    font-size:18px!important;line-height:28px!important;
  }
}
body.zx.zx-deck .zx a.zx-btn,body.zx.zx-deck .zx button.zx-btn{font-family:var(--zx-font-display)!important}

/* -- row 35 - THE LEGACY .cmp-grid IS ALSO A COMPARISON BOX (SC19) ------
   ai-visibility does not use the shared `.cmpgrid`; it ships premium.css's
   older `.cmp-grid` (hyphen) with the same `.cmp-us/.cmp-lbl/.cmp-them` cell
   classes. SC19 measures the CELL classes, not the wrapper, so that page read
   17px/28.9 and 17.5/29.75 against the anchor's 18/32.4 - the live SC19
   failure carried over from iteration 001. Desktop only: premium.css owns
   these cells inside its own mobile block. */
@media(min-width:861px){
  body.zx.zx-deck .zx .cmp-grid :is(.cmp-us,.cmp-lbl,.cmp-them),
  body.zx.zx-deck .zx .cmp-grid .cmp-head .cmp-them{
    font-size:18px;line-height:32.4px;padding:20px 12px;
  }
}
/* same pass: the label rule and the FAQ card line-height are SC19-locked too.
   The anchor renders .cmp-lbl with an inset rgb(230,233,238) hairline and
   .faq-item at 16/28.8; the other pages inherited 16/26 from the body. */
body.zx.zx-deck .zx .cmp-grid .cmp-lbl{box-shadow:inset 0 1px 0 rgb(230,233,238)}
body.zx.zx-deck .zx .faq-item{line-height:28.8px}

/* -- row 36 - SC19 PARITY ON THE ANCHOR-LOCKED GATE CLASSES -------------
   Shane's rule: the comparison / answer / FAQ / stat blocks are the same on
   every page and medical-tourism is the anchor. Three real divergences were
   measured after rows 22-35 landed:
   (a) the anchor's body line-height is 1.8, every other page inherited 1.625,
       so `.blk`, `.vs`, `.vs-col` and `.answer-box` read 26px against the
       anchor's 28.8px. One value, site-wide - body COPY keeps 16/24 because
       row 28 declares that on p/li explicitly.
   (b) `.answer-box` still carried a 1px border on check + ku-mud, where a
       page-local rule outranks row 22.
   (c) `.faq-item` line-height, same cause as (a). */
body.zx.zx-deck .zx :is(.blk,.vs,.vs-col,.answer-box,.answer-block,.faq-item,.mt-stat,.proofgrid,.cmp-vs){line-height:28.8px}
body.zx.zx-deck .zx .faq-item{border-radius:20px}

/* -- row 37 - 390: THE ARCHIVE NESTING WAS EATING 112px OF CARD ---------
   Measured at 390 on line-oa: .see-row widths ran 310 / 274 / 238 because the
   archive stacks up to three disclosure levels and each one adds its own side
   padding (350 -> 310 -> 274 -> 238). Inside a 238px card the .dv-line SVG
   renders 206px wide, and dataviz.css's 21px viewBox date label composites to
   9.1px - under the 10px floor. The panels that host feature rows already
   stopped being cards in row 32; here they stop reserving card padding too. */
@media(max-width:860px){
  body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox),
  body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox) > .dx-body,
  body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox) .wrap,
  body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox) .sect,
  body.zx.zx-deck .zx .zx-archive .dx:has(.see-row,.skubox,.gtee,.ctabox) .see-sect{
    padding-inline:0;
  }
}

/* ========================================================================
   GAN ITERATION 003 — rows 38-45. Every item below is a measured miss from
   gan-harness/anga-box/feedback/feedback-002.md, fixed in the kit so no page
   inline <style> grows.
   ======================================================================== */

/* -- row 38 - THE GROUND TIER REACHES THE REST OF THE CARDS --------------
   feedback-002 fix #2: 17 of 317 cards sat on a paper ground at contrast
   <=1.03 and still painted `0 4px 50px rgba(0,0,0,.28)` — a 28% black smear
   under a card the same colour as the band. zx-deck.js already stamps
   .zx-gl/.zx-gd on .faq-item, so the miss was purely CSS: row 22's :is()
   list never named .faq-item / .zx-card / the page-local card classes.

   TWO DIFFERENT TREATMENTS, and the split is forced by the gate, not taste:

   (a) .faq-item and .blk are SC19_COMPONENTS. shane_check compares their
       computed `boxShadow` with ZERO tolerance across every page, so a
       ground-DEPENDENT shadow on those two classes would fail SC19 the
       moment a light page (case-mdcc) and a navy page (restaurant) are
       compared. They therefore take ONE value site-wide, and the value is
       ANGA's own measured alpha: 0 4px 50px rgba(0,0,0,.07). On navy the
       card still reads at 13-15x from its paper fill, not from the shadow;
       on paper the smear is gone. The FILL is what changes per ground —
       backgroundColor is not an SC19 property.
   (b) .zx-card / .zx-ticket / the page-local card classes are NOT gate-
       locked, so they get the real two-value tier. */
body.zx.zx-deck .zx :is(.faq-item,.faq > details,.blk).zx-gl,
body.zx.zx-deck .zx :is(.faq-item,.faq > details,.blk).zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.faq-item,.faq > details,.blk){
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}
body.zx.zx-deck .zx :is(.faq-item,.faq > details,.blk).zx-gl{background-color:#FFFFFF}
body.zx.zx-deck .zx :is(.faq-item,.faq > details,.blk).zx-gd{background-color:#FBFBF9}

body.zx.zx-deck .zx :is(.zx-card,.zx-ticket,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row).zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.zx-card,.zx-ticket,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row).zx-gd{
  background-color:#FBFBF9;box-shadow:0 4px 50px rgba(0,0,0,.28);
}
body.zx.zx-deck .zx :is(.zx-card,.zx-ticket,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row).zx-gl,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.zx-card,.zx-ticket,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row).zx-gl{
  background-color:#FFFFFF;box-shadow:0 4px 50px rgba(0,0,0,.07);
}
/* A card nested INSIDE a .see-row is not a second card (ANGA's row holds one
   surface). Same rule row 33 applies to .ctabox inside .qcard. */
/* .blk is NOT in this list on purpose: it is an SC19 component, so its
   padding must be identical on every page and a row-scoped padding:0 fails
   the gate (measured: why-zipai/chatbot .blk paddingTop 30 -> 0). */
body.zx.zx-deck .zx .see-row :is(.reason,.stepcard,.do-item,.sym,.zx-step,.art-phase-item,.phase-row,.wz-step,.km-cause,.p-split-copy,.transp-item){
  background:transparent;box-shadow:none;border:0;padding:0;margin:0;
}

/* -- row 39 - border:0 DELIVERED FOR REAL -------------------------------
   feedback-002 fix #3. Iteration 002 claimed this and a computed-style probe
   found ~50 survivors, because the page-local and premium.css rules that set
   them are (0,2,0)+ and row 22's list did not include the two classes that
   actually carried them: .faq-item (1px solid #E6E9EE / #DDD5C4 on
   google-maps x8, geo-aeo x10, check x5, website x5, line-oa x5) and
   .zx-card.zx-ticket (1px solid rgba(11,31,42,.14) on index x4, line-oa x3,
   website x3, google-maps x2), plus ai-visibility's .zx-paper x7.
   The .lit x13 repeat is the documented (0,15,1) trap from zx-system.css:417.
   .cmp-* is still excluded: those borders are the comparison table's ROW
   RULES, i.e. structure. The .zx-ticket hairline that row 22 kept as the
   "torn-stub motif" goes too — the perforated .zx-perf edge is the motif and
   it survives on its own. */
body.zx.zx-deck .zx :is(.faq-item,.faq > details,.zx-card,.zx-ticket,.zx-paper,.see-row,.see-fig,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row,.answer-box,.answer-block,.zx-tile,.mech-card,.stat-card,.price-card),
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.faq-item,.faq > details,.zx-card,.zx-ticket,.zx-paper,.see-row,.see-fig,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row,.answer-box,.answer-block,.zx-tile,.mech-card,.stat-card,.price-card){
  border-width:0;border-style:none;
}

/* -- row 40 - .cmp-us RADIUS 12 -> 20 -----------------------------------
   feedback-002 fix #4. ANGA's us-column is r20; ours computed 12, and the
   top/bottom cells carried the split forms `12px 12px 0 0` / `0 0 12px 12px`.
   The split shape is correct (it is one column drawn as stacked cells) — only
   the number was wrong. */
body.zx.zx-deck .zx .cmpgrid .cmp-us{border-radius:20px}
body.zx.zx-deck .zx .cmpgrid .cmp-hd.cmp-us{border-radius:20px 20px 0 0}
body.zx.zx-deck .zx .cmpgrid .cmp-us:last-child{border-radius:0 0 20px 20px}

/* -- row 41 - THE TICKET COVER STOPPED CLIPPING ITS PICTURE -------------
   feedback-002 fix #5. `.zx-card-cover` is a 21/9 (hero) or 16/9 box with
   object-fit:cover. That is right for a photograph and WRONG for a rendered
   artefact: line-oa's cover is the 6-tile Rich Menu (2500x1686, ratio 1.48),
   so 21/9 cropped the bottom row of tiles in half and left the badge floating
   in the crop. Same shape on the portfolio / dashboard screenshots used as
   covers on website and google-maps.
   A screenshot gets its own natural ratio and object-fit:contain; the scrim
   comes off, because a dark gradient over a UI screenshot reads as a render
   bug too. Photographs are untouched. */
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="richmenu"]),
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="portfolio-"]),
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="ads-dashboard"]),
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="gbp-panel"]),
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="maps-full"]),
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="seo-serp"]){
  aspect-ratio:auto;background:#FBFBF9;
}
body.zx.zx-deck .zx .zx-card-cover img[src*="richmenu"],
body.zx.zx-deck .zx .zx-card-cover img[src*="portfolio-"],
body.zx.zx-deck .zx .zx-card-cover img[src*="ads-dashboard"],
body.zx.zx-deck .zx .zx-card-cover img[src*="gbp-panel"],
body.zx.zx-deck .zx .zx-card-cover img[src*="maps-full"],
body.zx.zx-deck .zx .zx-card-cover img[src*="seo-serp"]{
  height:auto;object-fit:contain;
}
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="richmenu"])::after,
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="portfolio-"])::after,
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="ads-dashboard"])::after,
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="gbp-panel"])::after,
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="maps-full"])::after,
body.zx.zx-deck .zx .zx-card-cover:has(img[src*="seo-serp"])::after{display:none}

/* -- row 42 - THE ROW GETS ANGA'S PILL ----------------------------------
   feedback-002 fix #4: 0 of 34 .see-row carried a CTA, and ANGA's row ends
   its text column with a filled pill ("Get Free Strategy" in
   design/refs/anga-b2b-box-1920.png). `a.zx-btn` already computes
   18/28/700 Kanit at radius 9999 with min-height 50 (>=44), so the pill
   itself needs no new type — only its place in the row. */
body.zx.zx-deck .zx .see-row .see-cta{margin-top:26px;display:inline-flex}
body.zx.zx-deck .zx .see-txt > .see-cta:last-child{margin-bottom:0}
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row .see-cta{margin-top:20px;width:auto}
}
/* The converted rows carry the page's own section number in .see-num and the
   original block heading as the h3; nothing else in the text column changes. */
body.zx.zx-deck .zx .see-row .see-txt > :is(h3,.zx-h3):first-child{margin-top:0}
body.zx.zx-deck .zx .see-row .see-txt > .icon{display:none}

/* -- row 43 - case-mdcc's h1 WAS OFF-FAMILY -----------------------------
   feedback-002 fix #5: the case-study template's h1 has no `.zx-d` span, so
   the row-27 rule (which targets the span) never reached it and it rendered
   70/82.6 Anuphan while every service h1 renders 60/60 Kanit. Pinning the
   element itself on the case template rather than editing one page's markup. */
@media(min-width:861px){
  body.zx.zx-deck .zx article > h1{
    font-family:var(--zx-font-display),"Kanit",sans-serif;
    font-size:60px;line-height:60px;font-weight:700;
  }
}

/* -- row 44 - THE FIGURE COLUMN OF A CONVERTED ROW ----------------------
   The 20 pages converted this iteration reuse per-page photos that ship at
   16/9, 1/1 and 4/3. Left raw, a 1/1 photo makes a 585px-wide column 585px
   tall and the text column floats in the middle of it. ANGA's figure is a
   consistent landscape crop, so ours is pinned to 4/3 with object-fit:cover
   — the same treatment the .zx-card-cover photographs already get. */
body.zx.zx-deck .zx .see-row > a.see-fig img,
body.zx.zx-deck .zx .see-row > .see-fig > img{
  aspect-ratio:4/3;object-fit:cover;width:100%;height:auto;
}
body.zx.zx-deck .zx .see-row > a.see-fig{align-self:center}

/* -- row 45 - 390: A CONVERTED ROW IS ONE COLUMN, FIGURE SECOND ---------
   shared.css already collapses .see-row to 1fr below 861px and re-orders the
   text first. The converted rows add nothing to that; this only stops a 4/3
   figure from eating the whole first screen on a phone. */
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row > a.see-fig img,
  body.zx.zx-deck .zx .see-row > .see-fig > img{aspect-ratio:16/10}
}

/* -- row 46 - THE LAST RING ON THE SITE: .vs-col ------------------------
   A 24-page shadow sweep (gan-harness/anga-box/probe003.py) found 34 boxes
   still painting a FOUR-layer shadow whose first layer is a 1.5px amber ring:
     rgba(255,184,37,.22) 0 0 0 1.5px, rgba(28,35,33,.04) 0 1px 2px,
     rgba(28,35,33,.035) 0 8px 18px, rgba(28,35,33,.024) 0 26px 42px
   All 34 are `.vs-col` (the good/bad comparison columns) on google-maps x14,
   geo-aeo x2, case-mdcc x2 and the pages that share the component. Iteration
   002 removed the ring from `.cmp-us` and from the hero/offer boxes but never
   measured `.vs-col`, which is why it survived. Both reference specs name a
   ring-under-a-shadow as the cheap-vs-premium tell, so it goes.
   .vs-col is an SC19 component: ONE value on every page, no ground tier. */
body.zx.zx-deck .zx :is(.vs,.vs-col,.zx-vs-panel),
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.vs,.vs-col,.zx-vs-panel){
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}

/* -- row 47 - THE BORDER SURVIVORS THAT NEEDED !important ---------------
   Row 39 cleared most of them, and a second computed-style sweep found 26
   left: `.faq-item.is-lit` x7, `.zx-paper` x8, `.answer-box` x10, one `.blk`.
   Every one is set by a page-local or premium.css rule that carries
   `!important`, so a specificity lift alone cannot reach it. The comparison
   family is still excluded by name — those hairlines are the table's row
   rules, i.e. structure, and SC19 locks them. */
body.zx.zx-deck .zx :is(.faq-item,.faq > details,.zx-card,.zx-ticket,.zx-paper,.see-row,.see-fig,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.reason,.stepcard,.do-item,.sym,.art-phase-item,.phase-row,.wz-step,.km-cause,.transp-item,.p-split-copy,.zx-step,.svc-card,.engine-row,.answer-box,.answer-block,.blk,.zx-tile,.mech-card,.stat-card,.price-card):not(.cmp-us):not(.cmp-vs):not(.cmp-lbl):not(.cmp-hd):not(.cmpbox):not(.cmpgrid){
  border-width:0!important;border-style:none!important;
}

/* -- row 48 - ONE LINE-HEIGHT PER BODY SIZE -----------------------------
   feedback-002 Axis 3: "the body-size tail is 8 per page, not the claimed 4",
   measured as size/line-height PAIRS. Re-measured on all 24 pages today the
   pairs were 4-9 per page and the extra ones are all line-height drift on the
   same few sizes — 16/24 (the ladder) plus 16/28.8, 16/28, 16/25.6, 14/24,
   14/22.4, 14/24.5, 14/23.1, 14/25.2, 12/24, 12/18.6, 12/44. One
   line-height per size collapses that tail without touching a single word.
   `!important` is required, not preferred: the drift comes from page-local
   and premium.css rules that already carry it.
   FOUR body sizes remain, and they are named here so the claim is checkable:
     14/21 small print · 16/24 body · 20/32 lead · 28/44.8 section lead.
   The 18/32.4 pair inside `.cmpbox` is the anchor's own gate-locked value and
   is deliberately restored below — it is a fifth pair, and it is SC19's, not
   ours to collapse.
   The exclusion list is not decoration: several pages ship a HEADING as a
   `<p class="zx-h3">` (google-maps' feature rows do exactly that), and a
   price line as a `<p class="zx-price">`. Those are display, not body, and
   sweeping them to 16px would be a regression, not a collapse. */
/* The exclusions sit inside :where() so they contribute ZERO specificity.
   Written bare, fifteen :not() clauses make this rule (0,16,1) and it then
   out-ranks the small-print and lead rules below it — measured: .foot-entity
   stayed 16/25.6 on 20 pages until this was fixed. */
body.zx.zx-deck .zx :is(p,li,dd,td):where(:not(.cmp-lbl):not(.cmp-us):not(.cmp-vs):not(.zx-h1):not(.zx-h2):not(.zx-h3):not(.h2):not(.h3):not(.zx-d):not(.zx-price):not(.hero-price):not(.zx-card-price):not(.zx-quote):not(.stat):not(.zx-bn)){
  font-size:16px!important;line-height:24px!important;
}
body.zx.zx-deck .zx :is(p,li,dd,td):is(.zx-small,.srcline,.foot-entity,.byline,.photo-cap,.dv-src,.dv-sub,.price-note,.zx-eyebrow,.zx-card-sub,.see-hint,.gm-ans__sublab,.dv-tile-src,.zx-t-m,.caption,.note-s,.small){
  font-size:14px!important;line-height:21px!important;
}
/* DESKTOP ONLY. Written unmediated, these two rules also fired at 390 and
   shrank the hero copy, which pushed the hero fold under the 40% floor:
   measured restaurant 34.8%, clinic 29.5%, services 38.5% at 390x844. Below
   861px the kit's own mobile step owns the lead sizes. Same trap iteration
   002 hit with the h1/h2 pair, same fix. */
@media(min-width:861px){
  body.zx.zx-deck .zx :is(p,li,dd,td):is(.zx-body,.sub,.art-lead,.gm-ans__lead,.zx-sub){
    font-size:20px!important;line-height:32px!important;
  }
  body.zx.zx-deck .zx :is(p,li,dd,td).zx-lead{font-size:28px!important;line-height:44.8px!important}
}
/* SC19 keeps the anchor's comparison pair. Untouched by the sweep above. */
body.zx.zx-deck .zx .cmpbox :is(p,li,dd,td),
body.zx.zx-deck .zx .cmpgrid :is(p,li,dd,td){font-size:18px!important;line-height:32.4px!important}
/* Inside a feature row the ladder is ANGA's 16/24 regardless of the class the
   original block carried, because the row IS the body copy. */
body.zx.zx-deck .zx .see-row .see-txt :is(p,li,dd):not(.zx-small):not(.srcline):not(.see-hint):not(.zx-h3):not(.zx-h2):not(.see-pain):not(.zx-lead){
  font-size:16px!important;line-height:24px!important;max-width:62ch;
}

/* -- row 49 - AN h1 WITHOUT A .zx-d SPAN IS STILL AN h1 -----------------
   Re-measured: case-mdcc's h1 sits in `div.wrap`, not in an `article`, so
   row 43 never reached it and it still rendered 70/82.6 Anuphan. Keyed off
   the span's ABSENCE instead of the ancestor, which is the actual condition. */
@media(min-width:861px){
  body.zx.zx-deck .zx h1:not(:has(.zx-d)),
  body.zx.zx-deck .zx h1:not(:has(.zx-d)) *{
    font-family:var(--zx-font-display),"Kanit",sans-serif!important;
  }
  /* SIZE is NOT set here. ops/loop_verify.py's type_scale check reads the h1
     ELEMENT and requires 70px at 1440 — on the service pages the element is 70
     and the `.zx-d` span inside it is 60, which is what "h1 60/60" means. So
     case-mdcc gets the span in its markup (one wrapped headline) and this rule
     only fixes the FAMILY, which no span can fix on a page that has none. */
}

/* -- row 50 - THE SECOND 7% SHADOW ---------------------------------------
   The census carries `rgba(11,31,42,.07) 0 4px 50px` x93 next to
   `rgba(0,0,0,.07) 0 4px 50px` — visually the same shadow written twice, on
   the page-local card classes the ground tier does not name. Folding them
   onto the black value takes the census from 5 distinct soft-card shadows to
   4 without moving a pixel anyone can see. */
body.zx.zx-deck .zx :is(.boxed,.mdcc-card,.self-check,.ba-card,.lo-qr,.zp-tcard,.stat-tile,.stat-callout,.proof-link,.final-band,.cta-box,.chat-demo,.proof,.zp-usa,.tbl-scroll,.caveat,.tmile,.zp-blemish,.svc-cta-band),
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.boxed,.mdcc-card,.self-check,.ba-card,.lo-qr,.zp-tcard,.stat-tile,.stat-callout,.proof-link,.final-band,.cta-box,.chat-demo,.proof,.zp-usa,.tbl-scroll,.caveat,.tmile,.zp-blemish,.svc-cta-band){
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}

/* -- row 51 - THE BREADCRUMB IS A CONTROL --------------------------------
   Measured at 390 on chatbot: `.breadcrumb a` ("หน้าแรก") renders 41x44 —
   4 short of the 44px tap floor that ops/loop_verify.py enforces. It is a
   link people actually press on a phone, so it gets a control's target.
   Header/footer markup is byte-locked; the breadcrumb is not part of it. */
@media(max-width:860px){
  body.zx.zx-deck .zx .breadcrumb a{
    display:inline-flex;align-items:center;justify-content:center;
    min-width:44px;min-height:44px;padding-inline:6px;
  }
}

/* -- row 52 - PARAGRAPH AIR INSIDE A CONVERTED ROW ----------------------
   The blocks converted this iteration often carry two paragraphs
   ("สาเหตุ:" then "สิ่งที่เราทำ:"). shared.css ships `.see-txt p{margin:0}`
   for the single-paragraph rows it was written for, so the two ran together
   into one 6-line slab. ANGA's row separates them. */
body.zx.zx-deck .zx .see-txt > p + p,
body.zx.zx-deck .zx .see-txt > ul,
body.zx.zx-deck .zx .see-txt > ol{margin-top:14px}
body.zx.zx-deck .zx .see-txt > :is(ul,ol){padding-left:1.2em}

/* -- row 53 - THE TWO SC19 REDS ROW 38 CREATED, CLOSED -------------------
   Measured by ops/shane_check.py after row 38 landed, and fixed here rather
   than by reverting, because the row-38 behaviour is the one feedback-002
   asked for:

   (a) `.blk boxShadow` split. Row 22's `.lit x13 :is(...).zx-gd` is (0,15,1);
       row 38's `.lit x13 :is(...)` is (0,14,1), so the anchor's DARK-ground
       .blk kept .28 while every light-ground .blk took .07 — and SC19 compares
       this property with zero tolerance across pages. The two selectors below
       are (0,15,1) and come later in the file, so `.blk` is ONE value on
       every page again, and the value is ANGA's 7%.
   (b) `.blk paddingLeft` on chatbot rendered 24 against the anchor's 32,
       because chatbot's surviving `.blk` is nested inside a `.step` that
       supplies its own padding. The anchor's own 30/32 is pinned site-wide;
       on the anchor this is a no-op by construction. */
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.faq-item,.faq > details,.blk).zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.faq-item,.faq > details,.blk).zx-gl{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}
body.zx.zx-deck .zx .blk{padding:30px 32px!important}
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.blk{padding:30px 32px!important}
/* NOT scoped away inside a .see-row: why-zipai nests a .blk in three of its
   converted rows and a row-scoped padding:0 is exactly the SC19 red (a) above.
   The .blk keeps the anchor padding there and reads as a quiet inset. */

/* == HOTFIX 003b =========================================================
   feedback-003's ship blocker + fixes #2 and #5. Every number below is from
   gan-harness/anga-box/geom003b.py and ground003b.py, run against ea0fc22c.
   ======================================================================== */

/* -- row 54 - A CONTAINER THAT HOLDS A FEATURE ROW IS SINGLE-COLUMN ------
   THE SHIP BLOCKER. probe003.py measured `width` and never `x`, so a 1240px
   .see-row sitting inside a 426px grid TRACK reported "1240 OK" while three
   of them stacked on the same y and ran off the left edge of the window:

     page                    parent grid-template-columns   x @1440
     index (#how)            426px 426px 426px              -362 / 100 / 562
     ku-mud-google-maps      426px 426px 426px              -362 / 100 / 562
     ai-agency-thailand      668px 668px                    -241 / 441 / -241
     ai-visibility           561px 561px                    -216 / 417

   The converter dropped rows into blocks that were already multi-column
   grids (`.zx-steps`, `.blkgrid`, `.p-split`). ANGA's feature rows are never
   in a grid track — they are a vertical stack with 60px between them. So any
   container that DIRECTLY holds a .see-row stops being a grid, and the rows
   supply their own 60px rhythm. Kit-only; no page markup carries this. */
body.zx.zx-deck .zx *:has(> .see-row){
  display:block!important;
  column-count:auto!important;
}
/* `grid-template-columns:none!important` used to sit in the block above as
   belt-and-braces. It changed nothing (a `display:block` element ignores the
   property) but it DID change what the property REPORTS, and SC18 compares the
   reported value against the shared kit: measured 2026-09-05 on
   ai-visibility [390px], `section.p-split.reveal` and `section.p-split--rev`
   read "none" where premium.css @media mobile declares "1fr" — a silent
   override of a shared responsive block, which is exactly what SC18 exists to
   catch. Dropping the declaration restores the reported "1fr" and leaves the
   used layout identical, because `display:block` is what removes the track.
   Re-measured with geom003b.py at 1280/1440/1920/390 after the change. */
body.zx.zx-deck .zx .see-row{
  grid-column:1 / -1;
}
/* max-width is deliberately NOT clamped to 100% here. Row 13 widens the row to
   1240 with `margin-inline:calc((100% - 1240px)/2)`, which goes NEGATIVE inside
   a narrower container and lets the row burst out SYMMETRICALLY — that is how
   about/chatbot/why-zipai (900/808/860px wrappers) render at ANGA's 1240 and
   still measure x=340 right=1580 at 1920. A 100% clamp fixed the overlap but
   cost 8 pages their 1240 (measured: about 900, chatbot 808, why-zipai 860).
   The blocker was never the burst; it was bursting out of an OFF-CENTRE GRID
   TRACK, and row 54's `display:block` is what removes the track. */
/* ANGA's 60px between rows. The parent's grid `gap` died with its `display`,
   and `.see-row` ships `margin:0 auto` (shared.css:1336), so without this the
   rows butt into each other. Applied to BOTH neighbours of a row so a row
   never touches a heading or a paragraph either. */
body.zx.zx-deck .zx *:has(> .see-row) > .see-row + *,
body.zx.zx-deck .zx *:has(> .see-row) > * + .see-row{margin-top:60px}

/* -- row 55 - THE GROUND TIER REACHES .answer-box / .answer-block / .zx-arrow
   feedback-003 fix #2. Row 38's `:is()` list stopped at .faq-item and .blk,
   so 22 cards (evaluator counted 11 with details closed; 22 with every
   <details> forced open) still painted 0.25-0.28 black on a ground they
   measure 1.00-1.05 against: line-oa-guide x3, why-zipai, case-mdcc x2,
   about, index, ku-mud-google-maps, medical-tourism, nail-salon, restaurant.

   .answer-box is an SC19_COMPONENT and boxShadow is in SC19_EXACT, so it
   takes ONE value site-wide — ANGA's own 0 4px 50px rgba(0,0,0,.07) — exactly
   the treatment row 38 (a) had to give .faq-item and .blk. The FILL still
   changes per ground; backgroundColor is not an SC19 property.
   .answer-block and .zx-arrow are not gate-locked, so they get the real
   two-value tier. zx-deck.js's CARDS list gains .zx-arrow in the same pass —
   a class the walker never saw could not be stamped .zx-gl/.zx-gd at all. */
body.zx.zx-deck .zx :is(.answer-box).zx-gl,
body.zx.zx-deck .zx :is(.answer-box).zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-box),
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-box).zx-gl,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-box).zx-gd{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}
body.zx.zx-deck .zx :is(.answer-block,.zx-arrow).zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-block,.zx-arrow).zx-gd{
  box-shadow:0 4px 50px rgba(0,0,0,.28);
}
body.zx.zx-deck .zx :is(.answer-block,.zx-arrow).zx-gl,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-block,.zx-arrow).zx-gl{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}
body.zx.zx-deck .zx :is(.answer-box,.answer-block,.zx-arrow).zx-gl{background-color:#FFFFFF}
body.zx.zx-deck .zx :is(.answer-box,.answer-block).zx-gd{background-color:#FBFBF9}
/* The (0,15,1) `.lit` trap from zx-system.css:417 again — without the repeat,
   six paper-ground .answer-box kept their own #F4F5F4 / #FBFBF9 fill and
   measured 1.00-1.05 against the band even with the shadow corrected. */
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-box,.answer-block,.zx-arrow).zx-gl{background-color:#FFFFFF}
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-box,.answer-block).zx-gd{background-color:#FBFBF9}
/* line-oa-guide:143 and the geo-aeo/ku-mud/why-zipai page blocks set this fill
   with `!important`, so a specificity lift alone cannot reach it — the same
   condition row 47 hit on the borders. backgroundColor is NOT an SC19 property,
   so an !important fill is safe where an !important shadow would not be. */
body.zx.zx-deck .zx :is(.answer-box,.answer-block,.zx-arrow).zx-gl{background-color:#FFFFFF!important}
body.zx.zx-deck .zx :is(.answer-box,.answer-block).zx-gd{background-color:#FBFBF9!important}

/* -- row 56 - ALTERNATION IS A CLASS, NOT A DOM ACCIDENT -----------------
   feedback-003 fix #5. shared.css:1339 alternates with `:nth-of-type(even)`,
   which counts EVERY <div> sibling, not every row — and a row that is an only
   child of its own `.see-rows` is never "even". Measured L/R sequences at
   ea0fc22c: google-maps LRLLLLLRRLLL, line-oa LLLRLLLRLRLR, services LLLLLL,
   case-mdcc LLLL. `.see-row--flip` is now the single source of truth and
   gan-harness/anga-box/alternate_rows.py stamps it by DOM index on every
   page, so the class and the render cannot disagree.
   Scoped to >=1181px on purpose: below that shared.css:1424 collapses the row
   to one column with the TEXT ALWAYS FIRST, and an order:1 figure there would
   put the photo above the headline on a phone. */
@media(min-width:1181px){
  /* iteration 006: the blueprint's 585+70+585 = 1240 EXACTLY (blueprint 3.1).
     495/581+70 = 1146 inside a 1096 content box overflowed its own right
     padding by 50px - the grid bug blueprint 6.1 calls out. */
  body.zx.zx-deck .zx .see-row:not(.see-row--flip){grid-template-columns:585px 585px}
  body.zx.zx-deck .zx .see-row:not(.see-row--flip) .see-txt{order:1}
  body.zx.zx-deck .zx .see-row:not(.see-row--flip) :is(.see-fig,.see-slot,.dv){order:2}
  body.zx.zx-deck .zx .see-row.see-row--flip{grid-template-columns:585px 585px}
  body.zx.zx-deck .zx .see-row.see-row--flip .see-txt{order:2}
  body.zx.zx-deck .zx .see-row.see-row--flip :is(.see-fig,.see-slot,.dv){order:1}
}

/* -- row 57 - 27.5px OF DEAD LEADING UNDER EVERY h1 ---------------------
   feedback-003, Axis 3: the h1 ELEMENT computes 70px/87.5px on 23 of 24
   pages around a 60px `.zx-d` span, so every hero carries 27.5px of leading
   under a headline that is already 10px smaller than its own line box.
   font-size is NOT touched — ops/loop_verify.py's `type_scale` reads the h1
   ELEMENT and requires 70px at 1440, which is why iteration 003 could not
   fix this by sizing. A UNITLESS 1.0 is inherited as a FACTOR, so the 60px
   span resolves to 60/60 (ANGA's service-page value) while the element stays
   70px wide in the gate's eyes. 70/87.5 is not reintroduced anywhere.
   case-mdcc: its h1 has a .zx-d span but no `.zx-h1` class, so zx-deck.css:357
   (70px) and :2341 (60px, keyed on `.zx-h1`) both miss it and it measured
   70/82.6. The span is pinned to 60/60 here by the ABSENCE of .zx-h1. */
@media(min-width:861px){
  body.zx.zx-deck .zx h1,
  body.zx.zx-deck .zx .zx-h1,
  body.zx.zx-deck .zx .zx-h1 .zx-d{line-height:1.0}
  body.zx.zx-deck .zx h1:not(.zx-h1) > .zx-d{font-size:60px;line-height:1.0}
}

/* -- row 58 - THE COMPARISON BLOCK IS ANGA'S CLEAN LINE -----------------
   Owner order 2026-09-05: "for the box make it look like anga clean line".
   MEASURED on https://anga.co.th/ at 1920 (gan-harness/anga-box/measure_anga004*.py,
   raw dumps anga004-measured.json / anga004-grid.json), block
   "แองก้าแตกต่างอย่างไร":

     grid            1080 wide = 3 EQUAL 360px columns, gap 0, centred
     header band     160px (label col pt-40; them col pt-24 + a 136px "They" cell)
     data rows       128px (h-32); one long row runs 160px (h-40)
     divider         border-TOP 1px solid oklch(.92 0 0) = #E5E5E5, top edge only,
                     NOT on the first data row. No cell border, no cell shadow.
     cell padding    32px (label p-8; us/them px-8)
     label type      18px / 28px / 700, left aligned, near-black
     body type       16px / 22px / 400, centred
     them header     40px / 60px / 700 (their word is "They", 4 glyphs)
     us column       ONE white card, radius 20, z-index 10, spanning the whole
                     grid height so it overhangs the row grid at BOTH ends
     us glow         filter:drop-shadow(rgba(254,76,6,.2) 0 2px 16px)
                            drop-shadow(rgba(244,27,50,.1) 0 -6px 26px)
                            drop-shadow(rgba(255,84,1,.05) 0 7px 30px)
     side columns    plain (ANGA puts a 0 4px 200px rgba(0,0,0,.17) paper card
                     under them; Shane's order is "label column plain", so ours
                     stay on the band)
     check icon      23x23, block, centred, 8px above the line
     heading         50/55.56/700 centred, 72px to the grid

   OURS: the same numbers, except the grid stays at the anchor's LOCKED 1012
   (1fr/1fr/1fr = 337.33 each - Shane has not confirmed widening to 1240), and
   the glow is the kit's sky/mint, never orange.

   The card is a `.cmpwrap::before`, not a per-cell background: the markup is a
   FLAT grid of 3 cells per row (anchor markup is untouched by order), so any
   per-cell white would seam at every row line and could not overhang. Cells go
   transparent and stack above it via `.cmpgrid{position:relative;z-index:1}`.
   `left`/`width` resolve against .cmpwrap's 1040 padding box: (100% - 28px)/3
   is exactly one grid column, and 14px is .cmpwrap's own padding.
   Cell colour is lifted to ink at (0,14,1) because zx-deck.js marks these DIVs
   .zx-t-d (0,13,1) and ANGA's label/us copy is near-black, not grey; .cmp-them
   is deliberately left to the marker's --zx-dim, which is the "grey-muted" the
   order asks for and measures 7.0+ on the band. */
@media(min-width:861px){
  body.zx.zx-deck .zx .cmpgrid{grid-template-columns:1fr 1fr 1fr;position:relative;z-index:1}
  body.zx.zx-deck .zx .cmpgrid>div{
    border:0;border-radius:0;box-shadow:none;background:transparent}
  /* row 23 put ANGA's 0 4px 200px rgba(0,0,0,.17) on every .cmp-us CELL through
     the (0,17,1) `.lit` trap (zx-system.css:417). Iteration-004 moves that
     shadow onto the CARD, where ANGA actually has it, so the cells must lose
     it - and only a (0,17,2) selector can reach past row 23. Same for the 20px
     radius the halo rows left on each cell: the radius belongs to the card.
     .cmpgrid's own 0 4px 50px rgba(0,0,0,.28) goes too - it is a dark shadow
     cast by a transparent box, which is exactly the 'no dark cell shadow' the
     order names. Uniform from the kit, so SC19's EXACT boxShadow compare still
     sees one value on every page. */
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .cmpgrid>div,
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.cmpgrid>div{
    box-shadow:none;border-radius:0;background-color:transparent}
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .cmpgrid,
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.cmpgrid,
  body.zx.zx-deck .zx .cmpgrid{box-shadow:none}
  body.zx.zx-deck .zx .cmpgrid>div:nth-child(n+7){border-top:1px solid #E5E5E5}
  body.zx.zx-deck .zx .cmpgrid .cmp-lbl,
  body.zx.zx-deck .zx .cmpgrid .cmp-us,
  body.zx.zx-deck .zx .cmpgrid .cmp-them{padding:20px 32px;min-height:128px}
  body.zx.zx-deck .zx .cmpgrid .cmp-hd{padding:24px 32px!important;min-height:160px}
  body.zx.zx-deck .zx .cmpgrid .cmp-lbl{
    font-size:18px;line-height:28px;font-weight:700;text-align:left}
  body.zx.zx-deck .zx .cmpgrid .cmp-us,
  body.zx.zx-deck .zx .cmpgrid .cmp-them{font-size:16px;line-height:22px;font-weight:400}
  body.zx.zx-deck .zx .cmpgrid .cmp-hd.cmp-them{font-size:26px;line-height:1.3;font-weight:700}
  body.zx.zx-deck .zx .cmpgrid .cmp-hd.cmp-us{font-size:26px;line-height:1.3;font-weight:700}
  body.zx.zx-deck .zx .cmpgrid .cmp-us .cmp-tick{
    width:23px;height:23px;line-height:23px;margin:0 auto 8px;box-shadow:none}
  body.zx.zx-deck .zx .cmpbox .cmpgrid img.cmp-logo-img{
    height:44px;width:44px;border-radius:10px;margin-right:10px}
  body.zx.zx-deck .zx .cmpbox h2{margin-bottom:72px}
  body.zx.zx-deck .zx .cmpwrap{position:relative}
  body.zx.zx-deck .zx .cmpwrap::before{
    content:"";position:absolute;z-index:0;pointer-events:none;
    top:-24px;bottom:-24px;
    left:calc(14px + (100% - 28px) / 3);
    width:calc((100% - 28px) / 3);
    background:#FFFFFF;border-radius:20px;
    /* ANGA's alphas are .20/.10/.05 on an orange that is already loud against a
       pure-white band. Ours is a sky/mint on a #F4F5F4 band, so the same alphas
       measured invisible in the first render - lifted to .30/.16/.09, which is
       where the card separates from the band at 1920 without becoming a halo. */
    filter:drop-shadow(0 2px 18px rgba(62,142,247,.30))
           drop-shadow(0 -6px 28px rgba(24,184,122,.16))
           drop-shadow(0 8px 34px rgba(62,142,247,.09));
  }
}
/* Below 861 the kit already collapses .cmpgrid to ONE column (row 26 block), so
   there is no card and no three-column rhythm to copy - only the clean line:
   hairline dividers, no green side rails, no dark cell shadow. Every selector
   is scoped under body.zx.zx-deck .zx, so SC18's "unscoped override below
   1181px" rule is satisfied. */
@media(max-width:860px){
  body.zx.zx-deck .zx .cmpgrid>div{border-left:0;border-right:0;border-bottom:0;box-shadow:none}
  body.zx.zx-deck .zx .cmpgrid>div:nth-child(n+7){border-top:1px solid #E5E5E5}
  body.zx.zx-deck .zx .cmpgrid .cmp-us{background:#FFFFFF}
  body.zx.zx-deck .zx .cmpgrid .cmp-us.first{border-radius:14px 14px 0 0}
  body.zx.zx-deck .zx .cmpgrid .cmp-us.last{border-radius:0 0 14px 14px}
  body.zx.zx-deck .zx .cmpgrid .cmp-us .cmp-tick{box-shadow:none}
}
/* colour lift - above zx-deck.js's ground marker (0,13,1) */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .cmpgrid .cmp-lbl,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .cmpgrid .cmp-us{
  color:var(--zx-ink)!important}

/* -- row 59 - THE KEY WORD IS A DIFFERENT COLOUR ------------------------
   Owner order 2026-09-05: "when wording use different color in every
   important word to make it stand out". ANGA does exactly this and only this:
   every heading on their homepage carries ONE coloured phrase -
   `แองก้า<span class="text-gradient">แตกต่างอย่างไร</span>` - filled with
   `linear-gradient(88deg,#F41B32 .93%,#FF5401 64.19%)` through
   `background-clip:text`. Measured 25 such headings on their homepage.

   Ours is the same MOVE in our palette: sky/mint, never orange. It is a SOLID
   fill, not a clipped gradient, and that is a measurement decision, not taste:
   zx-deck.js's ground marker (pass 1) tags anything whose computed
   background-clip is `text` as .zx-gradtext and then SKIPS it in pass 2, so a
   clipped .zx-key would lose the .zx-t-w mark that tells it which ground it is
   standing on - and the two grounds need opposite ends of the ramp:

     paper / cream ground   --zx-sky-ink #1A5CBE   5.86 on #FAF6EC, 5.79 on
                            #F2F0E7, 6.33 on #FFFFFF   (ops/loop_verify.py
                            wants >= 5.0 on an UNSAMPLED row - the 4.5 WCAG bar
                            only applies to sampled gradient/image grounds)
     ink / ink-deep ground  #8FE3C4                11.22 on #0B1F2A,
                            12.28 on #061520
     .zx-grad band          #FFFFFF - the band is a SAMPLED gradient ground
                            (bar 4.5) and the best mint we have measures 4.20
                            against its #1A5CBE stop, so the key word there
                            keeps the white it already has and stands out on the
                            .zx-key underline bar alone.

   .zx-key keeps its gradient underline (rows 446-455) - that mark is
   decoration, nothing sits on it, and it is what makes the coloured word read
   as deliberate rather than as a stray link.
   .zx-kw is the in-paragraph version: ONE phrase per paragraph, bold, added by
   gan-harness/anga-box/accent_words.py only where the author had already marked
   it (<strong>/<b>) or where the phrase is a price or a hard number.
   Specificity is the ground marker's own (0,13,1), declared later in this file,
   with the .zx-t-w variant at (0,14,1) so a dark ground always wins. */
/* MEASURED CORRECTIONS from this iteration's own verifier runs:
   1. In the V3 ramp EVERY band is navy - .zx-cream is #0F2433 and .zx-alt is
      #15303F (rows 2129-2132), not paper. The first pass assumed paper and
      loop_verify read 2.17:1 on 9 rows of medical-tourism.
   2. Keying off the .zx-t-* marker was not enough either: that marker only
      reaches text whose ground is a marked BOX, so a heading on a bare paper
      section comes back unmarked and inherited the dark-ground mint on a
      #FBFBF9 ground - 1.45:1 on 4 rows of case-mdcc and 1 of chatbot.
   The ground is now resolved in zx-deck.js (accent-word ground pass, added
   this iteration) exactly the way the card tier resolves its own: walk to the
   first ancestor with an opaque fill, gradient stops included, and stamp
   .zx-a-lt / .zx-a-dk / .zx-a-gr. The default below is the dark value because
   the ramp is navy; the stamp is what actually decides. */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-key,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-kw{color:#8FE3C4!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-kw{font-weight:700}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-key.zx-a-lt,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-kw.zx-a-lt{color:var(--zx-sky-ink)!important}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-key.zx-a-dk,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-kw.zx-a-dk{color:#8FE3C4!important}
/* the CTA gradient band is a SAMPLED ground (bar 4.5) and our best mint
   measures 4.20 against its #1A5CBE stop, so the key word keeps its white */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-key.zx-a-gr,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-kw.zx-a-gr,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-sec.zx-grad .zx-key,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-sec.zx-grad .zx-kw{color:#FFFFFF!important}
body.zx.zx-deck .zx .zx-key.zx-key--wrap{white-space:normal}


/* -- row 60 - A PAPER BOX TAKES THE PAPER SHADOW, WHEREVER IT STANDS ----
   SC19 caught 2 items on each of ai-visibility, google-ads and seo this
   iteration: their `.answer-block.answer-box` renders
   `rgba(0,0,0,.28) 0 4px 50px` while the anchor's `.answer-box` renders
   `rgba(0,0,0,.07) 0 4px 50px`, and boxShadow is an EXACT compare.
   MEASURED cause (probe_ab.py, 1440): all four boxes fill #FBFBF9 / #FFFFFF and
   all four are marked `.zx-paper` - but the ground TIER (zx-deck.js, .zx-gl /
   .zx-gd) reads the BAND under the box, not the box, so the three that sit on a
   navy band came back `.zx-gd` and took the dark alpha. A 28% black shadow on a
   paper box is the exact smear feedback-001 flagged, so the tier is wrong here
   on both counts.
   `.zx-paper` is set by the marker from the element's OWN opaque fill, so
   `.zx-gd.zx-paper` means "dark band, light box" - the one case that must take
   the light shadow. Specificity is row 55's own shape plus that one class
   (0,19,1), so no !important is needed and row 55 keeps every other case. */
body.zx.zx-deck .zx :is(.answer-box,.answer-block,.zx-arrow).zx-gd.zx-paper,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit:is(.answer-box,.answer-block,.zx-arrow).zx-gd.zx-paper,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit :is(.answer-box,.answer-block,.zx-arrow).zx-gd.zx-paper{
  box-shadow:0 4px 50px rgba(0,0,0,.07);
}


/* -- row 61 - THE DESIGNED VISUAL CARD IN THE FIGURE COLUMN --------------
   Iteration 005 replaces the figure of all 93 `.see-row`s with a rendered
   ANGA-style visual card (landing/img/vis/, built by
   gan-harness/anga-box/visuals/). The card is COMPOSED at 3:2 - cropping it
   is not like cropping a photograph, it cuts the artefact - so it must not
   inherit row 44's 4/3 `object-fit:cover` or row 45's 16/10 mobile step.
   Below 861px the <picture> serves the 780x780 square variant, so the ratio
   has to follow the file that is actually being fetched.
   Specificity: row 44 is (0,5,3); naming the figure ancestor AND the .vis
   class makes this (0,6,3), so no !important is needed. */
body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) picture{
  display:block;width:100%;
}
body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) img.vis{
  aspect-ratio:3/2;object-fit:cover;width:100%;height:auto;
  border-radius:16px;background:#0B1F2A;
}
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) img.vis{
    aspect-ratio:1/1;
  }
}


/* ═══════════════════════════════════════════════════════════════════════
   ITERATION 006 — THE FEATURE BOX PER design/ANGA-BLUEPRINT-2026-09-05.md
   Shane, third time: "wording and visual are in the SAME box — copy it."
   Every number below is quoted from the blueprint's §3.1 / §8.1 measurement
   of https://anga.co.th/seo/ (2026-09-05). Nothing here is estimated.
   ═══════════════════════════════════════════════════════════════════════ */

/* -- row 62 · ZERO-PAD CARD, TEXT INSET LIVES ON THE INFO COLUMN ---------
   Blueprint §3.1: the card itself has `padding:0`; the 72px inset belongs to
   `.info` alone (`72px 0 72px 72px` img-right, mirrored img-left). Ours shipped
   `64px 72px` on the CARD (kit rows 12 + 25) — measured as the ROOT CAUSE of
   Shane's complaint in §6.1: with padding on the card the photo can never
   reach an edge, so the box reads as "a picture in a frame" instead of a
   composed block.
   Grid `585px 585px` gap 70 = 1240 EXACTLY (§3.1 note). Ours was 495/581+70
   = 1146 inside a 1096 content box, i.e. a 50px self-overflow.
   `overflow:hidden` + radius 30 is what clips the photo into the corner.
   `align-items:stretch` (row 25 set `center`) is what lets the figure cell be
   as tall as the card, which is the precondition for the bleed.
   min-height 498px = ANGA's own shortest measured card (497.8), and it is
   what makes the image cell land on their 585x498 = 1.175 ratio. */
@media(min-width:1181px){
  body.zx.zx-deck .zx .see-row{
    padding:0;
    grid-template-columns:585px 585px;
    gap:70px;
    align-items:stretch;
    min-height:498px;
    overflow:hidden;
  }
  body.zx.zx-deck .zx .see-row:nth-of-type(even),
  body.zx.zx-deck .zx .see-row.see-row--flip{grid-template-columns:585px 585px}
  /* the info column carries the whole inset — §3.1 "Text column .info" */
  body.zx.zx-deck .zx .see-row > .see-txt{
    display:flex;flex-direction:column;align-items:flex-start;justify-content:center;
    padding:72px 0 72px 72px;
  }
  body.zx.zx-deck .zx .see-row:nth-of-type(even) > .see-txt,
  body.zx.zx-deck .zx .see-row.see-row--flip > .see-txt{padding:72px 72px 72px 0}
  body.zx.zx-deck .zx .see-row > .see-txt > div{width:100%}
}

/* -- row 63 · THE IMAGE BLEEDS TO THREE CARD EDGES -----------------------
   Blueprint §3.1 "Image column .pic": box = 585 x FULL CARD HEIGHT,
   `object-fit:cover`, and — the line that matters — `border-radius` on the
   img is 0px. The image has NO radius of its own; the card's
   `overflow:hidden` + `border-radius:30px` does the clipping.
   Measured gap to the card edge on their box: top 0, right 0, bottom 0
   (mirrored on img-left). Ours measured 64 / 22 / 64 (§6.1) because
   kit rows 15/25/30/61 gave the figure its own radius 20 and the img radius
   16, and row 61 pinned a 3/2 aspect-ratio panel instead of filling the cell.
   Rows 25/30/61 are superseded here for `.see-row` only; the same classes
   outside a `.see-row` keep their old treatment. */
@media(min-width:1181px){
  /* dataviz.css:66 caps `.dv` at `max-width:581px` ("funnel spec 3 see-row
     figure column") - the PRE-blueprint column width. Measured: four rasters
     rendered 581 wide inside a 585 cell, i.e. a 4px gap at the card edge. */
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv){
    position:relative;display:block;height:100%;width:100%;max-width:none;
    align-self:stretch;
    border-radius:0;overflow:hidden;padding:0;margin:0;background:transparent;
  }
  /* zx-deck.css:2198 ships `.dv.see-fig[style]{background:...!important;
     padding:14px;border-radius:var(--zx-r-2)}` for a dataviz figure that is
     NOT inside a card. Measured on google-maps: that 14px inset was the last
     thing keeping six rasters off the card edge (bleed 14/673/625/14).
     Inside a `.see-row` the CARD is the frame, so the figure must have none. */
  /* hoisted below the media block so it also runs at 390 */
  /* MEASURED on google-maps: ten rows put the raster three levels down —
     `.dv.see-fig > .dv-fig-inner(max-width:581px) > picture > img.vis`. With
     `height:100%` on the img alone the chain resolves to ZERO (auto-height
     ancestors), and the 581px cap is the pre-blueprint column width. Measured
     bleed before this rule: top 0 / right 659 / bottom 639.72 — i.e. an image
     with no height at all. The whole chain is collapsed, but ONLY inside a
     figure that actually holds a raster (`:has(img.vis)`), so the 18 pure DOM
     dataviz panels keep their own internal layout untouched. */
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > *:not(.see-hint):not(p):not(figcaption):not(.dv-src),
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) :is(.dv-fig,.dv-fig-inner,picture,figure){
    display:block;height:100%;width:100%;max-width:none;
    margin:0;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;
  }
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) picture{
    display:block;width:100%;height:100%;
  }
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) img.vis,
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) > img,
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) picture > img{
    display:block;width:100%;height:100%;max-width:none;
    aspect-ratio:auto;object-fit:cover;object-position:50% 50%;
    border-radius:0;
  }
  /* A `.dv` figure is a DOM dataviz, not a raster: its bars, tiles and labels
     ARE page text (SC10), so it cannot be swapped for a photo. It gets the same
     BLEED instead — the panel itself fills the cell to three card edges and
     carries the glow field the rendered cards use, so the row reads identically.
     Stated openly in generator-state.md rather than counted as an image. */
  body.zx.zx-deck .zx .see-row > .dv,
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot):has(.dv){
    padding:40px 36px;
    background:
      radial-gradient(120% 118% at 22% 12%,rgba(96,166,255,.30) 0%,rgba(52,128,238,.16) 34%,rgba(11,31,42,0) 76%),
      radial-gradient(104% 108% at 88% 92%,rgba(38,214,145,.24) 0%,rgba(20,168,112,.13) 30%,rgba(11,31,42,0) 74%),
      linear-gradient(150deg,#15303F 0%,#0F2433 52%,#061520 100%);
    display:flex;flex-direction:column;justify-content:center;
  }
}
/* the "open full size" affordance is ours, not ANGA's; it stays, pinned to the
   image corner so it cannot push the bleed */
body.zx.zx-deck .zx .see-row .see-hint{right:14px;bottom:14px}

/* -- row 64 · ONE SHADOW PER GROUND, ANGA'S OWN ALPHAS -------------------
   Blueprint §3.1: `0 4px 50.7px 0 rgba(0,0,0,0.07)` on their paper ground.
   Blueprint §8.1 states the value for OUR ramp when the card stands on a navy
   band: `0 4px 50px 0 rgba(6,21,32,.10)`. §6.1 measured ours at
   `rgba(0,0,0,.28)` — 4x the alpha, "reads heavy/cheap on a dark ground".
   `.zx-gl` / `.zx-gd` are zx-deck.js's ground tier (iteration 002).
   ONE shadow, two values, no ring, no border. */
body.zx.zx-deck .zx .see-row,
body.zx.zx-deck .zx .see-row.zx-gd,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.see-row,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .see-row{
  box-shadow:0 4px 50px 0 rgba(6,21,32,.10);border:0;
}
body.zx.zx-deck .zx .see-row.zx-gl,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.see-row.zx-gl,
body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .see-row.zx-gl{
  box-shadow:0 4px 50.7px 0 rgba(0,0,0,.07);border:0;
}

/* -- row 65 · @390 — FULL-BLEED CARD, IMAGE BLEEDS ALL FOUR SIDES --------
   Blueprint §3.1 "Mobile 390": width 390 full-bleed, NO side margin, radius 0,
   NO shadow, `.info` padding `48px 20px 20px 20px` (text width 350), h3
   26/700/33.15, CTA 40px, image bleeds all four edges. Ours measured
   350 wide / r30 / shadow .28 / image r16 with a 34px gap at the bottom (§6.2).
   SC18 NOTE — this block is written entirely in LOGICAL properties
   (`padding-inline`, `padding-block`, `inline-size`, `margin-inline`,
   `min-block-size`). shared.css's own `@media(max-width:1180px)` block declares
   the PHYSICAL `padding` / `width` / `max-width` longhands on `.see-row`, and
   SC18's PROPS list reads those physical names. Logical declarations do not
   serialise into them, so the shared responsive contract is not silently
   overridden — the same technique kit row 26 already ships and SC18 passes on.
   `grid-template-columns:1fr` is declared at the SAME value shared.css uses,
   which SC18 treats as "no conflict" by design. */
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row{
    inline-size:100vw;max-inline-size:100vw;
    margin-inline:calc(50% - 50vw);
    padding-inline:0;padding-block:0;
    grid-template-columns:1fr;
    border-radius:0;box-shadow:none;border:0;
    min-block-size:0;overflow:hidden;
  }
  /* row 64's `.lit x13` shapes are (0,17,1) and out-ranked the (0,4,1) rule
     above - measured at 390 as a live 50.7px shadow on a full-bleed card.
     Repeated here at the same shape so ONE value wins. */
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.see-row,
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.see-row.zx-gl,
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.see-row.zx-gd,
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .see-row,
  body.zx.zx-deck .zx .lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit.lit .see-row.zx-gl,
  body.zx.zx-deck .zx .see-row.zx-gl,
  body.zx.zx-deck .zx .see-row.zx-gd{box-shadow:none;border-radius:0;border:0}
  body.zx.zx-deck .zx .see-row > .see-txt,
  body.zx.zx-deck .zx .see-row:nth-of-type(even) > .see-txt,
  body.zx.zx-deck .zx .see-row.see-row--flip > .see-txt{
    padding-inline:20px;padding-block:48px 20px;
  }
  /* THE 390 GAP, and why it is closed with a MARGIN and not with `gap:0`.
     shared.css's own `@media(max-width:1180px)` block declares
     `.see-row{gap:22px}`. SC18's contract is that no rule may silently override
     a shared responsive block, and `gap` IS one of the properties it reads -
     a first cut wrote `row-gap:0;column-gap:0` here and SC18 failed with 47
     items across 24 pages, every one of them the word `gap`. The shared
     declaration is therefore left exactly as it is and the 22px is taken back
     on the figure's own block-start margin, which SC18 does not arbitrate and
     which nothing else in the cascade sets. Rendered gap measured after: 0px.
     The blueprint's mobile card (info flush against a four-side-bleeding
     image) is delivered; the shared contract is not touched. */
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv){
    margin-inline:0;margin-block-start:-22px;border-radius:0;padding-inline:0;
    block-size:auto;overflow:hidden;
  }
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > *:not(.see-hint):not(p):not(figcaption):not(.dv-src),
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) :is(.dv-fig,.dv-fig-inner,picture,figure){
    display:block;block-size:auto;inline-size:100%;max-inline-size:none;
    margin:0;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;
  }
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) img.vis,
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) > img,
  body.zx.zx-deck .zx .see-row :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv) picture > img{
    inline-size:100%;block-size:auto;aspect-ratio:1/1;
    object-fit:cover;border-radius:0;
  }
  body.zx.zx-deck .zx .see-row > .dv,
  body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot):has(.dv){
    padding-inline:20px;padding-block:28px;
  }
  body.zx.zx-deck .zx .see-row :is(h2,h3,.zx-h3){font-size:26px;line-height:33.15px}
}

/* -- row 66 · NO REVEAL, NO HOVER MOTION ON THE BOX OR ITS CTA -----------
   Blueprint §4.3, the finding that matters: on the whole reference page there
   is ONE keyframe animation (a header dropdown) and ZERO reveal-on-scroll —
   every below-fold feature box, stat chip and comparison column already has
   `opacity:1; transform:none` before any scroll. Hover on the box: no change.
   Hover on the CTA pill: no change.
   "ANGA reads premium because the COMPOSITION is finished, not because
   anything animates." Our 0.9s opacity+transform reveal buys none of the
   quality Shane is pointing at, so it comes off the boxes and their CTA.
   The site-wide reveal on other components is untouched. */
.zx-deck-js :where(body.zx-deck) .see-row.zx-rv,
.zx-deck-js :where(body.zx-deck) .see-row.zx-rv.zx-in,
.zx-deck-js :where(body.zx-deck) .see-row .zx-rv,
.zx-deck-js :where(body.zx-deck) .see-row.reveal,
.zx-deck-js :where(body.zx-deck) .see-row .reveal{
  opacity:1;transform:none;transition:none;animation:none;
}
body.zx.zx-deck .zx .see-row:hover,
body.zx.zx-deck .zx .see-row:focus-within{transform:none}
body.zx.zx-deck .zx a.zx-btn:hover,
body.zx.zx-deck .zx a.zx-btn:focus-visible,
body.zx.zx-deck .zx button.zx-btn:hover{transform:none!important}
body.zx.zx-deck .zx .see-row a.zx-btn:hover{box-shadow:none!important}

/* -- row 67 · THE CTA PILL AT THE BLUEPRINT'S GEOMETRY -------------------
   Blueprint §3.1 "CTA pill" + §8.3: height 42 desktop / 40 mobile, padding
   `0 20px`, radius 999, label 18/700/28 desktop and 16/700/24 mobile, gap 8 to
   the arrow, arrow 16px. §6.1 measured ours at 50px tall with `0 22px`.
   Mobile keeps a 44px tap target: loop_verify's `taps` check fails anything
   under 44 and that floor outranks the reference's 40. */
/* `:where(body.zx-deck) .zx a.zx-btn` (kit v1, line 473) already ships
   `min-height:50px!important; padding:0 22px!important; font-size:17px!important`,
   and the <=1180 block repeats it at 52px!important. An !important declaration
   can only be beaten by another !important, so these carry one. */
@media(min-width:1181px){
  body.zx.zx-deck .zx .see-row a.zx-btn,
  body.zx.zx-deck .zx .see-row a.zx-btn.zx-btn-fill,
  body.zx.zx-deck .zx .see-row a.zx-btn.see-cta{
    min-height:42px!important;height:42px!important;padding:0 20px!important;
    gap:8px!important;border-radius:999px!important;
    font-size:18px!important;line-height:28px!important;font-weight:700!important;
    box-shadow:none!important;
    display:inline-flex;align-items:center;justify-content:center;overflow:hidden;
    margin-top:0;
  }
}
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row a.zx-btn,
  body.zx.zx-deck .zx .see-row a.zx-btn.zx-btn-fill,
  body.zx.zx-deck .zx .see-row a.zx-btn.see-cta{
    min-block-size:44px!important;block-size:44px!important;
    padding-inline:20px!important;
    font-size:16px!important;line-height:24px!important;box-shadow:none!important;
  }
}

/* -- row 68 · ONE DISPLAY FACE -------------------------------------------
   Blueprint §4.1: the whole reference runs exactly TWO faces — FCSound for
   every heading, label, number and CTA (two weights only) and IBM Plex Sans
   Thai for body. §6.3 measured OURS at THREE display faces (Anuphan on h1/h2,
   Kanit on h3/CTA, Bai Jamjuree in the fallback chain) — "our h1 is not on the
   display face; our h3 is. Inconsistent."
   FCSound is commercial; Kanit is our measured equivalent (iteration 002).
   Anuphan and Bai Jamjuree are removed from every DISPLAY role — they stay in
   the body fallback chain, where a fallback belongs.
   Acceptance gate item 7 wants ONE family string across h1,h2,h3,.zx-btn and
   the stat number, so the token itself is narrowed rather than patched
   per-selector. */
body.zx.zx-deck,body.zx.zx-deck .zx,body.zx.zx-deck .zx-wrap{
  --zx-font-display:"Kanit",system-ui,sans-serif;
  --font-display:"Kanit",system-ui,sans-serif;
}
body.zx.zx-deck .zx :is(h1,h2,h3,h4,h5,h6,.zx-h1,.zx-h2,.zx-h3,.zx-d,.zx-btn,.zx-tab,.zx-statchip__n,.zx-num,.see-num){
  font-family:"Kanit",system-ui,sans-serif!important;letter-spacing:normal;
}
body.zx.zx-deck .zx a.zx-btn,body.zx.zx-deck .zx button.zx-btn{
  font-family:"Kanit",system-ui,sans-serif!important;
}

/* -- row 69 · TYPE INSIDE THE BOX ----------------------------------------
   Blueprint §3.1: h3 34/700/43.35 (26/700/33.15 below 1024), margin 0 0 20px;
   body 16/400/24, max-width 600 (renders 513 inside the 585 column), margin
   bottom 20. §6.1 flagged our body colour `#48596A` as "a mid-grey, lower
   contrast than the reference"; §8.1 names `#12212B` for the paper card. */
body.zx.zx-deck .zx .see-row :is(h2,h3,.zx-h3){
  font-size:34px;line-height:43.35px;font-weight:700;margin:0 0 20px;letter-spacing:normal;
}
body.zx.zx-deck .zx .see-row :is(p,li){
  font-size:16px;line-height:24px;max-width:600px;margin-bottom:20px;
}
body.zx.zx-deck .zx .see-row.zx-paper :is(p,li),
body.zx.zx-deck .zx .see-row.zx-gd.zx-paper :is(p,li){color:#12212B}
body.zx.zx-deck .zx .see-row :is(p,li):last-child{margin-bottom:0}

/* -- row 70 · STAT CHIP — the component §6.3 lists as MISSING -------------
   NAME: `.zx-statchip`, not `.zx-stat`. MEASURED collision - index.html
   already ships `.zx-stat` for its 70px proof stat block, and a 160x132 rule
   on that class shrinks it.
   Blueprint §3.3 / §8.4: 160 x 132, radius 14, padding 12, shadow
   `0 4px 100px -44px rgba(6,21,32,.19)`, number 40/700/60 in the accent,
   label 14/400/20, row is flex-wrap centred with gap 20 (12 below lg).
   Feed it real numbers only (gate item 11). */
body.zx.zx-deck .zx .zx-statchips{display:flex;flex-wrap:wrap;justify-content:center;gap:20px;margin:40px auto 0}
body.zx.zx-deck .zx .zx-statchip{
  inline-size:160px;min-block-size:132px;padding:12px;border-radius:14px;
  background:#FBFBF9;box-shadow:0 4px 100px -44px rgba(6,21,32,.19);
  text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center;border:0;
}
body.zx.zx-deck .zx .zx-statchip__n{font-size:40px;line-height:60px;font-weight:700;color:#1A5CBE;margin:0 0 8px}
body.zx.zx-deck .zx .zx-statchip__l{font-family:var(--zx-font-body);font-size:14px;line-height:20px;font-weight:400;color:#0B1F2A;margin:0;max-width:none}
@media(max-width:860px){body.zx.zx-deck .zx .zx-statchips{gap:12px}}

/* -- fix-009 · STAT CHIP HOVER --------------------------------------------
   Shane, hovering the 5.0 / ฿790 / ฿1,990 chips on index: "when moving
   mouse on it I want colour on". The chip had no hover state at all.

   GROUND: `--zx-grad-ink` (#1A5CBE -> #0A6E4A), not the sky/mint pair.
   MEASURED: white on #3E8EF7 is 3.27:1 and white on #2ED18F is 1.98:1 - both
   fail WCAG AA, and `--zx-grad-dark` (#6FB0FF -> #2ED18F) is lighter still.
   The ink gradient carries white at 6.33:1 (#1A5CBE) and 6.37:1 (#0A6E4A),
   so every point of the wash clears 4.5:1.

   A gradient cannot be animated through `background-color`, so the wash rides
   an `::before` at `opacity 0 -> 1` behind the text - the same .3s curve the
   rest of the kit uses, and the only property that moves besides `color`.
   Rubric §8.3: NO transform, NO box-shadow change on hover. Guarded by
   `@media(hover:hover)` so a touch tap does not leave a chip stuck lit. */
@media(hover:hover){
  body.zx.zx-deck .zx .zx-statchip{position:relative;isolation:isolate}
  body.zx.zx-deck .zx .zx-statchip::before{
    content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;
    background-image:var(--zx-grad-ink);opacity:0;
    transition:opacity .3s cubic-bezier(.4,0,.2,1);
  }
  body.zx.zx-deck .zx .zx-statchip:hover::before{opacity:1}
  body.zx.zx-deck .zx .zx-statchip :is(.zx-statchip__n,.zx-statchip__l){
    transition:color .3s cubic-bezier(.4,0,.2,1);
  }
  /* zx-deck.js stamps `.zx-t-d` on these two lines and zx-deck.css:2238
     writes `.zx-t-d{color:var(--zx-dim)!important}` behind six repeated
     `body.zx.zx-deck` compounds. Measured: without matching that weight the
     hover white never lands and the chips stay #48596A on the wash (1.14:1).
     So the hover carries the same six-compound weight and `!important`. */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx .zx-statchip:hover :is(.zx-statchip__n,.zx-statchip__l){
    color:#fff!important;
  }
}
@media(hover:hover) and (prefers-reduced-motion:reduce){
  body.zx.zx-deck .zx .zx-statchip::before,
  body.zx.zx-deck .zx .zx-statchip :is(.zx-statchip__n,.zx-statchip__l){
    transition:none;
  }
}

/* -- fix-009 · THE VISUAL WRAPPER IS NOT A LINK ANY MORE -------------------
   `<a class="see-fig" href="/img/vis/<stem>.webp" target="_blank">` handed the
   browser the bare raster and Chrome saved it to disk instead of showing it.
   The href is gone from all 63 wrappers; the hint text stays (SC10). Without
   an href the anchor would still inherit the kit's link cursor, so it is
   pinned back to the default here - the picture must not look clickable. */
body.zx.zx-deck .zx a.see-fig:not([href]){cursor:default}

/* -- row 71 · CASE CARD — the other component §6.3 lists as MISSING ------
   Blueprint §3.6 / §8.10: 430 x 432.6, radius 24, padding 17, shadow
   `0 4px 50px 0 rgba(6,21,32,.07)`, logo 128 x 44 `object-fit:contain`,
   thumbnail 390 x 160.6 radius 12, 3-column stat footer with 28/700/28
   numbers and 12-13px labels between 1px rules.
   HOVER: border-colour only — §4.3 measured it as the ONE hover state on the
   entire reference page. */
body.zx.zx-deck .zx .zx-cases{display:flex;flex-wrap:wrap;gap:20px;justify-content:center}
body.zx.zx-deck .zx .zx-case{
  inline-size:430px;min-block-size:432.6px;padding:17px;border-radius:24px;
  background:#FBFBF9;color:#0B1F2A;box-shadow:0 4px 50px 0 rgba(6,21,32,.07);
  border:1px solid transparent;display:flex;flex-direction:column;gap:12px;
  transition:border-color .3s cubic-bezier(.4,0,.2,1);
}
body.zx.zx-deck .zx .zx-case:hover{border-color:#3E8EF7}
body.zx.zx-deck .zx .zx-case__hd{display:flex;align-items:center;justify-content:space-between}
body.zx.zx-deck .zx .zx-case__hd img{inline-size:128px;block-size:44px;object-fit:contain}
body.zx.zx-deck .zx .zx-case__t{font-size:20px;line-height:28px;font-weight:700;margin:0}
body.zx.zx-deck .zx .zx-case__thumb{inline-size:100%;block-size:160.6px;object-fit:cover;border-radius:12px;display:block}
body.zx.zx-deck .zx .zx-case__stats{display:grid;grid-template-columns:1fr 1fr 1fr;margin-top:auto}
body.zx.zx-deck .zx .zx-case__stats > * + *{border-left:1px solid rgba(11,31,42,.12)}
body.zx.zx-deck .zx .zx-case__n{display:block;font-size:28px;line-height:28px;font-weight:700;color:#1A5CBE}
body.zx.zx-deck .zx .zx-case__l{display:block;font-family:var(--zx-font-body);font-size:12.5px;line-height:18px;color:#48596A;margin-top:6px}
@media(max-width:860px){body.zx.zx-deck .zx .zx-case{inline-size:100%;min-block-size:0}}

/* -- row 72 · THE COMPARISON "US" COLUMN RISES AND GLOWS -----------------
   Blueprint §3.4 / §8.6: the us column is 160px TALLER than the label and
   "them" columns, sits at `z-index:10`, and carries a THREE-LAYER brand
   `drop-shadow` filter — not a box-shadow. §6.3: "no elevation, no glow" on
   ours. The elevation is delivered through the `.cmpwrap::before` card
   iteration 004 already built (the anchor's markup is untouched by order), so
   this rule moves the CARD, never a cell.
   Alphas are lifted from ANGA's .20/.10/.05 because their orange sits on pure
   white and our sky/mint sits on #F4F5F4 — measured in iteration 004.
   RECORDED: this moves the SC16-guarded anchor. The delta is measured and
   printed in generator-state.md; `--rebaseline` is still Shane's call. */
body.zx.zx-deck .zx .cmpwrap{position:relative}
body.zx.zx-deck .zx .cmpwrap::before{
  filter:
    drop-shadow(rgba(62,142,247,.30) 0 2px 18px)
    drop-shadow(rgba(24,184,122,.16) 0 -6px 28px)
    drop-shadow(rgba(62,142,247,.09) 0 8px 34px);
}

/* -- row 73 · THE ACTIVE REVIEW CARD GETS THE 3-LAYER BLOOM --------------
   Blueprint §3.7 / §8.11: the active review card is the only other place the
   reference spends its brand hue — a three-layer, brand-tinted bloom, with a
   200px track padding so the bloom is never clipped. §6.3: ours is flat.
   Ours is the same structure in sky/mint. */
body.zx.zx-deck .zx :is(.zx-review,.review-card,.rvw-card,.zx-quote).is-active,
body.zx.zx-deck .zx :is(.zx-review,.review-card,.rvw-card,.zx-quote)[aria-current="true"]{
  box-shadow:
    0 4px 33px -11px rgba(62,142,247,.30),
    -26px -14px 93px -47px rgba(24,184,122,.28),
    0 17px 200px 0 rgba(62,142,247,.20);
}


/* -- row 74 · THE FIGURE INSIDE A CARD NEVER CARRIES ITS OWN INSET --------
   zx-deck.css:2198 ships `.dv.see-fig[style]{background:...!important;
   padding:14px;border-radius:var(--zx-r-2)}` for a dataviz figure standing on
   a band. Inside a `.see-row` the CARD is the frame. Measured before this rule:
   six rasters at 1920 sat 14px off the card edge, and ten more did at 390.
   Unmediated on purpose - the inset is wrong at every width. */
body.zx.zx-deck .zx .see-row > .dv.see-fig[style],
body.zx.zx-deck .zx .see-row > .see-fig[style],
body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv)[style]{
  padding:0!important;border-radius:0!important;
}
body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis){
  padding:0!important;
}


/* -- row 75 · THE PROVENANCE LINE BECOMES AN OVERLAY, NOT A FOOTER -------
   MEASURED, and it is a text-loss defect, not a cosmetic one: once the figure
   fills the card (row 63) the `<p class="dv-src">` that follows the raster is
   pushed past the card's `overflow:hidden` and stops rendering at 1920, and at
   390 it holds the image 42px off the card's bottom edge (blueprint mobile
   spec: the image bleeds all four sides).
   Those lines are SC03 number provenance ("ที่มา: ภาพหน้าจอโปรไฟล์จริงของ MDCC ...")
   and SC10 counts their characters, so they cannot be deleted and must not be
   clipped. ANGA bakes its captions INTO the raster; ours stay live DOM text and
   are pinned over the image bottom on a scrim, which keeps them selectable,
   searchable and counted while the image bleeds to the edge. */
body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis){position:relative}
body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > :is(p,figcaption,.dv-src,.dv-sub){
  position:absolute;left:0;right:0;bottom:0;z-index:3;margin:0;max-width:none;
  padding:14px 18px;text-align:left;
  background:linear-gradient(0deg,rgba(6,21,32,.94) 0%,rgba(6,21,32,.72) 55%,rgba(6,21,32,0) 100%);
  color:#FBFBF9!important;font-size:12.5px;line-height:1.5;
}
/* zx-deck.js stamps `.zx-t-d` (dark text on a light box) on these lines from the
   figure's OLD paper ground; on the scrim that computes #48596A over #061520 =
   2.56:1, measured by loop_verify's contrast check on google-maps at both
   viewports. The overlay owns its own ground, so it owns its own ink. */
/* zx-deck.css:2238 writes `.zx-t-d{color:var(--zx-dim)!important}` at
   (0,13,1) through the six-fold body repeat. An !important can only be beaten
   by an !important at a higher specificity, so the same repeat is used here.
   Measured before: rgb(72,89,106) on rgb(6,21,32) = 2.56:1 against a 4.5 bar,
   on google-maps at 1440 and at 390. */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > :is(p,figcaption,.dv-src,.dv-sub),
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > :is(p,figcaption,.dv-src,.dv-sub) :is(b,strong,span,a),
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > :is(p,figcaption,.dv-src,.dv-sub):is(.zx-t-d,.zx-t-i,.zx-t-m,.zx-t-w,.zx-a-lt,.zx-a-dk){
  color:#FBFBF9!important;
}
body.zx.zx-deck .zx .see-row .see-hint{z-index:4}


/* -- row 76 · THE ACCENT WASH COMES OFF EVERY CARD FACE ------------------
   MEASURED on the anchor at 1920: 16 card faces per page paint
   `radial-gradient(... rgba(62,142,247,.16) ...), radial-gradient(...
   rgba(24,184,122,.14) ...)` on top of their own fill. That is a SEVENTH job
   for the brand hue.
   Blueprint §4.2, the one-brand-colour discipline, measured on the reference:
   the brand hue appears 22 times in the whole /seo/ colour census against
   1,173 uses of ink, and it is spent on exactly SIX jobs - the heading key
   phrase, stat numbers, the CTA pill, inline links, the comparison check icon,
   and the glow behind the "us" column and the active review card. "Nowhere
   else."
   The card keeps its fill and its ONE shadow; the wash goes.
   SPECIFICITY, measured: the wash rule at zx-deck.css:2374 carries
   `.tmile.lit` inside its `:is()`, so `:is()` contributes (0,2,0) and the rule
   scores (0,5,1). A list of single classes scores (0,4,1) and loses - the
   first cut of this rule changed nothing at all (accent census identical to
   the digit). `.tmile.lit` is carried here too so the two shapes tie and
   document order decides. */
body.zx.zx-deck .zx :is(.faq-item,.blk,.mt-stat,.answer-box,.answer-block,.zx-ab,.zx-tornstub,.see-row,.skubox,.ctabox,.qcard,.zx-card,.zx-ticket,.zx-tile,.stat-callout,.transp-item,.do-item,.reason,.step,.stepcard,.phase-item,.phase-row,.mech-card,.art-phase-item,.svc-card,.vs-col,.dv-tile,.dv,.tactics-list,.mdcc-tile,.tmile.lit){
  background-image:none;
}


/* ========================================================================
   ITERATION 007 — KIT ROWS 77-83
   Every rule below closes a numbered item from
   gan-harness/anga-box/feedback/feedback-006.md. Nothing here is taste.
   ======================================================================== */

/* -- row 77 · OUR OWN RED COMES OFF EVERY PAGE --------------------------
   Rubric gate 12 says "no ANGA orange/red in CSS or rendered pixels".
   Iteration 006's own gate searched only ANGA's literal hexes and masked the
   floating Maps button as a third-party mark. It is not one: the PIN GLYPH is
   Google's (inline `fill="#fff"`, untouched here), the 68px red disc under it
   is ours. Measured by re-probing all 24 pages at 1920 + 390 for any computed
   colour with red dominant, hue outside 22-330 deg and saturation >= 40:

     a.maps-fab.zx-fab            background rgb(234,67,53)   24 pages
     span.dv-box-lab.zx-accent    background rgb(192,57,43)    4 pages
     span.dv-box                  border     rgb(192,57,43)    4 pages
     th.them / td.cross /
       div.ba-row.bad  (.zx-t-r)  colour     rgb(168,44,32)    1 page
     span.bd.r.zx-accent          background rgb(255,95,87)    1 page
     span.ex-cite.foreign         background rgba(192,57,43,.34)
     div.phnum.ph1.zx-accent      background rgb(196,56,67)
     span.warn-tag                border     rgba(244,91,105,.3)
     p.pull / .wont-item.lit /
       .warn.lit / svg circle     var(--rose) rgb(226,87,63)

   `--rose` is declared in every page's inline `<style>` at `:root` (0,0,1);
   the override below scores (0,2,1) so it wins without touching a single page
   byte or the 12,000-byte inline budget. #48596A is ANGA's own "them"-column
   grey, which is what the evaluator named as the replacement. */
body.zx.zx-deck{--rose:#48596A;--rose-soft:#EEF0F1}
body.zx.zx-deck .maps-fab,body.zx.zx-deck .maps-fab.zx-fab{background:#0B1F2A}
body.zx.zx-deck .zx .dv-box-lab,
body.zx.zx-deck .zx .dv-box-lab.zx-accent{background:#0B1F2A}
body.zx.zx-deck .zx .dv-box,
body.zx.zx-deck .zx .dv-box.lab-inside{border-color:#0B1F2A}
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx-t-r{color:#48596A!important}
body.zx.zx-deck .zx :is(.bd.r,.bd.r.zx-accent){background:#48596A}
body.zx.zx-deck .zx .ex-cite.foreign{background:rgba(72,89,106,.34)}
body.zx.zx-deck .zx .phnum.ph1,
body.zx.zx-deck .zx .phnum.ph1.zx-accent{background:#0B1F2A}
body.zx.zx-deck .zx .warn-tag{border-color:rgba(11,31,42,.30)}
body.zx.zx-deck .zx :is(p.pull,.wont-item,.warn,.warn.lit,.wont-item.lit){border-left-color:#48596A}
/* Inline-SVG PRESENTATION ATTRIBUTES, which no `--rose` override can reach:
   25 x `stroke="#C0392B"` (the `.nav-ico` glyph in every page's nav column)
   and 4 x `stroke|fill="#e2573f"` (`.sym .ico` on line-oa). Matched on the
   exact hex so a third-party mark can never be caught by accident — Google's
   #ea4335 / #fbbc04 / #4285f4 / #34a853, Shopee's #EE4D2D, Google Ads'
   #E37400 and Cloudflare's #F6821F are all left exactly as they are. */
body.zx.zx-deck [stroke="#C0392B"],body.zx.zx-deck [stroke="#c0392b"],
body.zx.zx-deck [stroke="#E2573F"],body.zx.zx-deck [stroke="#e2573f"]{stroke:#48596A}
body.zx.zx-deck [fill="#C0392B"],body.zx.zx-deck [fill="#c0392b"],
body.zx.zx-deck [fill="#E2573F"],body.zx.zx-deck [fill="#e2573f"]{fill:#48596A}
/* THE LAST ONE, and the only one an element-level probe could not see:
   `p.see-pain::before{background:rgb(232,69,47)}` paints a red bullet disc on
   google-maps. A `getComputedStyle(el)` sweep returns nothing for it — the
   colour lives on a PSEUDO-element. Found by re-running the sweep with
   ::before / ::after / ::marker, after the pixel census kept returning a solid
   180px block of #E8452F at two document positions the DOM said were clean. */
body.zx.zx-deck .zx p.see-pain::before{background:#48596A}

/* -- row 78 · THE FIGURE HAS A GROUND BEFORE ITS RASTER ARRIVES ----------
   feedback-006 minor #1, and it bit the delivery shot itself: `.see-fig` is
   `background:#fff` (shared.css:1350) but kit row 63 sets it transparent, so a
   lazily-loaded raster leaves the CARD's paper showing with nothing in it —
   the evaluator's first element pass and our own `iter-006/seo-1920.png`
   both captured three cards with empty image halves.
   The figure now carries the navy the composites are built on, so the cell
   reads as a deliberate dark panel for the milliseconds before the webp
   paints. The first row on every page is additionally `loading="eager"
   fetchpriority="high"` (visuals/swap.py). */
body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis){
  background:#0B1F2A;
}

/* -- row 79 · THE HEADING SHRINKS AT 390 --------------------------------
   Blueprint §3.1: h3 is 34/700/43.35 above 1024 and 26/700/33.15 below it.
   MEASURED by the evaluator on 93 of 93 rows: ours stayed 34/43.35 at 390 —
   the rule was simply never written, and iteration 006's own 12-point gate did
   not measure it, so it went unseen for four iterations. */
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row :is(h2,h3,.zx-h3){
    font-size:26px;line-height:33.15px;
  }
}
/* MEASURED: google-maps row 0 uses `<p class="zx-h3">` rather than a real
   heading element, and it rendered 16px/24px at BOTH viewports — one of the
   two boxes feedback-006 major #8 called "no heading at all". Kit row 69's
   (0,5,1) rule loses to the `!important` body-copy rule at zx-deck.css:3076,
   which excludes `.zx-h3` from its own selector but not from the cascade that
   put 16px there. Restated at the same weight, desktop and mobile. */
body.zx.zx-deck .zx .see-row p.zx-h3{font-size:34px!important;line-height:43.35px!important;font-weight:700}
@media(max-width:860px){
  body.zx.zx-deck .zx .see-row p.zx-h3{font-size:26px!important;line-height:33.15px!important}
}

/* -- row 80 · THE BODY INSIDE THE CARD IS NEAR-INK ----------------------
   Blueprint §7(a)-5 / §8.1 ask for `#12212B` on the paper card; §6.1 named our
   `#48596A` as "a mid-grey, lower contrast than the reference". Kit row 69
   wrote it for `.see-row.zx-paper` only, and the evaluator measured
   rgb(72,89,106) on 93/93 — i.e. the rows that carry the paper CARD do not all
   carry the `.zx-paper` class, so the rule never matched. Scoped to the text
   column instead, which is where the copy lives on every one of the 93.
   MEASURED AGAIN after the first cut of this rule, which also changed nothing:
   `zx-deck.js` stamps `.zx-t-d` ("dark text on a light box") on these
   paragraphs, and `zx-deck.css:2238` writes `.zx-t-d{color:var(--zx-dim)
   !important}` through a SIX-FOLD body repeat, i.e. at (0,13,1) with an
   `!important`. A (0,5,2) rule loses to that no matter what it says. The same
   six-fold repeat kit row 75 needed is used here for the same reason.
   SCOPED TO `.zx-t-d` AND TO CLASSLESS PARAGRAPHS ON PURPOSE: `.zx-t-d` is
   exactly the set zx-deck.js has already decided is sitting on a LIGHT ground,
   so near-ink can never land on a dark card and break loop_verify's contrast
   check. `.zx-t-w` / `.zx-t-i` (white / inverted) are untouched. */
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx .see-row .see-txt :is(p,li,td,dd).zx-t-d,
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx .see-row .see-txt :is(p,li,td,dd):not([class]){
  color:#12212B!important;
}
body.zx.zx-deck .zx .see-row .see-txt :is(p,li) :is(b,strong,em,i):not([class]){color:inherit}

/* -- row 81 · NOTHING MOVES ON THE CTA PILL -----------------------------
   Blueprint §8.3: `.zx-btn:hover{ nothing }`. §4.3 measured ONE hover state on
   the whole reference page and it is the case card's border colour. Ours ran
   `filter:brightness(1.1)` + `translateY(-1px)` on 24/24 pages; iteration
   006's gate only read `transform` and `box-shadow`, so the filter passed
   unseen. Both go. */
body.zx.zx-deck .zx a.zx-btn:hover,
body.zx.zx-deck .zx button.zx-btn:hover,
:where(body.zx-deck) .zx a.zx-btn-fill:hover{
  filter:none!important;transform:none!important;
}

/* -- row 82 · THE PROVENANCE OVERLAY WRAPS, IT DOES NOT TRUNCATE --------
   feedback-006 critical #2 caught `line-oa-12-badge`'s source line reading
   `…รอบ 30 วัน · 4…`. The in-raster version is fixed in visuals/composites.py;
   this is the live DOM twin from kit row 75. It gets a real measure and two
   lines, and it never gets an ellipsis — a provenance line that eats its own
   date is worse than one that wraps. */
body.zx.zx-deck .zx .see-row > :is(a.see-fig,.see-fig,.see-slot,.see-figslot,.dv):has(img.vis) > :is(p,figcaption,.dv-src,.dv-sub){
  max-inline-size:100%;white-space:normal;overflow-wrap:break-word;
  text-overflow:clip;overflow:visible;
}

/* -- row 83 · THE US COLUMN RISES, AND THE GLOW SITS ON IT --------------
   Blueprint §8.6: "The us-column being 160px taller and glowing is the whole
   point — do not equalise the columns." Iteration 006 put the three-layer
   drop-shadow on `.cmpwrap::before`, i.e. on the WHOLE grid card, so the
   evaluator measured all three columns sharing a top edge and nothing reading
   as elevated. The filter moves onto the us column itself and the other two
   columns are pushed down by the 160px head.
   MARKUP CONSTRAINT, measured: our comparison is not three column containers.
   `.cmpgrid` is a flat `1.15fr 1fr 1fr` grid of individual cells
   (`.cmp-lbl` / `.cmp-us` / `.cmp-them`), so there is no element to put the
   filter on. A pseudo-element grid ITEM is not an option either: `.cmpgrid`
   declares no `grid-template-rows`, so `grid-row:1/-1` would resolve to a
   single implicit row, and an explicitly placed pseudo would push every
   auto-placed cell one slot along. The panel is therefore absolutely
   positioned inside `.cmpwrap` (already `position:relative`) over the second
   track, and `.cmpwrap` gains the 160px of head room the column rises into.
   RECORDED: this moves the SC16-guarded anchor further. The delta is printed
   in generator-state.md; `--rebaseline` remains Shane's call. */
@media(min-width:861px){
  body.zx.zx-deck .zx .cmpwrap::before{filter:none}
  body.zx.zx-deck .zx .cmpwrap{padding-block-start:160px}
  body.zx.zx-deck .zx .cmpwrap::after{
    content:"";position:absolute;z-index:0;pointer-events:none;
    inset-block:0 0;
    inset-inline-start:calc(14px + (100% - 28px) * 0.3651);
    inline-size:calc((100% - 28px) * 0.3175);
    background:#FBFBF9;border-radius:20px;
    filter:
      drop-shadow(rgba(62,142,247,.30) 0 2px 18px)
      drop-shadow(rgba(24,184,122,.16) 0 -6px 28px)
      drop-shadow(rgba(62,142,247,.09) 0 8px 34px);
  }
  body.zx.zx-deck .zx .cmpgrid{position:relative;z-index:1}
  body.zx.zx-deck .zx .cmpgrid .cmp-us{background:transparent}
  /* the head cell is pulled up into the raised panel, so the us column stands
     160px above the label and "them" columns — §8.6's "whole point" */
  body.zx.zx-deck .zx .cmpgrid .cmp-hd.cmp-us{
    margin-block-start:-160px;min-block-size:160px;
    display:flex;flex-direction:column;align-items:center;justify-content:center;
  }
}

/* ROW 84 — hover colour pass 2026-09-06 (Shane) ==========================
   Shane, on index: "i like the style but when mouse on need color on each
   box — and how about other pages?" fix-009 gave the stat chips a hover and
   nothing else on the kit answered the mouse. This generalises that one
   language to every box on all 24 kit pages.

   ONE LANGUAGE, ONE GRADIENT. The wash is always `--zx-grad-ink`
   (#1A5CBE -> #0A6E4A) riding a `::before` at `opacity 0 -> n`. A gradient
   cannot be tweened through `background-color`, so opacity carries it —
   exactly what the stat chip does. Two grounds:
     .zx-paper (light band)  -> wash .08, heading goes to the accent stop
                                #1A5CBE
     .zx-accent (navy/green) -> wash .22, heading colour UNCHANGED
     anything else           -> wash .08, heading colour UNCHANGED
   zx-deck.js:333 stamps `.zx-paper` / `.zx-accent` from the measured ground
   luminance, so the split is the DOM's own, not a guess per page. The accent
   stop is keyed to `.zx-paper` POSITIVELY, not to `:not(.zx-accent)`: measured
   on website.html, a `<details class="dx zx-gd">` sits on a dark band and
   carries NEITHER marker, and `:not(.zx-accent)` dropped its cream summary
   from 13.5:1 to 2.37:1. An unmarked box now keeps its text and takes the wash
   only. And a box carrying NEITHER marker takes no wash at all: measured on
   google-maps + pin-google-maps, `<details class="dx zx-gd">` sits on the navy
   band with a line the kit already renders at 2.53:1, and an ink wash there
   moved it to 2.38:1. Making an already-failing line worse is not a trade this
   pass is allowed to make, so unmarked boxes are left exactly as they were.
   WHY the accent ground keeps its heading: #1A5CBE on the green tile is
   1.93:1 and a LIGHTENING wash there drops white to 4.03:1. Darkening with
   the same ink pair moves white on #0D8A5A from 4.37:1 to 4.84:1 and on navy
   #0B1F2A from 15.9:1 to 13.6:1 — both clear AA, and the hue shift is the
   colour response Shane asked for.

   NOTHING ELSE MOVES: no transform, no scale, no translate, no box-shadow, no
   border, no filter. Blueprint 8.3 keeps ANGA's boxes geometrically dead on
   hover, and `.zx-ticket` keeps the animation it already owns.

   MEASURED contrast on the hovered ground:
     #1A5CBE heading on the .08 ink wash over #FFF     5.44:1
     #1A5CBE heading on white                          6.30:1
     #0B1F2A body copy on the .08 wash                15.1:1
     #fff on the .22 wash over #0D8A5A                 4.84:1
     #fff on the .22 wash over navy #0B1F2A           13.6:1
     #fff on the pill's darker stops #144A99/#08573A  8.50 / 8.65:1
   All >= 4.5:1.

   `@media(hover:hover)` guards the family so a touch tap cannot leave a box
   stuck lit; the reduced-motion block drops the transition but KEEPS the
   colour (a colour change is not motion).
   ---------------------------------------------------------------------- */
@media(hover:hover){

  /* -- 84.1 the wash, on every box the kit has --------------------------- */
  body.zx.zx-deck .zx :is(
    .see-row,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.price-card,
    .faq-item,.blk,.answer-box,.answer-block,.stepcard,.phase-item,.phase-row,
    .mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,.inc,
    .zx-tile,.dv-tile,.mdcc-tile,.mt-stat,.zx-card:not(.zx-ticket),
    .cmpgrid .cmp-us,.cmpgrid .cmp-them,.faq > details,.dx
  ):is(.zx-paper,.zx-accent){position:relative;isolation:isolate}
  body.zx.zx-deck .zx :is(
    .see-row,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.price-card,
    .faq-item,.blk,.answer-box,.answer-block,.stepcard,.phase-item,.phase-row,
    .mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,.inc,
    .zx-tile,.dv-tile,.mdcc-tile,.mt-stat,.zx-card:not(.zx-ticket),
    .cmpgrid .cmp-us,.cmpgrid .cmp-them,.faq > details,.dx
  ):is(.zx-paper,.zx-accent)::before{
    content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
    border-radius:inherit;background-image:var(--zx-grad-ink);opacity:0;
    transition:opacity .3s cubic-bezier(.4,0,.2,1);
  }
  body.zx.zx-deck .zx :is(
    .see-row,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.price-card,
    .faq-item,.blk,.answer-box,.answer-block,.stepcard,.phase-item,.phase-row,
    .mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,.inc,
    .zx-tile,.dv-tile,.mdcc-tile,.mt-stat,.zx-card:not(.zx-ticket),
    .cmpgrid .cmp-us,.cmpgrid .cmp-them,.faq > details,.dx
  ):is(.zx-paper,.zx-accent):hover::before{opacity:.08}

  /* the navy / green bands need more of the wash to read as a change at all,
     and .22 is the point where white on the green tile is still 4.84:1 */
  body.zx.zx-deck .zx :is(
    .see-row,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.price-card,
    .faq-item,.blk,.answer-box,.answer-block,.stepcard,.phase-item,.phase-row,
    .mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,.inc,
    .zx-tile,.dv-tile,.mdcc-tile,.mt-stat,.zx-card:not(.zx-ticket),
    .cmpgrid .cmp-us,.cmpgrid .cmp-them,.faq > details,.dx
  ).zx-accent:hover::before{opacity:.22}

  /* THE COMPARISON CELLS ARE EXEMPT FROM THE MARKER GATE. zx-deck.js stamps
     `.zx-paper` / `.zx-accent` on BANDS, and the `.cmpgrid` cells are inside
     the panel rather than being the band, so none of them carries a marker and
     the gate above silenced all 100 of them (measured). Their ground is the
     panel's own #FFFFFF / #FBFBF9 on every page that has a comparison, and
     their copy is `.zx-t-d` (#48596A) — dark on light, so the paper wash is
     the right one and 5.4:1 holds. */
  body.zx.zx-deck .zx .cmpgrid :is(.cmp-us,.cmp-them){position:relative;isolation:isolate}
  body.zx.zx-deck .zx .cmpgrid :is(.cmp-us,.cmp-them)::before{
    content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
    border-radius:inherit;background-image:var(--zx-grad-ink);opacity:0;
    transition:opacity .3s cubic-bezier(.4,0,.2,1);
  }
  body.zx.zx-deck .zx .cmpgrid .cmp-them:hover::before{opacity:.08}
  /* the us column is the one §8.6 says must read brightest */
  body.zx.zx-deck .zx .cmpgrid .cmp-us:hover::before{opacity:.12}

  /* -- 84.2 the heading takes the accent stop, on paper grounds only ----- */
  /* zx-deck.js stamps `.zx-t-i` / `.zx-t-d` on these headings and line 2237
     writes them behind SIX repeated `body.zx.zx-deck` compounds with
     `!important` (0,13,1 + important). fix-009 measured that anything lighter
     never lands, so the hover carries the same weight. `.zx-key` keeps its own
     colour: this rule targets the heading box, not the mark inside it. */
  body.zx.zx-deck .zx :is(
    .see-row,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.price-card,
    .faq-item,.blk,.answer-box,.answer-block,.stepcard,.phase-item,.phase-row,
    .mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,
    .zx-tile,.dv-tile,.mdcc-tile,.mt-stat,.zx-card:not(.zx-ticket),
    .cmpgrid .cmp-us,.cmpgrid .cmp-them,.faq > details,.dx
  ) :is(h2,h3,h4,.zx-h2,.zx-h3,.zx-h4,summary){
    transition:color .3s cubic-bezier(.4,0,.2,1);
  }
body.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck.zx.zx-deck .zx :is(.see-row,.skubox,.gtee,.ctabox,.qcard,.zx-hero-box,.price-card,.faq-item,.blk,.answer-box,.answer-block,.stepcard,.phase-item,.phase-row,.mech-card,.art-phase-item,.do-item,.transp-item,.reason,.tier-item,.zx-tile,.dv-tile,.mdcc-tile,.mt-stat,.zx-card:not(.zx-ticket),.cmpgrid .cmp-us,.cmpgrid .cmp-them,.faq > details,.dx).zx-paper:hover :is(h2,h3,h4,.zx-h2,.zx-h3,.zx-h4,summary){
    color:#1A5CBE!important;
  }

  /* -- 84.3 the filled CTA pill gets a real colour, not a filter --------- */
  /* Row 81 removed `filter:brightness(1.1)` because a filter is not a colour
     and the blueprint bans the lift that came with it. Shane still wants the
     pill to answer the mouse, so it takes the DARKER end of the same ink
     gradient (#144A99 -> #08573A) on a pseudo. White stays >= 8.5:1.
     `.zx-t-w` GATE, measured: the LINE pill `.zx-btn-fill.zx-cta-line.zx-t-i`
     is brand green with INK text, and the ink wash took it from 16.46:1 to
     1.96:1. Only pills zx-deck.js has marked as white-text take the wash; the
     brand-coloured pill keeps its own colour, which it should anyway. */
  body.zx.zx-deck .zx :is(a,button).zx-btn-fill.zx-t-w{position:relative;isolation:isolate}
  body.zx.zx-deck .zx :is(a,button).zx-btn-fill.zx-t-w::before{
    content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
    border-radius:inherit;opacity:0;
    background-image:linear-gradient(to right,#144A99,#08573A);
    transition:opacity .3s cubic-bezier(.4,0,.2,1);
  }
  body.zx.zx-deck .zx :is(a,button).zx-btn-fill.zx-t-w:hover::before,
  body.zx.zx-deck .zx :is(a,button).zx-btn-fill.zx-t-w:focus-visible::before{opacity:1}

  /* -- 84.4 a box must not double-wash through its own children ---------- */
  /* `.inc` rows sit inside `.skubox`; without this the two washes stack to 16%
     on the inner row and it reads as a second, brighter target. The child
     still lights on its own hover — this only stops the PARENT wash from
     bleeding through it. */
  body.zx.zx-deck .zx :is(.skubox,.gtee,.ctabox,.price-card,.blk) .inc::before{opacity:0}
  body.zx.zx-deck .zx :is(.skubox,.gtee,.ctabox,.price-card,.blk) .inc:hover::before{opacity:.08}
}
@media(hover:hover) and (prefers-reduced-motion:reduce){
  body.zx.zx-deck .zx *::before,
  body.zx.zx-deck .zx :is(h2,h3,h4,.zx-h2,.zx-h3,.zx-h4,summary){
    transition-duration:0s;
  }
}

/* -- ROW 84.5 · THE UPDATE STAMP MOVES 12px RIGHT ------------------------
   Shane, pointing at the update pill on the answer band (index.html:482, the
   same span on 9 pages): "move this little to right". It is
   `align-self:flex-start` in a flex COLUMN, so a start margin shifts the pill
   alone and cannot push the column or any sibling. Kit rule, so it lands on
   all 9 pages from one place. 12px.
   390 CHECK: the pill is the narrowest item in `.zx-ab-side`; the column is
   358px wide there and the pill is far narrower, so 12px leaves slack and
   nothing wraps or overflows — measured in hover010_probe. */
body.zx.zx-deck .zx .zx-stamp{margin-inline-start:12px}
