/* ============================================================================
   datasite.css — IDENTITY A, "Editorial Data"
   Component + token layer for the 18-site data ring. Loads AFTER styles.css
   and deliberately RETOKENS it: styles.css is the shared base for the whole
   portfolio (and is owned elsewhere), so every Identity A decision is made
   here, as an override, rather than by forking the base.

   Reference standard: SuburbIQ. Warm paper, warm hairlines, serif display,
   sans figures, hairline-divided stat rows instead of boxes, tables typeset
   flush to the measure rather than inset in a widget.

   Palette validated with _shared/contrast-check.mjs — 132 text/background
   pairs, all ≥4.5:1 (≥3:1 for large display type and UI edges). Do not change
   an accent without re-running it: several of these hues (lux #7C5A22,
   fast #8A5A14, gym #7A4E1D) had to be darkened well past "looks nice" to
   clear AA on warm paper.
   ========================================================================== */

/* ============================================================ 1. the tokens */
:root {
  /* paper stack — warm off-white ground, pure-white surfaces laid on it */
  --paper: #FBF9F5;
  --paper-2: #F4F1EA;
  --bg: var(--paper);
  --bg-tint: var(--paper-2);
  --surface: #FFFFFF;
  --surface-2: var(--paper-2);

  /* ink ramp — four real steps, never opacity-faded text */
  --ink: #14181B;
  --ink-2: #454D53;
  --muted: #646C72;
  --faint: #7E868D;

  /* warm hairlines. The warmth is the single biggest contributor to the
     "printed" feel and the easiest thing to lose when porting. */
  --line: #E7E1D6;
  --line-2: #D6CEC0;
  --rule: var(--line);
  --rule-2: var(--line-2);
  --grid: var(--line);

  /* one accent hue per site, injected on <body style="--site-accent:…"> by
     shell(). Falls back to the ring's neutral slate if a site forgets. */
  --accent: var(--site-accent, #3C4A55);
  --accent-2: var(--site-accent, #3C4A55);
  --accent-3: var(--site-accent, #3C4A55);
  --accent-soft: color-mix(in srgb, var(--accent) 9%, var(--paper));

  /* 8pt spacing scale */
  --s1: 4px; --s2: 8px; --s3: 16px; --s4: 24px; --s5: 32px;
  --s6: 48px; --s7: 64px; --s8: 96px;

  /* editorial radii — near-flat. Cards are defined by hairlines, not shadows. */
  --radius: 6px;
  --radius-lg: 8px;
  --radius-sm: 4px;

  /* two shadows exist in the whole system, both functional */
  --shadow-xs: none;
  --shadow-sm: none;
  --shadow: none;
  --shadow-lg: 0 12px 34px -14px rgba(20, 24, 27, .28);

  --max: 1180px;
  --measure: 72ch;

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --serif: "Fraunces", Georgia, "Times New Roman", serif;
  --font: var(--sans);
  color-scheme: light;

  /* ---- AAA+++ STUDIO PREMIUM additions (2026-07-30) ------------------------
     Strictly ADDITIVE. Nothing above is renamed — ~90,000 built pages resolve
     against those names. See _plans/AAA-PLUS-STUDIO-PREMIUM.md §2.1.          */

  /* The third voice. Identity A was Fraunces + Inter and nothing else, so no
     figure on any page read as *measured*. This is the specification register:
     dates, units, codes, source vintages, step numbers. System stack only —
     the zero-new-requests constraint is non-negotiable, so we never load a
     mono font file. */
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, Menlo, monospace;
  --fs-mono: .8125rem;

  --paper-deep: #F0EBE0;          /* band-raised sections; one step below --paper-2 */
  --accent-deep: color-mix(in srgb, var(--accent) 78%, var(--ink));
  --on-accent: #FFFFFF;
  --accent-wash: color-mix(in srgb, var(--accent) 7%, var(--paper));

  /* The ONE elevating shadow. Reserved for the single object per page that
     asks for action. Everything else is flat or a surface step. */
  --shadow-lift: 0 1px 0 rgba(20, 24, 27, .05), 0 24px 48px -32px rgba(20, 24, 27, .25);

  --t-fast: 140ms;
  --t-med: 220ms;
  --t-slow: 420ms;
  --ease-out: cubic-bezier(0.25, 1, 0.4, 1);
  --ease-inout: cubic-bezier(0.45, 0.05, 0.25, 1);

  --fs-stat: clamp(2.8rem, 8vw, 4.8rem);   /* tokenises the .answer .big magic number */
  --track-label: .11em;
  --grain-alpha: .028;
}

/* Cluster I ships English today and Hindi later. Putting Noto Sans Devanagari
   in the stack now costs nothing (it is only fetched if a Devanagari glyph is
   actually rendered) and means the day a Hindi string lands it is already
   typeset rather than falling back to a system default. */
body[data-cluster="india"] {
  --sans: "Inter", "Noto Sans Devanagari", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* Dark mode is WARM BASALT, not a grey inversion.
   Identity A's whole material argument is warm paper; flipping to cool
   charcoal #121416 meant the brand became a different brand after sunset —
   the single strongest cue (warmth) died at night. Same doctrine as the trades
   pair's #171613. Contrast verified: #EDE9DF on #161512 = 14.4:1,
   #C7C1B4 = 9.6:1, #9C968A = 5.6:1. Re-run contrast-check.mjs if touched. */
:root[data-theme="dark"] {
  --paper: #161512;
  --paper-2: #1C1B17;
  --bg: var(--paper);
  --bg-tint: var(--paper-2);
  --surface: #1E1C18;
  --surface-2: var(--paper-2);
  --ink: #EDE9DF;
  --ink-2: #C7C1B4;
  --muted: #9C968A;
  --faint: #6E6960;
  --line: #312F28;
  --line-2: #413E35;
  --paper-deep: #131210;
  --shadow-lg: 0 24px 60px -30px rgba(0, 0, 0, .72);
  /* Dark elevation is a SURFACE STEP plus a hairline, never a shadow. A cast
     shadow on a dark ground is grey mush — it reads as a rendering artifact. */
  --shadow-lift: none;
  color-scheme: dark;
}

body {
  font-family: var(--sans);
  background: var(--paper);
  color: var(--ink);
  font-size: 16.5px;
  line-height: 1.62;
  letter-spacing: -.006em;
}
@media (max-width: 640px) { body { font-size: 16px; } }

.wrap { max-width: var(--max); padding: 0 28px; }
@media (max-width: 640px) { .wrap { padding: 0 20px; } }
/* Narrow the CHILDREN, not the container, so the left gutter stays aligned
   with every full-width section above and below it.

   ⚠ FIXED 2026-08-10. `max-width: none` also cancelled `.wrap`'s own
   `max-width: var(--max)`, because `.wrap.narrow` is the same element — so the
   container stopped being the centred 1180px column and ran the full viewport.
   Measured on a yoga asana page at 1280: the header, crumbs, hero and footer
   sat at x=43 while ELEVEN narrow sections sat at x=28. A 15px stagger down
   every page in the ring, and the exact opposite of what this comment asks
   for. The container keeps the shared cap; only the children are narrowed. */
.narrow { max-width: none; }
.wrap.narrow { max-width: var(--max); }
.narrow > * { max-width: var(--measure); }

/* ======================================================== 2. the type pair
   Fraunces for display; Inter for UI, body and every number a reader reads,
   compares or copies. A serif numeral looks handsome and reads slower — the
   whole product is numbers, so numbers are always sans. */
h1, h2, h3, .serif {
  font-family: var(--serif);
  font-weight: 500;
  letter-spacing: -.02em;
  font-variation-settings: "SOFT" 0, "WONK" 0;
}
h1 { font-size: clamp(2.1rem, 5.4vw, 3.4rem); line-height: 1.05; letter-spacing: -.03em; }
h2 { font-size: clamp(1.45rem, 3vw, 2rem); line-height: 1.15; letter-spacing: -.024em; }
h3 { font-size: 1.12rem; line-height: 1.3; letter-spacing: -.018em; }

.eyebrow {
  display: inline-block; font-family: var(--sans); font-size: .7rem; font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase; color: var(--accent); margin-bottom: var(--s2);
}
.lede, .deck {
  font-size: 1.14rem; line-height: 1.55; color: var(--ink-2);
  max-width: 60ch; margin-top: var(--s3);
}
a { color: var(--accent); text-underline-offset: 2px; }

/* ---------------------------------------------------------- breadcrumbs */
.crumbs { border-bottom: 0; background: transparent; font-size: .82rem; }
.crumbs .wrap { height: auto; padding-top: 20px; gap: 7px; }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--accent); text-decoration: underline; }
.crumbs .sep { color: var(--line-2); }
.crumbs [aria-current] { color: var(--ink-2); font-weight: 600; }

/* ===================================================== 3. answer above fold
   The shared skeleton (§3): the number is visible without scrolling, then the
   method, then the full table, then related. */
.hero { padding: var(--s6) 0 var(--s5); border-bottom: 1px solid var(--line); }
.hero h1 { max-width: 20ch; }

/* ---- 3.1 the fold budget on a phone ---------------------------------- */
/* DESIGN-MODERNISATION-2026-08 §3.2, measured at a real 375x812 viewport.
   The India ring drifted from its Cluster G siblings not in its tokens but in
   its hero ORDER: fuel puts the search at 477px and its source line at 788px,
   both above the fold, while grow/sehat/sugar interpose a YMYL band and a
   400-500px set piece first and push the search to 939-1182px — a phone reader
   reaches the action only after two swipes.

   Fixed here rather than in six templates because it is one rule and because
   reordering in CSS changes no markup, no DOM order for assistive technology
   outside this breakpoint, and nothing at all on desktop where the set piece
   legitimately leads. `:has()` degrades to today's behaviour where unsupported,
   so the worst case is the status quo.

   THE GUARD. Every rule below is scoped to `.wrap:has(...)` — a hero that
   actually contains a recognised ACTION. Without that guard the block also
   reorders heroes it cannot help, and it did: GymFinder names its search
   `.gsearch`, so the action stayed put while the source line was promoted
   ABOVE the answer it vouches for. Reordering a hero whose action you cannot
   find is strictly worse than leaving it alone, so now it leaves it alone.

   The action is matched by CONTENT (`input[type="search"]`), not by class
   name, so .search / .searchbox / .gsearch and any future wrapper all qualify
   without this sheet having to know their names. */
@media (max-width: 560px) {
  .hero { padding: var(--s5) 0 var(--s4); }

  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) {
    display: flex; flex-direction: column;
  }
  /* The five-element hero, in the order CoverCheck proved: headline, subhead,
     ACTION, visual, trust — then everything else. `.hero-cta` and `.hero-set`
     are the generic opt-in hooks for sites whose action and set piece are not
     a search box (CostCompare's pills, NameCurve's ledger curve). */
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > * { order: 7; }
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .eyebrow,
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .kicker { order: 1; }
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > h1 { order: 2; }
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .lede { order: 3; margin-bottom: 0; }
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .hero-act,
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .hero-cta,
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > input[type="search"],
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > :has(input[type="search"]) { order: 4; }
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .hero-set { order: 5; }
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .srcline,
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .srcline-min { order: 6; }
  /* the armour band is a footnote to the answer, not a gate in front of it */
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > .ymyl,
  .hero > .wrap:has(.hero-act, .hero-cta, input[type="search"]) > :has(> .ymyl) { order: 8; }
}
h1 .unit { font-family: var(--sans); font-size: .42em; font-weight: 500; color: var(--muted); letter-spacing: 0; }

/* The generated one-sentence answer. Left accent bar, no card chrome. */
.answer {
  max-width: var(--measure); margin: var(--s4) 0 0; padding: var(--s4) var(--s4) var(--s4) var(--s4);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); box-shadow: none; text-align: left;
}
.answer p { color: var(--ink-2); font-size: 1.02rem; line-height: 1.6; }
.answer p + p { margin-top: var(--s2); }
.answer b, .answer strong { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }

/* THE hero numeral. Sans, proportional figures, optically large. This is the
   "answer above the fold" and it is the same component on every site — only
   the accent and the unit change. */
.answer .big, .figure-hero {
  display: block; font-family: var(--sans);
  font-size: clamp(2.8rem, 8vw, 4.8rem); font-weight: 650;
  letter-spacing: -.045em; line-height: .96; color: var(--ink);
  font-variant-numeric: proportional-nums; margin-bottom: var(--s2);
}
.answer .big { color: var(--accent); }
.figure-unit { font-size: .94rem; font-weight: 500; color: var(--muted); letter-spacing: 0; margin-top: var(--s2); }

/* Hairline stat row — not boxes. gap:0 and right-hairlines, zero left padding
   so the first stat sits flush with the wrap gutter. */
.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 0; border-top: 1px solid var(--line); max-width: none; margin: var(--s4) 0 0;
}
.stat {
  background: none; border: 0; border-right: 1px solid var(--line); border-radius: 0;
  padding: 18px 22px 18px 0; text-align: left; box-shadow: none;
}
.stat:last-child { border-right: 0; }
@media (max-width: 720px) {
  .stat { border-right: 0; border-bottom: 1px solid var(--line); padding-right: 0; }
  .stat:last-child { border-bottom: 0; }
}
.stat .k { display: block; font-size: .71rem; font-weight: 600; letter-spacing: .11em; text-transform: uppercase; color: var(--muted); margin-bottom: 0; }
.stat .v { font-size: 1.72rem; font-weight: 620; letter-spacing: -.032em; margin-top: 7px; line-height: 1.1; font-variant-numeric: proportional-nums; }
.stat .s { display: block; font-size: .82rem; color: var(--muted); margin-top: 3px; }

/* ==================================================== 4. sections & rhythm */
.section { padding: var(--s6) 0; }
h2.sec {
  font-family: var(--serif); font-size: clamp(1.4rem, 3vw, 1.95rem); font-weight: 500;
  letter-spacing: -.024em; margin-bottom: var(--s2);
  padding-bottom: 14px; border-bottom: 1px solid var(--line);
}
h3.sub { font-size: 1.08rem; font-weight: 500; letter-spacing: -.018em; margin: var(--s4) 0 var(--s2); }
.sec-note { color: var(--muted); font-size: .93rem; margin-bottom: var(--s4); max-width: var(--measure); }
.prose { max-width: var(--measure); }
.prose h2 { font-size: 1.45rem; font-weight: 500; letter-spacing: -.024em; margin: var(--s5) 0 var(--s2); }
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-size: 1.08rem; font-weight: 500; margin: var(--s4) 0 var(--s2); }
.prose p, .prose li { max-width: var(--measure); }
.prose a { font-weight: 500; }
.upd { font-size: .82rem; color: var(--muted); margin-top: var(--s5); padding-top: var(--s3); border-top: 1px solid var(--line); }

/* --------------------------------------------------- the dated source line
   Doctrine #7 as a design element: every figure carries its provenance and
   its vintage, in the same place, on every page in the ring. */
.src, .srcline {
  display: block; font-size: .82rem; color: var(--muted); line-height: 1.55;
  margin-top: var(--s3); padding-top: 10px; border-top: 1px solid var(--line);
  max-width: var(--measure);
}
.src a, .srcline a { color: var(--muted); text-decoration: underline; text-decoration-color: var(--line-2); }
.src a:hover, .srcline a:hover { color: var(--accent); }
.srcline b { color: var(--ink-2); font-weight: 600; }

/* ================================================================ 5. tables
   Typeset, not boxed: no vertical rules, no zebra, no inset. Column 1 aligns
   flush with the wrap gutter; numerals align flush right and are tabular. */
.tablewrap {
  overflow-x: auto; border: 0; border-top: 1px solid var(--line-2);
  border-radius: 0; background: none; box-shadow: none; -webkit-overflow-scrolling: touch;
}
table.data { width: 100%; border-collapse: collapse; font-size: .94rem; min-width: 440px; }
table.data caption { text-align: left; padding: 0 0 12px; font-size: .82rem; color: var(--muted); caption-side: top; }
table.data th, table.data td {
  padding: 11px 16px 11px 0; text-align: right; border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
table.data th:first-child, table.data td:first-child { text-align: left; font-variant-numeric: normal; white-space: normal; }
table.data th:last-child, table.data td:last-child { padding-right: 0; }
table.data thead th {
  position: sticky; top: 0; background: var(--paper);
  font-size: .7rem; font-weight: 600; letter-spacing: .11em; text-transform: uppercase;
  color: var(--muted); z-index: 1; border-bottom: 1px solid var(--line-2);
}
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--paper-2); }
table.data td a { text-decoration: none; font-weight: 550; color: var(--ink); border-bottom: 1px solid var(--line-2); }
table.data td a:hover { color: var(--accent); border-color: var(--accent); }
table.data .hi { color: var(--accent); font-weight: 600; }
table.data tr.self { background: var(--accent-soft); }
table.data tr.self td { font-weight: 600; }
.tnote { font-size: .82rem; color: var(--muted); margin-top: 10px; }

/* ================================================================ 6. search */
.search { position: relative; max-width: 560px; margin: var(--s4) 0 0; }
.search input {
  width: 100%; padding: 14px 18px 14px 46px; font-size: 1rem; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--line-2); box-shadow: none;
  font-family: var(--sans); color: var(--ink);
}
.search input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.search .ico { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; color: var(--muted); pointer-events: none; }

/* ---- 6.1 the hero action affordance ---------------------------------- */
/* DESIGN-MODERNISATION-2026-08 §3.2. On the ring the search box IS the primary
   action, but a bare input reads as a form field, not as an invitation — the
   static hero scorer kept marking these heroes "no CTA" and it was right about
   the affordance even when it was wrong about the element. .hero-act is the
   line that names the action and the size of the corpus behind it
   ("Search 1,799 foods ->"), plus a compact provenance chip, so the reader
   learns what the site HAS before deciding to type.
   Additive: a site opts in by passing `heroAct` to shell(). No colour is
   introduced that is not already in the palette (accent, muted, line), so this
   rule cannot move a contrast pair. */
.hero-act {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 16px;
  max-width: 560px; margin: 10px 0 0;
}
.hero-act .hero-act-go {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: .78rem; letter-spacing: .04em;
  color: var(--accent); text-decoration: none;
  border-bottom: 1px solid var(--accent-line);
  padding-bottom: 1px;
}
.hero-act .hero-act-go:hover { border-bottom-color: var(--accent); }
.hero-act .srcline-min {
  margin: 0; padding-top: 0; border-top: 0; flex: 1 1 auto; min-width: 15ch;
}
.qr {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); overflow: hidden; text-align: left; max-height: 60vh; overflow-y: auto;
}
.qr a { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 11px 16px; text-decoration: none; color: var(--ink); font-size: .92rem; font-weight: 500; border-bottom: 1px solid var(--line); }
.qr a:last-child { border-bottom: 0; }
.qr a:hover { background: var(--paper-2); color: var(--accent); }
.qr a em { font-style: normal; font-size: .78rem; color: var(--muted); flex: none; }
.qr-none { padding: 14px 16px; color: var(--muted); font-size: .88rem; }
.qr a.on { background: var(--paper-2); color: var(--accent); }
.qr-more {
  padding: 9px 16px; margin: 0; font-family: var(--mono); font-size: .72rem;
  letter-spacing: .03em; color: var(--muted); background: var(--paper-2);
  border-top: 1px solid var(--line);
}

/* ---- 6.2 the header search box --------------------------------------- */
/* SEARCH-EVERYWHERE-2026-08. Search used to exist on 21 homepages and on none
   of the ~180,000 deep pages, which is exactly backwards: organic readers land
   deep. This box ships in .head-right on every page, so it has to be quiet
   enough to sit beside the nav and reachable enough to be the way people move
   around a 37,802-page site.

   Desktop: a 210px field that widens to 300px on focus.
   <=860px:  the header wraps and the box takes its own full-width row, still
             visible. It is NOT collapsed behind a tap-to-open magnifier —
             that needs JS and hides the one control that replaces a nav a
             phone cannot show. 38px tall, so the fold cost at 375px is one
             compact row. */
.hsearch { position: relative; flex: 0 1 210px; min-width: 0; }
.hsearch input {
  width: 100%; padding: 8px 12px 8px 34px; font-size: .88rem;
  border-radius: var(--radius-sm); background: var(--paper-2);
  border: 1px solid var(--line-2); box-shadow: none;
  font-family: var(--sans); color: var(--ink);
  text-overflow: ellipsis; transition: flex-basis .16s, border-color .16s, box-shadow .16s;
}
.hsearch input::placeholder { color: var(--muted); }
.hsearch input:focus {
  outline: none; background: var(--surface);
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.hsearch:focus-within { flex-basis: 300px; }
.hsearch .ico {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--muted); pointer-events: none;
}
/* the panel is right-aligned under a header box so it cannot overflow the
   viewport on the right edge, and wide enough to read a long row */
.hsearch .qr { left: auto; right: 0; min-width: 320px; max-width: 92vw; }
.hsearch input::-webkit-search-cancel-button { cursor: pointer; }

@media (max-width: 860px) {
  /* Two different headers carry this box: the ring shell's .head-right, and
     the .masthead-in used by SchoolFinder and SuburbIQ. Both have to be
     allowed to wrap or the full-width search row cannot take its own line.

     The box is a direct child of .masthead-in, NOT of the .nav inside it —
     measured at 375px, a box inside .nav came out 231px wide and truncated its
     own placeholder, because .nav is only as wide as what the brand leaves it.
     Wrapping has to happen on the element that spans the header. */
  .head-in .head-right, .masthead-in { flex-wrap: wrap; }
  .hsearch { flex: 1 0 100%; order: 9; }
  .hsearch input { padding-top: 9px; padding-bottom: 9px; }
  .hsearch .qr { left: 0; right: 0; min-width: 0; }
}

/* ========================================================== 7. link grids */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 0; border-top: 1px solid var(--line); }
.grid a {
  display: block; padding: 13px 18px 13px 0; text-decoration: none; color: var(--ink);
  background: none; border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  font-size: .93rem; font-weight: 500; transition: color .16s; box-shadow: none;
}
.grid a:hover { color: var(--accent); transform: none; border-color: var(--line-2); }
.grid a small { display: block; font-weight: 400; color: var(--muted); font-size: .8rem; margin-top: 2px; }
.grid-tight { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); }
.chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 14px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line-2); text-decoration: none;
  color: var(--ink-2); font-size: .85rem; font-weight: 500; transition: .16s; box-shadow: none;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip b { color: var(--muted); font-weight: 500; font-size: .78rem; }

.az { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: var(--s4); }
.az a { width: 34px; height: 34px; display: grid; place-items: center; border-radius: var(--radius-sm); border: 1px solid var(--line-2); background: var(--surface); text-decoration: none; font-size: .85rem; font-weight: 600; color: var(--ink-2); }
.az a:hover { background: var(--accent); color: #fff; border-color: var(--accent); }

.pager { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-top: var(--s5); }
.pager a, .pager span { min-width: 38px; height: 38px; padding: 0 11px; display: inline-grid; place-items: center; border-radius: var(--radius-sm); border: 1px solid var(--line-2); background: var(--surface); text-decoration: none; font-size: .87rem; font-weight: 550; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.pager a:hover { border-color: var(--accent); color: var(--accent); }
.pager .on { background: var(--accent); color: #fff; border-color: var(--accent); }
.pager .gap { border: 0; background: none; color: var(--muted); }

/* ================================================================== 8. FAQ */
.faqs { max-width: var(--measure); }
.faqs details { border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: none; margin-bottom: 0; box-shadow: none; }
.faqs summary { padding: 15px 0; font-weight: 550; font-size: .97rem; cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.faqs summary::-webkit-details-marker { display: none; }
.faqs summary::after { content: "+"; color: var(--muted); font-size: 1.3rem; font-weight: 400; line-height: 1; flex: none; }
.faqs details[open] summary::after { content: "−"; }
.faqs details[open] summary { border-bottom: 0; }
.faqs .a { padding: 0 0 18px; color: var(--ink-2); font-size: .95rem; line-height: 1.65; max-width: 66ch; }
.faqs .a p + p { margin-top: 10px; }

/* ================================================ 9. header / netbar / foot */
.netbar { background: var(--ink); }
:root[data-theme="dark"] .netbar { background: #0B0D0F; border-bottom: 1px solid var(--line); }
.netbar-links a { display: inline-flex; align-items: center; gap: 6px; font-weight: 450; }
.nb-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; flex: none; }

.site-head {
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  border-bottom: 1px solid var(--line);
}
.brand { font-family: var(--serif); font-weight: 600; letter-spacing: -.02em; font-size: 1.1rem; }
.head-nav a { font-weight: 500; }
.head-nav a:hover { background: var(--paper-2); }
.icon-btn { border-radius: var(--radius-sm); border-color: var(--line-2); }
.icon-btn:hover { transform: none; }

.site-foot { padding: var(--s5) 0 var(--s6); border-top: 1px solid var(--line); background: var(--paper-2); }
.foot-in { align-items: flex-start; }
.foot-col { max-width: 46ch; }
.foot-brand { font-family: var(--serif); font-weight: 500; color: var(--ink) !important; font-size: 1.02rem !important; margin-bottom: 6px; letter-spacing: -.02em; }
.foot-note { font-size: .85rem !important; line-height: 1.55; color: var(--muted); }
.foot-disclaimer { margin-top: var(--s2); }
.foot-links a { color: var(--ink-2); font-size: .88rem; }
.foot-links a:hover { color: var(--accent); }
.foot-legal { margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--line); }
.foot-legal p { font-size: .8rem; color: var(--muted); max-width: 88ch; }

/* ===================================================== 10. the footer ring */
.ring { padding: var(--s6) 0; border-top: 1px solid var(--line); background: var(--paper); }
.ring-h { font-family: var(--sans); font-size: .7rem; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: var(--muted); margin-bottom: var(--s4); }
.ring-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(238px, 1fr)); gap: var(--s3); }
.ring-card {
  display: block; text-decoration: none; color: inherit; padding: 20px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: none; transition: border-color .16s;
  border-top: 2px solid var(--card-accent, var(--accent));
}
.ring-card:hover { transform: none; border-color: var(--line-2); border-top-color: var(--card-accent, var(--accent)); }
.ring-card h3 { font-family: var(--serif); font-size: 1.05rem; font-weight: 500; letter-spacing: -.02em; margin-bottom: 6px; }
.ring-card p { font-size: .87rem; color: var(--muted); line-height: 1.55; }
.ring-card .go { display: inline-block; margin-top: 11px; font-size: .8rem; font-weight: 600; color: var(--card-accent, var(--accent)); font-family: var(--sans); }
/* The single bridge card to the other cluster reads as a footnote, not a
   sibling — a dashed edge says "different family" without extra copy. */
.ring-bridge { border-style: dashed; border-top-style: solid; }
.ring-more { margin-top: var(--s4); font-size: .88rem; }
.ring-more a { color: var(--ink-2); text-decoration: underline; text-decoration-color: var(--line-2); }
.ring-more a:hover { color: var(--accent); }

/* ================================================= 11. the /network hub page */
.hub-list { border-top: 1px solid var(--line-2); max-width: 980px; }
.hub-row {
  display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; gap: var(--s3);
  align-items: baseline; padding: 20px 0; border-bottom: 1px solid var(--line);
}
.hub-dot { width: 9px; height: 9px; border-radius: 50%; align-self: center; }
.hub-name { font-family: var(--serif); font-size: 1.22rem; font-weight: 500; letter-spacing: -.02em; }
.hub-name a { text-decoration: none; color: var(--ink); border-bottom: 1px solid var(--line-2); }
.hub-name a:hover { color: var(--accent); border-color: var(--accent); }
.hub-name em { font-style: normal; font-family: var(--sans); font-size: .7rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-left: 8px; }
.hub-blurb { font-size: .95rem; color: var(--ink-2); line-height: 1.55; max-width: 66ch; margin-top: 4px; }
.hub-host { font-size: .8rem; color: var(--faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.hub-row.is-self { background: var(--paper-2); }
@media (max-width: 640px) { .hub-row { grid-template-columns: 10px minmax(0, 1fr); } .hub-host { display: none; } }

/* =========================================== 12. the entity bridge (§2.3)
   The one sanctioned cross-cluster contextual link, set as an editorial
   pull-quote rather than a card: it is an argument ("the same food, counted
   two ways"), and it should read like one. */
.entity-bridge {
  max-width: var(--measure); margin: var(--s5) 0;
  padding: var(--s2) 0 var(--s2) var(--s4);
  border-left: 3px solid var(--accent);
}
.eb-kicker { font-family: var(--sans); font-size: .7rem; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: var(--muted); margin-bottom: 10px; }
.eb-body { font-family: var(--serif); font-size: 1.16rem; font-weight: 400; line-height: 1.5; letter-spacing: -.015em; color: var(--ink); }
.eb-go { margin-top: 12px; font-size: .95rem; font-weight: 550; }
.eb-go a { color: var(--accent); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
.eb-go a:hover { border-color: var(--accent); }
.eb-go a::after { content: " →"; }

/* --------------------------------------------------- the entity handoff (§(c)4)
   The ring generalisation of the tools cluster's sticky loop: "you have the
   answer about X — here is the next question about X, on the site that owns
   it." Deliberately QUIETER than .entity-bridge: the bridge makes an argument
   and earns a pull-quote, the handoff is furniture at the end of the road, and
   two pull-quotes on one page would compete. Renders only when the resolver
   verified a destination, so it is never a generic "see also". */
.handoff {
  max-width: var(--measure); margin: var(--s5) 0 var(--s4);
  padding-top: var(--s3); border-top: 1px solid var(--rule);
}
.ho-k { font-family: var(--sans); font-size: .7rem; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
.ho-go { font-size: 1.05rem; font-weight: 550; }
.ho-go a { color: var(--accent); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
.ho-go a:hover { border-color: var(--accent); }
.ho-go a::after { content: " →"; }
.ho-why { margin-top: 8px; font-size: .92rem; color: var(--muted); }

/* ============================================== 13. the house-ad slot (§2.5)
   Deliberately the ONLY dark, cinematic surface in the system. It should not
   look like editorial content, because it is not: it is labelled, nofollowed
   and UTM'd. Keeping it visually foreign is honest and also protects the
   editorial voice of everything around it. */
.pf-cinema {
  position: relative; max-width: 980px; margin: 0 auto; overflow: hidden;
  border-radius: var(--radius-lg); border: 1px solid rgba(255, 255, 255, .08);
  background: #05070f; box-shadow: var(--shadow-lg); text-align: center;
}
.pfc-scene { position: absolute; inset: 0; pointer-events: none; }
.pfc-scene::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(900px 420px at 50% 118%, #f97316 -18%, rgba(249, 115, 22, .28) 22%, transparent 60%),
    radial-gradient(600px 300px at 82% -10%, rgba(124, 140, 255, .22), transparent 65%),
    linear-gradient(180deg, #0b1224 0%, #0a0f1e 45%, #120b18 100%);
}
.pfc-stars {
  position: absolute; inset: 0 0 34% 0; opacity: .8;
  background-image:
    radial-gradient(1.2px 1.2px at 12% 28%, #fff, transparent),
    radial-gradient(1px 1px at 31% 12%, rgba(255,255,255,.8), transparent),
    radial-gradient(1.4px 1.4px at 47% 34%, rgba(255,255,255,.9), transparent),
    radial-gradient(1px 1px at 63% 18%, rgba(255,255,255,.7), transparent),
    radial-gradient(1.3px 1.3px at 78% 30%, #fff, transparent),
    radial-gradient(1px 1px at 89% 10%, rgba(255,255,255,.75), transparent),
    radial-gradient(1px 1px at 22% 48%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.1px 1.1px at 55% 52%, rgba(255,255,255,.55), transparent);
  animation: pfc-twinkle 5.5s ease-in-out infinite alternate;
}
@keyframes pfc-twinkle { from { opacity: .45; } to { opacity: .95; } }
.pfc-ridge {
  position: absolute; left: -4%; right: -4%; bottom: 0; height: 34%;
  background: linear-gradient(180deg, transparent 0%, #05070f 62%);
  clip-path: polygon(0 78%, 9% 58%, 18% 70%, 29% 40%, 38% 62%, 50% 30%, 61% 58%, 71% 44%, 82% 66%, 92% 50%, 100% 72%, 100% 100%, 0 100%);
  background-color: #060910;
}
.pfc-sweep {
  position: absolute; top: -30%; bottom: -30%; width: 34%; left: -40%;
  background: linear-gradient(100deg, transparent 0%, rgba(255, 209, 102, .07) 42%, rgba(255, 255, 255, .13) 50%, rgba(255, 209, 102, .07) 58%, transparent 100%);
  transform: skewX(-14deg);
  animation: pfc-sweep 7s cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes pfc-sweep { 0% { left: -45%; } 55% { left: 115%; } 100% { left: 115%; } }
@media (prefers-reduced-motion: reduce) { .pfc-sweep, .pfc-stars { animation: none; } }

.pfc-in { position: relative; padding: 40px 26px 34px; }
.pfc-adlabel { font-family: var(--sans); font-size: .62rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: rgba(255, 255, 255, .38); margin-bottom: 16px; }
.pfc-hook { font-family: var(--serif); font-size: 1rem; color: rgba(255, 255, 255, .72); font-style: italic; margin-bottom: 14px; }
.pfc-title { font-family: var(--sans); font-size: clamp(1.7rem, 5.4vw, 3rem); font-weight: 700; letter-spacing: .04em; color: #fff; line-height: 1.04; margin-bottom: 12px; }
.pfc-title span { background: linear-gradient(92deg, #ffd166, #f97316 55%, #fb7185); -webkit-background-clip: text; background-clip: text; color: transparent; }
.pfc-sub { color: rgba(255, 255, 255, .74); font-size: .97rem; max-width: 56ch; margin: 0 auto 22px; line-height: 1.6; }
.pfc-sub strong { color: #ffd166; }
.pfc-count { display: flex; justify-content: center; align-items: flex-start; gap: 8px; margin-bottom: 24px; }
.pfc-cell { min-width: 64px; padding: 10px 8px 8px; border-radius: 10px; background: rgba(255, 255, 255, .05); border: 1px solid rgba(255, 255, 255, .1); }
.pfc-cell b { display: block; font-size: clamp(1.4rem, 4vw, 2rem); font-weight: 650; color: #fff; font-variant-numeric: tabular-nums; line-height: 1; }
.pfc-cell i { display: block; font-style: normal; font-size: .62rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: rgba(255, 255, 255, .5); margin-top: 5px; }
.pfc-sep { color: rgba(255, 255, 255, .3); font-size: 1.5rem; font-weight: 600; line-height: 2.2; }
.pfc-btn {
  display: inline-flex; align-items: center; gap: 10px; padding: 14px 28px; border-radius: 10px;
  background: linear-gradient(93deg, #ffd166, #f97316); color: #1a0f02;
  text-decoration: none; font-weight: 700; font-size: 1rem; letter-spacing: -.01em;
  transition: transform .16s;
}
.pfc-btn svg { width: 18px; height: 18px; }
.pfc-btn:hover { transform: translateY(-1px); }
.pfc-note { margin-top: 14px; font-size: .76rem; color: rgba(255, 255, 255, .4); }
/* Phone compaction (2026-08-04).

   Measured on a real 375x812 phone viewport, this unit rendered 593px tall —
   73% of the screen — because every element kept its desktop rhythm and the CTA
   label wrapped onto a second line (an 80px button, taller than the countdown).
   A single ad that fills three-quarters of the screen reads as an interstitial,
   which is the exact thing the "one unit per page, always after real content"
   rule in §2.5 exists to prevent. Target here is ~50% of a phone viewport.

   Breakpoint is 560px, not the old 520px: the common large phones (Pro Max /
   Ultra class, 428-430px CSS px) and every phone in landscape-ish widths sat
   just inside the old query but still needed the compaction. */
@media (max-width: 560px) {
  .pfc-in { padding: 24px 15px 22px; }
  .pfc-adlabel { margin-bottom: 10px; }
  .pfc-flag { margin-bottom: .55rem; }

  /* The hook is a decorative italic teaser and the sub-headline underneath
     already states the same offer in plainer words. On a phone it is the one
     line that can go: dropping it removes ~66px of stacked text above the
     headline without losing a single fact or claim. */
  .pfc-hook { display: none; }

  .pfc-title { font-size: clamp(1.45rem, 7vw, 1.9rem); margin-bottom: 8px; }
  .pfc-sub { font-size: .89rem; line-height: 1.5; margin-bottom: 16px; }

  .pfc-count { gap: 5px; margin-bottom: 18px; }
  .pfc-cell { min-width: 50px; padding: 7px 5px 6px; }
  .pfc-cell b { font-size: 1.3rem; }
  .pfc-sep { font-size: 1.2rem; line-height: 2.1; }

  /* Full-width block button. Inline-flex + horizontal padding is what let the
     label wrap; going full width and centring keeps it on one line and gives a
     comfortably large tap target at the same time. */
  .pfc-btn { display: flex; width: 100%; justify-content: center; padding: 13px 16px; font-size: .95rem; }
  .pfc-note { margin-top: 10px; }
}

/* ------------------------------------------- 13b. the poster variant (art)
   2026-08-10. A campaign that supplies `art` renders as a two-column poster:
   photograph on the left, the offer on the right. Everything here is scoped to
   .pfc-hasart, so a campaign WITHOUT art (GymFinder) is byte-for-byte the
   centred unit above and needed no edit to stay that way.

   The photograph is warm and bright and the slot is near-black, so the two are
   joined by a fade rather than butted together: .pfc-artfade carries the seam
   in whichever direction the layout is running (a hard edge read as two
   unrelated boxes glued side by side, which is what a cheap ad looks like).

   The ad label moves ONTO the artwork in this variant (the markup branch in
   houseAd()). It is not being hidden — same text, same size, still first in
   the source order, still inside the labelled aside. Moving it recovers the
   ~34px it used to cost above the headline, which is part of what pays for the
   photo on a phone (see the 560px target in §13 above).

   PHONE HEIGHT, HONESTLY. §13 sets a ~50% of viewport target for this unit and
   this variant does not meet it: measured at 375x812 it is 614px, 76%, against
   427px for the same campaign with no picture. A photograph costs vertical
   space and there is no arrangement of a portrait frame plus six lines of copy
   that does not. Two cheaper options were built and rejected: a 2.35:1 band
   (hits ~66% but crops to a torso, so the photo stops being a person) and the
   photo as an 88%-scrimmed background (hits the 50% target and loses the reason
   the photo is here at all). If this unit is ever measured as hurting phone
   engagement, the lever is `art` — remove it from the campaign and the centred
   text creative comes back with no other change. */
.pfc-hasart { text-align: left; }
.pfc-art .pfc-adlabel {
  position: absolute; z-index: 3; top: 11px; left: 14px; margin: 0;
  color: rgba(255, 255, 255, .82); text-shadow: 0 1px 8px rgba(0, 0, 0, .75);
}
.pfc-art { position: relative; z-index: 1; overflow: hidden; background: #120b18; }
.pfc-art img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  /* Her raised hand is near the top of the frame and her face sits at ~38% —
     a centred crop decapitates the pose on every short viewport. */
  object-position: 50% 30%;
}
.pfc-art .pfc-artfade { position: absolute; inset: 0; pointer-events: none; }
/* The AI hallmark. Same mark, same wording rule and same reason as the yoga
   banner's .yb-ai: Geeta does not exist, and a photograph of a person who does
   not exist ships with the disclosure attached to it, not buried in a policy
   page. The small print under the CTA carries the sentence version. */
.pfc-art .pfc-ai {
  position: absolute; z-index: 2; top: 12px; right: 12px;
  font-family: var(--sans); font-size: .58rem; font-weight: 700; letter-spacing: .16em;
  padding: 4px 8px; border-radius: 999px; color: rgba(255, 255, 255, .82);
  background: rgba(8, 10, 18, .58); border: 1px solid rgba(255, 255, 255, .22);
  backdrop-filter: blur(4px);
}
.pfc-prov { display: block; margin-top: 3px; color: rgba(255, 255, 255, .34); }

/* ---- phone and small tablet: photo is the header band, copy underneath. */
@media (max-width: 759px) {
  /* 16:9, and the crop is pulled UP to 18%: at 2.35:1 the band was a torso and
     a forearm — a texture, not a person. 18% of the vertical overflow puts the
     raised hand at the top edge and the hips at the bottom, so the pose still
     reads at 333px wide. */
  .pfc-hasart .pfc-art { aspect-ratio: 16 / 9; }
  .pfc-hasart .pfc-art img { object-position: 50% 18%; }
  .pfc-hasart .pfc-artfade {
    background: linear-gradient(180deg, rgba(5, 7, 15, .42) 0%, transparent 30%, rgba(5, 7, 15, .58) 78%, #05070f 100%);
  }
  .pfc-hasart .pfc-in { padding-top: 18px; }
}

/* ---- desktop / tablet: the poster proper. 42% is the widest the photo can be
   before the sub-headline drops under 44 characters a line, which is where the
   three-line paragraph becomes five. */
@media (min-width: 760px) {
  .pfc-hasart { display: grid; grid-template-columns: 42% 1fr; align-items: stretch; }
  .pfc-hasart .pfc-art { min-height: 100%; }
  .pfc-hasart .pfc-artfade {
    background:
      linear-gradient(90deg, transparent 40%, rgba(5, 7, 15, .5) 78%, #05070f 100%),
      linear-gradient(180deg, rgba(5, 7, 15, .3) 0%, transparent 30%);
  }
  .pfc-hasart .pfc-in { padding: 44px 40px 38px 34px; align-self: center; }
  .pfc-hasart .pfc-sub { margin-left: 0; margin-right: 0; max-width: 46ch; }
  .pfc-hasart .pfc-count { justify-content: flex-start; }
  .pfc-hasart .pfc-adlabel { color: rgba(255, 255, 255, .7); }
}

/* legacy blocks still referenced by a few builds — retokened, not removed */
.pf-card { max-width: 860px; margin: 0 auto; border-radius: var(--radius-lg); overflow: hidden; background: #0d1117; border: 1px solid rgba(255,255,255,.09); position: relative; }
.pf-in { position: relative; padding: 32px; }
.pf-eyebrow { display: inline-block; font-size: .7rem; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: rgba(255,255,255,.6); margin-bottom: 12px; }
.pf-title { font-family: var(--serif); font-size: clamp(1.3rem, 3.4vw, 1.85rem); font-weight: 500; letter-spacing: -.024em; color: #fff; line-height: 1.15; margin-bottom: 11px; max-width: 24ch; }
.pf-body { color: rgba(255,255,255,.7); font-size: .96rem; line-height: 1.62; max-width: 60ch; margin-bottom: 20px; }
.pf-btn { display: inline-flex; align-items: center; gap: 9px; padding: 12px 22px; border-radius: var(--radius-sm); background: #fff; color: #0d1117; text-decoration: none; font-weight: 600; font-size: .94rem; }
.pf-btn svg { width: 17px; height: 17px; }
.pf-note { margin-top: 14px; font-size: .76rem; color: rgba(255,255,255,.4); }
.ik-banner { position: relative; max-width: 980px; margin: 0 auto; overflow: hidden; border-radius: var(--radius-lg); border: 1px solid rgba(255,255,255,.1); background: linear-gradient(115deg, #071426 0%, #0c2b45 46%, #b0491f 88%, #f9a03f 105%); }
.ik-in { position: relative; padding: 30px; display: flex; flex-wrap: wrap; align-items: center; gap: 18px 28px; }
.ik-copy { flex: 1 1 320px; min-width: 0; }
.ik-eyebrow { font-size: .68rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: #f9c06b; margin-bottom: 8px; }
.ik-title { font-family: var(--serif); font-size: clamp(1.25rem, 3.4vw, 1.7rem); font-weight: 500; letter-spacing: -.024em; color: #fff; line-height: 1.18; margin-bottom: 8px; }
.ik-sub { color: rgba(255,255,255,.78); font-size: .92rem; line-height: 1.55; max-width: 52ch; }
.ik-btn { flex: none; display: inline-flex; align-items: center; gap: 9px; padding: 12px 22px; border-radius: var(--radius-sm); background: #fff; color: #0c2b45; text-decoration: none; font-weight: 700; font-size: .95rem; }
.ik-btn svg { width: 16px; height: 16px; }

/* ========================================================== 14. calculators
   State-change feedback only. The result updates instantly; nothing animates
   in, nothing slides, nothing fades. */
.calc {
  max-width: 620px; margin: var(--s4) 0 0; padding: var(--s4); text-align: left;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius); box-shadow: none;
}
.calc .row { grid-template-columns: 1fr 1fr; gap: var(--s3); }
@media (max-width: 520px) { .calc .row { grid-template-columns: 1fr; } }
.calc input, .calc select { font-family: var(--sans); border-radius: var(--radius-sm); border-color: var(--line-2); background: var(--surface); color: var(--ink); font-variant-numeric: tabular-nums; }
.calc input:focus, .calc select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.calc .out { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); text-align: left; }
.calc .out .v { font-family: var(--sans); font-size: clamp(2rem, 7vw, 3rem); font-weight: 650; letter-spacing: -.045em; color: var(--accent); line-height: 1; font-variant-numeric: proportional-nums; }
.calc .out .k { font-size: .84rem; color: var(--muted); margin-top: var(--s2); }
.unit-toggle { display: inline-flex; gap: 3px; background: var(--paper-2); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 3px; }
.unit-toggle button { border: 0; background: none; padding: 5px 11px; border-radius: 3px; font-size: .78rem; font-weight: 550; color: var(--muted); }
.unit-toggle button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: none; border: 1px solid var(--line-2); }
:root[data-theme="dark"] .unit-toggle button[aria-pressed="true"] { background: var(--surface); }
.lbl-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.lbl-row label { margin-bottom: 0; }

/* ======================================================== 15. comparisons */
.vs { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--s3); align-items: stretch; max-width: 820px; margin: var(--s4) 0 0; }
.vs-side { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; text-align: left; box-shadow: none; }
.vs-side.win { border-color: var(--accent); box-shadow: none; border-left-width: 3px; }
.vs-side h2 { font-family: var(--serif); font-size: 1.05rem; font-weight: 500; letter-spacing: -.02em; margin-bottom: 10px; }
.vs-side .v { font-family: var(--sans); font-size: clamp(1.6rem, 5vw, 2.3rem); font-weight: 650; letter-spacing: -.04em; color: var(--accent); font-variant-numeric: proportional-nums; line-height: 1.1; }
.vs-side .s { font-size: .82rem; color: var(--muted); margin-top: 4px; }
.vs-side .badge { display: inline-block; margin-top: 12px; font-size: .68rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); background: var(--accent-soft); padding: 4px 10px; border-radius: 999px; }
.vs-mid { display: grid; place-items: center; font-size: .7rem; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: var(--muted); }
@media (max-width: 620px) { .vs { grid-template-columns: 1fr; } .vs-mid { padding: 4px 0; } }

/* ================================================== 16. the diagram moments
   §5: each page keeps one interactive set piece an AI answer cannot
   replicate. Design and AI-Overview defence are the same work, so these are
   treated as first-class components, not decoration.                        */

/* ---- shared SVG figure chrome -------------------------------------------- */
.figure, .routemap, .plan, .dial-wrap, .bandchart {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--s3); box-shadow: none; overflow: hidden;
}
.figure svg, .routemap svg, .plan svg, .dial-wrap svg, .bandchart svg { width: 100%; height: auto; }
.figure-cap { font-size: .82rem; color: var(--muted); margin-top: 10px; }
/* SVG text scales with the viewBox — on a narrow screen an 11px label renders
   at about 5px. Bump it, or the diagram is decorative rather than readable. */
@media (max-width: 760px) {
  .figure text, .routemap text, .plan text, .dial-wrap text, .bandchart text { font-size: 20px; }
  .figure .lab-lg, .dial-wrap .lab-lg, .bandchart .lab-lg { font-size: 24px; }
}

/* fly — great-circle route strip */
.routemap .land { fill: color-mix(in srgb, var(--ink) 8%, transparent); }
.routemap .grat { stroke: var(--line); stroke-width: .4; fill: none; }
.routemap .arc { stroke: var(--accent); stroke-width: 2; fill: none; stroke-linecap: round; }
.routemap .pin { fill: var(--accent); }
.routemap .plab { fill: var(--ink-2); font-size: 7px; font-weight: 600; font-family: var(--sans); }

/* lux — the ceiling plan, kept as-is structurally and simply retokened */
.plan .room { fill: color-mix(in srgb, var(--accent) 5%, transparent); stroke: var(--ink-2); stroke-width: 1.6; }
.plan .fix { fill: var(--accent); }
.plan .fixring { fill: none; stroke: var(--accent); stroke-width: 1; opacity: .35; }
.plan .dim { stroke: var(--muted); stroke-width: .7; }
.plan .dimtext { fill: var(--muted); font-size: 9px; font-weight: 500; font-family: var(--sans); }
.plan .beam { fill: color-mix(in srgb, var(--accent) 13%, transparent); }

/* read — the pages ↔ hours progress bar (also used by any "X of Y" figure) */
.progress { display: grid; gap: 12px; margin-top: var(--s3); }
.progress-row { display: grid; grid-template-columns: 118px 1fr auto; gap: 14px; align-items: center; }
.progress-track { display: block; height: 22px; position: relative; background: var(--paper-2); border: 1px solid var(--line); border-radius: 0 3px 3px 0; }
.progress-fill { display: block; height: 20px; background: var(--accent); border-radius: 0 3px 3px 0; min-width: 2px; }
.progress-row .lbl { font-size: .9rem; color: var(--ink-2); }
.progress-row .val { font-size: .94rem; font-weight: 620; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.progress-row .cnt { font-size: .78rem; color: var(--faint); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) { .progress-row { grid-template-columns: 92px 1fr auto; gap: 10px; } }

/* cook — the doneness-temperature dial */
.dial-wrap { display: grid; grid-template-columns: minmax(0, 260px) minmax(0, 1fr); gap: var(--s4); align-items: center; }
@media (max-width: 640px) { .dial-wrap { grid-template-columns: 1fr; } }
.dial .track { fill: none; stroke: var(--line); stroke-width: 14; stroke-linecap: round; }
.dial .zone-rare { stroke: #B14A1E; }
.dial .zone-med { stroke: #8A5A14; }
.dial .zone-well { stroke: #2F6B3F; }
.dial .zone { fill: none; stroke-width: 14; opacity: .32; }
.dial .needle { stroke: var(--ink); stroke-width: 3; stroke-linecap: round; transition: transform .18s ease; transform-origin: 100px 100px; }
.dial .hubdot { fill: var(--ink); }
.dial .tick { stroke: var(--line-2); stroke-width: 1.4; }
.dial .ticklab { fill: var(--muted); font-size: 9px; font-family: var(--sans); font-variant-numeric: tabular-nums; }
.dial .readout { fill: var(--ink); font-family: var(--sans); font-size: 26px; font-weight: 650; font-variant-numeric: proportional-nums; }
.dial .readsub { fill: var(--muted); font-family: var(--sans); font-size: 9px; letter-spacing: .12em; text-transform: uppercase; }
@media (prefers-reduced-motion: reduce) { .dial .needle { transition: none; } }
.dial-choices { display: flex; flex-wrap: wrap; gap: 6px; }
.dial-choices button {
  border: 1px solid var(--line-2); background: var(--surface); color: var(--ink-2);
  padding: 8px 14px; border-radius: 999px; font-size: .85rem; font-weight: 550;
}
.dial-choices button[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }

/* sehat — the Asian-Indian vs Western BMI band comparison */
.bandchart .band-w { fill: color-mix(in srgb, var(--ink) 10%, transparent); }
.bandchart .band-a { fill: color-mix(in srgb, var(--accent) 22%, transparent); }
.bandchart .band-edge { stroke: var(--line-2); stroke-width: .8; }
.bandchart .marker { stroke: var(--ink); stroke-width: 2.4; stroke-linecap: round; }
.bandchart .lab { fill: var(--muted); font-size: 10px; font-family: var(--sans); }
.bandchart .lab-lg { fill: var(--ink); font-size: 12px; font-weight: 650; font-family: var(--sans); }
.band-legend { display: flex; flex-wrap: wrap; gap: var(--s3); font-size: .84rem; color: var(--muted); margin-top: 12px; }
.band-legend span { display: inline-flex; align-items: center; gap: 7px; }
.band-legend i { width: 14px; height: 10px; border-radius: 2px; display: inline-block; }
.bandchart { color: var(--ink); position: relative; }
.bandchart svg { display: block; width: 100%; }
.bc-axis { position: relative; height: 18px; margin-top: 2px; }
.bc-axis span { position: absolute; transform: translateX(-50%); font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.bc-rows { display: flex; justify-content: space-between; font-size: .72rem; font-weight: 600; letter-spacing: .11em; text-transform: uppercase; color: var(--muted); margin-top: 4px; }
.bc-slide { display: flex; align-items: center; gap: 12px; margin-top: var(--s3); font-size: .85rem; color: var(--muted); }
.bc-slide input { flex: 1; min-width: 120px; accent-color: var(--accent); }
.bc-slide output { font-family: var(--sans); font-size: 1.15rem; font-weight: 650; color: var(--ink); font-variant-numeric: tabular-nums; min-width: 3.5ch; text-align: right; }

/* thali — katori / roti unit iconography beside every figure */
.unitfig { display: inline-flex; align-items: baseline; gap: 8px; }
.unitfig svg { width: 20px; height: 20px; align-self: center; color: var(--accent); flex: none; }
.unit-icon { color: var(--accent); vertical-align: -.2em; width: 18px; height: 18px; display: inline-block; flex: none; }
.unit-icon-lg { width: clamp(30px, 6vw, 46px); height: clamp(30px, 6vw, 46px); stroke-width: 1.2; }
.assume svg { color: var(--accent); }
.serving-note { font-size: .84rem; color: var(--muted); display: flex; align-items: center; gap: 8px; margin-top: 6px; }

/* gym — city page hero: count + a map-ish locality strip */
.city-hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--s5); align-items: end; }
@media (max-width: 900px) { .city-hero { grid-template-columns: 1fr; gap: var(--s4); } }
.citystrip { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); padding: var(--s3); }
.citystrip svg { width: 100%; height: auto; }
.citystrip .cs-grid { stroke: var(--line); stroke-width: .6; }
.citystrip .cs-dot { fill: var(--accent); opacity: .55; }
.citystrip .cs-dot.is-big { opacity: 1; }
.citystrip .cs-lab { fill: var(--muted); font-size: 8px; font-family: var(--sans); }
.citystrip-cap { font-size: .8rem; color: var(--muted); margin-top: 10px; }

/* ============================================================== 17. callouts */
.note {
  border: 0; border-left: 3px solid var(--accent); background: none;
  border-radius: 0; padding: 2px 0 2px var(--s3); font-size: .93rem; color: var(--ink-2); line-height: 1.6;
  max-width: var(--measure);
}
.note strong { color: var(--ink); }

/* ============================================================== 18. buttons */
.btn { border-radius: var(--radius-sm); font-weight: 600; letter-spacing: -.01em; }
.btn-primary { background: var(--accent); color: #fff; box-shadow: none; }
.btn-primary:hover { transform: none; background: var(--accent); filter: brightness(.92); }
.btn-ghost { background: var(--surface); border-color: var(--line-2); color: var(--ink-2); box-shadow: none; }
.btn-ghost:hover { transform: none; border-color: var(--accent); color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-sm); }

/* --- accent derivation (must live on body, not :root) ----------------------
   shell() injects the hue as <body style="--site-accent:…">. Custom properties
   inherit DOWNWARD only, so the :root derivation above can never see it and
   every site silently painted the neutral slate fallback. Re-derived here, on
   the element that actually carries the variable. Dark cuts override --accent
   directly below, because the inline --site-accent outranks any stylesheet. */
body {
  --accent: var(--site-accent, #3C4A55);
  --accent-2: var(--site-accent, #3C4A55);
  --accent-3: var(--site-accent, #3C4A55);
  --accent-soft: color-mix(in srgb, var(--accent) 9%, var(--paper));
  /* AAA+++ FIX 2026-07-31: these two shipped in :root, where --accent still
     resolves to the #3C4A55 fallback because --site-accent is written on
     <body>. Result: on EVERY Identity A site the kickers, method step numbers
     and the .formula wash painted slate next to a correctly-accented keel.
     Same reason --accent-soft is redeclared here — they must live on the
     element that actually carries the variable. */
  --accent-deep: color-mix(in srgb, var(--accent) 78%, var(--ink));
  --accent-wash: color-mix(in srgb, var(--accent) 7%, var(--paper));
}

/* ==================================================== 19. per-site accents
   ONE hue per site, set on <body> by shell(). These blocks exist so that the
   dark theme can swap in a lighter cut of the same hue — a #7C5A22 ochre that
   is correct on warm paper is unreadable on near-black.
   Cluster G: slate / ochre / moss editorial range.
   Cluster I: a warmer band — saffron, deep green, chai brown — so the India
   ring reads as the same family at a different temperature.
   Every value below is validated by _shared/contrast-check.mjs.             */
:root[data-theme="dark"] body[data-site="burn"]  { --accent: #E8875A; --accent-2: #E8875A; --accent-3: #E8875A; --accent-soft: color-mix(in srgb, #E8875A 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="fuel"]  { --accent: #6FBF86; --accent-2: #6FBF86; --accent-3: #6FBF86; --accent-soft: color-mix(in srgb, #6FBF86 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="fly"]   { --accent: #63B4D6; --accent-2: #63B4D6; --accent-3: #63B4D6; --accent-soft: color-mix(in srgb, #63B4D6 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="lux"]   { --accent: #D9A85C; --accent-2: #D9A85C; --accent-3: #D9A85C; --accent-soft: color-mix(in srgb, #D9A85C 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="watts"] { --accent: #A79BE8; --accent-2: #A79BE8; --accent-3: #A79BE8; --accent-soft: color-mix(in srgb, #A79BE8 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="read"]  { --accent: #E4849B; --accent-2: #E4849B; --accent-3: #E4849B; --accent-soft: color-mix(in srgb, #E4849B 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="mpg"]   { --accent: #5FC0AC; --accent-2: #5FC0AC; --accent-3: #5FC0AC; --accent-soft: color-mix(in srgb, #5FC0AC 14%, var(--paper)); }
/* FIX 2026-08-04: salary had NO dark cut, so PayLens rendered its light
   accent #2F5D50 straight onto basalt — 2.44:1, unreadable. It went unnoticed
   because the site was never listed in contrast-check.mjs; it is now. This is
   a lighter cut of the same hue, in the family of the mpg/grow greens. */
:root[data-theme="dark"] body[data-site="salary"] { --accent: #5FBFA6; --accent-2: #5FBFA6; --accent-3: #5FBFA6; --accent-soft: color-mix(in srgb, #5FBFA6 14%, var(--paper)); }

/* The five Sideline editions. The clubsring engine was injecting these into its
   own <head> because it may not edit this file; registering them here means the
   dark cut applies wherever a clubs page is rendered, and lets the engine drop
   that injection. Field-guide palette, authored in clubsring/countries.mjs —
   keep the two in step. All five verified by contrast-check.mjs. */
:root[data-theme="dark"] body[data-site="clubs"] { --accent: #63BE97; --accent-2: #63BE97; --accent-3: #63BE97; --accent-soft: color-mix(in srgb, #63BE97 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="au"]    { --accent: #A9C46A; --accent-2: #A9C46A; --accent-3: #A9C46A; --accent-soft: color-mix(in srgb, #A9C46A 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="ca"]    { --accent: #7FB6D9; --accent-2: #7FB6D9; --accent-3: #7FB6D9; --accent-soft: color-mix(in srgb, #7FB6D9 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="us"]    { --accent: #A79FD2; --accent-2: #A79FD2; --accent-3: #A79FD2; --accent-soft: color-mix(in srgb, #A79FD2 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="pac"]   { --accent: #68BDBF; --accent-2: #68BDBF; --accent-3: #68BDBF; --accent-soft: color-mix(in srgb, #68BDBF 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="cook"]  { --accent: #E8887E; --accent-2: #E8887E; --accent-3: #E8887E; --accent-soft: color-mix(in srgb, #E8887E 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="tire"]  { --accent: #9DB2CC; --accent-2: #9DB2CC; --accent-3: #9DB2CC; --accent-soft: color-mix(in srgb, #9DB2CC 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="thali"] { --accent: #E28A5C; --accent-2: #E28A5C; --accent-3: #E28A5C; --accent-soft: color-mix(in srgb, #E28A5C 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="sehat"] { --accent: #5CBFB2; --accent-2: #5CBFB2; --accent-3: #5CBFB2; --accent-soft: color-mix(in srgb, #5CBFB2 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="gym"]   { --accent: #D2A468; --accent-2: #D2A468; --accent-3: #D2A468; --accent-soft: color-mix(in srgb, #D2A468 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="yoga"]  { --accent: #C58FBA; --accent-2: #C58FBA; --accent-3: #C58FBA; --accent-soft: color-mix(in srgb, #C58FBA 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="move"]  { --accent: #78B0DA; --accent-2: #78B0DA; --accent-3: #78B0DA; --accent-soft: color-mix(in srgb, #78B0DA 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="diet"]  { --accent: #A2C464; --accent-2: #A2C464; --accent-3: #A2C464; --accent-soft: color-mix(in srgb, #A2C464 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="run"]   { --accent: #E08F5C; --accent-2: #E08F5C; --accent-3: #E08F5C; --accent-soft: color-mix(in srgb, #E08F5C 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="grow"]  { --accent: #5BC0AE; --accent-2: #5BC0AE; --accent-3: #5BC0AE; --accent-soft: color-mix(in srgb, #5BC0AE 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="fast"]  { --accent: #D6A94F; --accent-2: #D6A94F; --accent-3: #D6A94F; --accent-soft: color-mix(in srgb, #D6A94F 14%, var(--paper)); }
:root[data-theme="dark"] body[data-site="sugar"] { --accent: #E1857C; --accent-2: #E1857C; --accent-3: #E1857C; --accent-soft: color-mix(in srgb, #E1857C 14%, var(--paper)); }

/* ============================================================== 20. motion
   None decorative. Only state-change feedback and the interactive diagrams,
   and all of it surrenders to prefers-reduced-motion (the base reset in
   styles.css nukes durations; these rules remove transforms too).          */
@media (prefers-reduced-motion: reduce) {
  .ring-card:hover, .btn:hover, .grid a:hover, .pfc-btn:hover, .icon-btn:hover { transform: none !important; }
}

/* ====================================================== 14. the publisher row
   "Published by Amit Sharma" + follow links, above the legal footer on every
   page. Deliberately quiet: this is a masthead credit, not a call to action —
   the page has exactly one CTA and it is the house-ad slot above. Reads as
   provenance, which is the same reason the dated-source line is styled. */
.pub {
  border-top: 1px solid var(--rule);
  background: var(--paper);
}
.pub-in {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 3rem;
  align-items: flex-start;
  justify-content: space-between;
  padding: 1.75rem 0;
}
.pub-k {
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 .35rem;
}
.pub-name {
  font-family: var(--font-display, Fraunces, Georgia, serif);
  font-size: 1.35rem;
  line-height: 1.1;
  margin: 0 0 .3rem;
}
.pub-name a { color: var(--ink); text-decoration: none; }
.pub-name a:hover { text-decoration: underline; text-underline-offset: .18em; }
.pub-tag {
  margin: 0;
  font-size: .85rem;
  color: var(--ink-2, var(--muted));
  max-width: 46ch;
}
.pub-socs { display: flex; flex-wrap: wrap; gap: .5rem; }
.pub-soc {
  --pub-ico-bg: var(--paper);
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .45rem .7rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink);
  text-decoration: none;
  font-size: .8rem;
  line-height: 1;
  background: var(--paper);
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.pub-soc svg { width: 16px; height: 16px; flex: none; }
.pub-soc:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.pub-soc:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (max-width: 560px) {
  .pub-soc-l { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .pub-soc { padding: .55rem; }
}

/* ============================================ 15. house-ad urgency flag (§2.5)
   The two facts that make the offer worth a click — it costs nothing and it
   ends — used to sit in 12px small print under the button. They now lead. */
.pfc-flag {
  margin: 0 0 .75rem;
  display: flex;
  justify-content: center;
}
.pfc-flag span {
  display: inline-block;
  padding: .35rem .8rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(255,255,255,.08);
}

/* per-site sheets (thali.css, gym.css, sehat.css …) load AFTER this file and
   may override anything above for their own set piece. */

/* --- Follow strip (social distribution) ------------------------------------
   Sits at the top of the footer on tools/property/india cluster sites only —
   see SOCIAL_CLUSTERS in globalsite.mjs for why the global ring is excluded.
   Deliberately quiet: this is a standing credit line, not a house ad. It must
   never out-shout the page's own content or the one campaign banner. */
.soc-strip{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:.75rem 1.25rem;padding:1rem 0 1.15rem;border-bottom:1px solid var(--rule);
}
.soc-lead{margin:0;font-size:.9rem;color:var(--muted);max-width:46ch}
.soc-lead strong{color:var(--ink);font-weight:600}
.soc-links{display:flex;flex-wrap:wrap;gap:.5rem}
.soc-link{
  display:inline-block;padding:.35rem .7rem;border:1px solid var(--rule);
  border-radius:999px;font-size:.82rem;line-height:1.35;color:var(--ink);
  text-decoration:none;background:var(--surface);
}
.soc-link:hover,.soc-link:focus-visible{
  border-color:var(--site-accent);color:var(--site-accent);
}
@media (max-width:640px){
  .soc-strip{flex-direction:column;align-items:flex-start}
  .soc-lead{max-width:none}
}

/* ============================================================================
   20. AAA+++ STUDIO PREMIUM — the mechanics layer
   Fable's spec: _plans/AAA-PLUS-STUDIO-PREMIUM.md §3, §4, §6.1.
   Everything here is NEW selectors or additive properties on existing ones.
   No rule below removes or overrides a layout property, so CLS stays 0.00.
   ========================================================================== */

/* ---- 20.1 the mono "spec" voice ---------------------------------------- */
/* Provenance, units, codes and vintages set as specification, not prose. This
   single register is the loudest signal that someone measured something. */
.spec,
.srcline,
.crumbs,
.vintage,
.freshness,
.kicker,
.eyebrow {
  font-family: var(--mono);
  font-size: var(--fs-mono);
  letter-spacing: .06em;
  font-variant-numeric: tabular-nums lining-nums;
}
.spec { color: var(--muted); }
/* display:block was only ever set inside .ymyl, so a kicker used as the eyebrow
   above an h1 — its whole purpose — ran inline with the heading. */
.kicker, .eyebrow { display: block; text-transform: uppercase; letter-spacing: var(--track-label); color: var(--accent-deep); }

/* ---- 20.2 optical type scale + real Fraunces tuning -------------------- */
/* Fraunces is already served as a variable font with its opsz 9-144 axis. The
   axis was never used, which is why display type read like one weight applied
   everywhere — the tell that no typographer was paid. Two extremes per page. */
h1,
.figure-hero-serif {
  font-optical-sizing: none;
  font-variation-settings: "opsz" 84, "SOFT" 0, "WONK" 0;
}
.stat-display {
  font-family: var(--serif);
  font-weight: 340;
  font-size: var(--fs-stat);
  line-height: .92;
  font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0;
  letter-spacing: -.02em;
  font-variant-numeric: proportional-nums lining-nums;
}
.stat-display .u { font-family: var(--mono); font-size: .26em; letter-spacing: .08em; color: var(--muted); vertical-align: .5em; margin-left: .3em; }
/* FIX 2026-07-31: §3's `.answer p` (0,1,1) outranks `.stat-display` (0,1,0),
   so a hero figure marked up as a <p> inside the answer block rendered at
   BODY size on every Identity A site — the one number the page exists to show,
   silently shrunk to 16px. Matching specificity here rather than raising
   .stat-display's, so the rule stays scoped to where the collision happens. */
.answer .stat-display,
.answer p.stat-display { font-size: var(--fs-stat); line-height: .92; color: var(--ink); }

/* ---- 20.3 hairline discipline: the keel ------------------------------- */
/* Section anatomy borrowed from the trades pair. A 44x2 accent rule above the
   kicker gives every section a datum line, which is what stops a long page
   from reading as stacked boxes. */
h2.sec::before,
.keel::before {
  content: "";
  display: block;
  width: 44px;
  height: 2px;
  background: var(--accent);
  margin-bottom: 14px;
}

/* ---- 20.4 tabular numerals with intent -------------------------------- */
/* Compared or copied -> tabular (columns must not jiggle).
   Display typography -> proportional (handled in .stat-display above). */
.num,
td.n,
.result-v,
table.data td,
table.data th,
.pager {
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- 20.5 grain: paper as material ----------------------------------- */
/* Pure CSS, one fixed non-interactive layer, zero requests. The dead-flat hex
   fill was what made the ground read as a CSS variable rather than paper. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: var(--grain-alpha);
  background-image:
    repeating-linear-gradient(0deg,  rgba(20, 24, 27, .9) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(20, 24, 27, .7) 0 1px, transparent 1px 4px);
  background-size: 3px 3px, 4px 4px;
  mix-blend-mode: multiply;
}
:root[data-theme="dark"] body::before { mix-blend-mode: screen; opacity: .02; }
body > header, body > main, body > footer, .netbar, .site-head, .site-foot { position: relative; z-index: 1; }
@media print { body::before { display: none; } }

/* ---- 20.6 focus & hover with intent ---------------------------------- */
/* Hover on a text link DRAWS an underline; it does not swap a colour. Hover on
   a card darkens its hairline and never moves it (motion here would cost CLS
   and reads as a template animation). Focus is never removed. */
a.draw,
.rel-card .rel-t,
.entity-bridge a {
  text-decoration: none;
  background: linear-gradient(var(--accent), var(--accent)) 0 100% / 0 1px no-repeat;
  transition: background-size var(--t-med) var(--ease-out);
}
a.draw:hover, a.draw:focus-visible,
.rel-card:hover .rel-t,
.entity-bridge a:hover { background-size: 100% 1px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

/* ---- 20.7 elevation: exactly one shadow ------------------------------ */
.card, .rel-card, .stat, .tbl-wrap, .link-grid a { box-shadow: none; }
.lift, .lead-panel { box-shadow: var(--shadow-lift); }
:root[data-theme="dark"] .lift,
:root[data-theme="dark"] .lead-panel { box-shadow: none; background: var(--surface); border-color: var(--line-2); }

/* ---- 20.8 entrance choreography -------------------------------------- */
/* Above the fold, once, on load. Gated behind no-preference so reduced-motion
   and no-JS readers get the finished page rather than content stranded at
   opacity 0 — the failure mode that reveal-on-scroll libraries ship with. */
@media (prefers-reduced-motion: no-preference) {
  .reveal { animation: aaa-rise var(--t-slow) var(--ease-out) both; }
  .reveal-2 { animation-delay: 60ms; }
  .reveal-3 { animation-delay: 120ms; }
  @keyframes aaa-rise { from { opacity: 0; transform: translateY(12px); } }
}
/* PRINT FIX 2026-08-04, found by the TireFit build. `animation-fill-mode: both`
   applies the BACKWARDS fill — the `from` keyframe — even in print, where the
   animation never runs. The from-state is opacity 0, so the reveal stack
   (kicker, h1, the answer block: the three things a printed page most needs)
   rendered BLANK on every Identity A site. Nobody would have seen it on
   screen. Neutralised explicitly rather than by dropping `both`, because the
   fill is what stops the pre-animation flash on screen. */
@media print {
  .reveal, .reveal-2, .reveal-3 {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ---- 20.9 the promoted source line (E-E-A-T as ornament) -------------- */
/* Doctrine: no figure without a source line, no source line without a date.
   Identical position under every figure, table and diagram, on every site. */
.srcline {
  display: block;
  border-top: 1px solid var(--line);
  padding-top: 8px;
  margin-top: 12px;
  color: var(--muted);
  text-transform: uppercase;
}
.srcline .sep { color: var(--faint); margin: 0 .5em; }
.srcline a { color: var(--muted); text-decoration: none; border-bottom: 1px solid var(--line-2); }
.srcline a:hover { color: var(--ink-2); border-bottom-color: var(--accent); }

/* ---- 20.10 the answer block, unboxed --------------------------------- */
/* Was a bordered card with a 3px left accent — the Bootstrap-alert shape. The
   answer is the reason the page exists; it should be open typography on paper
   with one hairline under it, not a component. */
.answer.aaa {
  background: none;
  border: 0;
  padding: 0;
  border-radius: 0;
}
.answer.aaa .big { font-family: var(--serif); font-weight: 340; font-size: var(--fs-stat); line-height: .92; letter-spacing: -.02em; font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0; }
.answer.aaa > .srcline { margin-top: 16px; }

/* ---- 20.11 the method mini-spec -------------------------------------- */
/* A prose paragraph titled "how we compute this" is skipped by every reader.
   Numbered, ruled, with the formula in the mono voice, it gets read — and it
   is the visible half of the E-E-A-T claim. */
.method { margin: var(--s5) 0; }
.method ol { list-style: none; margin: 0; padding: 0; counter-reset: mstep; }
.method li {
  counter-increment: mstep;
  border-top: 1px solid var(--line);
  padding: 14px 0 14px 44px;
  position: relative;
  color: var(--ink-2);
}
.method li:last-child { border-bottom: 1px solid var(--line); }
.method li::before {
  content: counter(mstep, decimal-leading-zero);
  position: absolute; left: 0; top: 14px;
  font-family: var(--mono); font-size: var(--fs-mono);
  letter-spacing: .06em; color: var(--accent-deep);
}
.method .formula {
  font-family: var(--mono);
  font-size: .875rem;
  background: var(--accent-wash);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 10px 0 0;
  overflow-x: auto;
  white-space: pre;
}

/* ---- 20.12 related cards that earn the click (spec 6.1) --------------- */
/* The old ring cards were identical rounded boxes with a generic blurb and a
   "go" arrow, i.e. the reader was given no reason to click. This is the one
   place where link design and SEO are literally the same work, so the reason
   line is mandatory and must not be transplantable to another card. */
.rel-grid { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); margin-top: var(--s4); }
.rel-card {
  display: flex; flex-direction: column; gap: 8px;
  padding: 18px 18px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent);
  border-radius: 0 0 var(--radius) var(--radius);
  text-decoration: none; color: inherit;
  transition: border-color var(--t-fast) var(--ease-out);
}
.rel-card:hover { border-color: var(--line-2); border-top-color: var(--accent); }
.rel-card .rel-t { font-weight: 650; color: var(--ink); font-size: 1.02rem; align-self: flex-start; }
.rel-card .rel-why { color: var(--ink-2); font-size: .92rem; line-height: 1.5; margin: 0; }
/* --faint measures 3.35:1 on dark paper, under the 4.5:1 floor. The freshness
   chip is a DATED E-E-A-T signal, not decoration — if it is too pale to read
   it is doing the opposite of its job. --muted is 5.6:1. */
.rel-card .freshness { margin-top: auto; padding-top: 10px; color: var(--muted); align-self: flex-end; }
/* Exactly one bridge card per page; the dashed edge is its "different family"
   grammar, so a reader is never surprised by where it lands. */
.rel-card.bridge { border-style: dashed; border-top-style: solid; background: var(--paper-2); }

/* ---- 20.13 netbar promoted from utility strip ------------------------- */
.netbar .net-label { font-family: var(--mono); font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; opacity: .72; }
/* FIX 2026-07-31: this was written as .net-dot, but globalsite.mjs has always
   emitted .nb-dot — so the rule was dead code on all 24 ring sites. */
.netbar .nb-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }
.netbar a { transition: opacity var(--t-fast) var(--ease-out); }

/* ---- 20.14 table caption vintage chip -------------------------------- */
table.data caption .vintage,
.tbl-wrap .vintage {
  display: inline-block;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  padding: 2px 7px;
  color: var(--muted);
  text-transform: uppercase;
  margin-left: 10px;
}

/* ---- 20.15 YMYL armour band (spec 6.5) ------------------------------- */
/* Calm and ruled, never a red alert box. A scary banner above a health figure
   reads as liability panic; a hairline-ruled note reads as a journal. */
.ymyl {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 14px 0;
  margin: var(--s4) 0;
  color: var(--ink-2);
  font-size: .93rem;
}
.ymyl .kicker { display: block; margin-bottom: 6px; }

/* ---- 20.16 confident emptiness --------------------------------------- */
/* At least one full-width near-white breath per page. Cheap sites fill;
   expensive sites breathe. */
.breathe { min-height: 18vh; }
@media (max-width: 640px) { .breathe { min-height: 10vh; } }

/* ---- 20.17 the AdSense slot (pre-built 2026-08-04) --------------------
   Written BEFORE the units exist, deliberately. `adUnit()` renders nothing
   while AD_SLOTS is empty, so until approval lands this whole block is inert
   — but the day a slot id is pasted in, ~90,000 pages start rendering an
   <aside class="ad-slot"> that had no rules at all, on every site at once.
   That is not a thing to discover in production.

   Three jobs, in order of importance:
   1. LOOK FOREIGN. An ad that borrows the page's own typography reads as an
      endorsement, and AdSense policy is explicit that ads must not be
      confusable with content. So: a ruled band on the paper-2 ground, the
      label in the mono spec voice, and no serif anywhere near it.
   2. RESERVE ITS SPACE. min-height is already inline on the <ins> for CLS,
      and the reservation is repeated here so the aside holds the box even
      before the iframe arrives.
   3. STAY QUIET. Hairlines, not boxes. This is the floor of the revenue
      stack, not the point of the page — see the revenue doctrine. */
.ad-slot {
  display: block;
  margin: var(--s5) auto;
  max-width: 100%;
  padding: 12px 0 14px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: transparent;
  text-align: center;
  /* An ad is never part of the reading measure — it spans the column but is
     visibly outside the argument. */
  clear: both;
}
.ad-label {
  display: block;
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: var(--track-label, .11em);
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 10px;
  text-align: left;
}
.ad-slot .adsbygoogle { display: block; width: 100%; }
/* The in-content unit sits inside a .wrap already; the pre-footer one is
   emitted by shell() outside any wrapper, so it needs its own gutter. */
.ad-slot--pre-footer {
  max-width: var(--max);
  padding-left: 28px; padding-right: 28px;
  margin-top: var(--s6);
}
:root[data-theme="dark"] .ad-slot { border-color: var(--line-2); }
@media (max-width: 640px) {
  .ad-slot { margin: var(--s4) auto; }
  .ad-slot--pre-footer { padding-left: 20px; padding-right: 20px; }
}

/* ── §21 SCOPED PORTFOLIO SEARCH ────────────────────────────────────────
   GROUP-DESIGN-SYSTEM-2026-08 §5. The scope select is a TRAILING SEGMENT of
   the existing search group, positioned absolutely inside the already-relative
   .hsearch rather than by converting .hsearch to a flex row. That is the
   conservative choice on purpose: this box ships on ~180,000 pages across 30
   sites, and changing its layout mode would put every one of those headers at
   risk to add a control that only some of them carry. Absolute positioning
   touches nothing on a site that renders no select.

   Every rule here is gated on :has(.pscope), so a site without the control
   gets byte-identical rendering to before. */
.hsearch:has(.pscope) input { padding-right: 104px; }
.hsearch:has(.pscope):focus-within { flex-basis: 340px; }
.pscope {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  max-width: 96px; border: 0; border-left: 1px solid var(--line-2);
  border-radius: 0; background: transparent; color: var(--muted);
  font-family: var(--mono, var(--sans)); font-size: .68rem;
  letter-spacing: .04em; text-transform: uppercase;
  padding: 4px 2px 4px 8px; cursor: pointer; appearance: none;
  text-overflow: ellipsis;
}
.pscope:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 3px; }
.pscope:hover { color: var(--ink); }

/* Result groups (§5.3). Only rendered in a cross-site scope; the single-site
   scope stays a flat list exactly as before. */
.qr-site {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 16px 6px; margin: 0;
  font-family: var(--mono, var(--sans)); font-size: .7rem;
  letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
  border-top: 1px solid var(--line); background: var(--paper-2);
}
.qr-site:first-child { border-top: 0; }
.qr-site em { font-style: normal; margin-left: auto; opacity: .8; }
.qr-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
/* The one place search links the group unprompted (§5.6). A button, not a
   link: it re-runs the query in a wider scope, it does not navigate. */
.qr-widen {
  display: block; width: 100%; text-align: left;
  padding: 11px 16px; border: 0; border-top: 1px solid var(--line);
  background: transparent; color: var(--accent); cursor: pointer;
  font-family: var(--sans); font-size: .85rem; font-weight: 500;
}
.qr-widen:hover { background: var(--paper-2); }
.qr-widen:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

@media (max-width: 860px) {
  /* At full width the group has room for a real label rather than a stub. */
  .pscope { max-width: 132px; font-size: .7rem; }
  .hsearch:has(.pscope) input { padding-right: 140px; }
}

/* ============================================================================
   THE MOBILE HERO ALIGNMENT REPAIR — ring-wide. 2026-08-09.

   THE DEFECT, measured on yoga.amit.marketing at 375x812 before the fix.
   Computed `text-align`, reading down the first screen:

       .hero .wrap      center     <- styles.css:178  .hero { text-align:center }
       .hero .eyebrow   center
       .answer          LEFT       <- datasite.css:252 .answer { text-align:left }
       .srcline         LEFT
       .search          center
       .stats           center
       every .section   LEFT

   Six alternations before the fold. Amit reported it as "some parts are left
   aligned some centred" and he was describing exactly this.

   WHY IT ONLY SHOWS ON PHONES. A centred hero sitting above left-aligned body
   copy is a normal editorial device at 1280px, because the hero is a distinct
   band with air either side and the eye reads it as a masthead. At 375px there
   is no air — every block is full-bleed to the same gutters — so the identical
   markup just looks like the alignment is broken. It was never a yoga problem;
   it is the shared stylesheets disagreeing, and it reaches every data site that
   puts an answer block in its hero (11 homepages at the time of writing, of the
   29 sites that carry this file).

   THE FIX, and why it is shaped like this:

   1. `:has(.answer)` scopes it to exactly the heroes that contain the
      conflict. A hero with no answer block is internally consistent already —
      centred throughout — and left-aligning it would be an unrequested visual
      change to sites that were never broken. Browsers without `:has()` simply
      keep today's behaviour, which is the current shipped state, so the
      fallback is "no change" rather than "half-applied".

   2. It aligns LEFT, not centre. Centring the answer instead would rag long
      paragraphs on both edges of a narrow column — measurably worse to scan —
      and the answer block is deliberately left-aligned everywhere else on the
      site, including on desktop. Left is both the better typography and the
      smaller change.

   3. `max-width: 560px` only. Desktop keeps its centred masthead untouched.
   ========================================================================== */
/* ⚠ BREAKPOINT WIDENED 560 → 759 on 2026-08-10. The repair originally stopped
   at 560px, but the sites that pair an answer with a side element (yoga's hero
   plate and breath clock) only go to their two-column left-aligned layout at
   760px. Between 561 and 759 NOBODY owned the hero: styles.css centred the
   masthead while the answer, the dial and the plate all sat left — the exact
   six-alternation defect this block exists to kill, still live on large phones
   in landscape, small tablets and split-screen windows. A hero is either a
   centred masthead with air either side, or it is a left-aligned column; 760
   is where this ring actually gets that air. */
/* ⚠ WIDENED AGAIN 759 → 900, same day, and this time from a MEASUREMENT rather
   than from a layout breakpoint. On sehat.amit.marketing/bmi/5-feet-6-inches at
   820px — iPad portrait, and 768/810/820 are three of the most common tablet
   widths there are — the hero still read:

       .eyebrow   CENTRE   dL28
       h1         CENTRE   dL132   <- 104px in from its own column
       .lede      CENTRE   dL57
       .ymyl      CENTRE   dL28
       .answer    LEFT     dL28
       .spec      CENTRE   dL28
       .bandchart CENTRE   dL28
       .nextsteps LEFT     dL43

   Eight blocks, five alternations, the same defect. The reason the 760px
   rationale did not hold: the .wrap measured 805px inside an 820px viewport,
   so the "air either side" that makes a centred masthead legible was 7px. The
   air does not arrive at 760 — it arrives when the page stops being full-bleed,
   which on this ring is around 900. Above 900 nothing changes, so the desktop
   masthead is still untouched and this remains a strict superset of the two
   earlier passes. Rule of thumb for the next person: pick this breakpoint by
   measuring the GUTTER, not by copying a layout breakpoint. */
@media (max-width: 900px) {
  .hero:has(.answer),
  .hero:has(.answer) .wrap,
  .hero:has(.answer) .eyebrow,
  .hero:has(.answer) h1,
  .hero:has(.answer) .lede,
  .hero:has(.answer) .search,
  .hero:has(.answer) .stats,
  .hero:has(.answer) .citystats {
    text-align: left;
  }

  /* ...and every OTHER hero child, whatever it is called. The enumeration
     above was re-broken three times by blocks it did not know about — thali's
     `.assume` and `.qty-strip`, sehat's `.spec` and `.bandchart`, each one
     centred while the answer beside it sat left. Every site adds its own hero
     blocks, so naming them is a losing game: if the hero contains an answer,
     the whole hero reads left. The enumeration is kept above only because it
     also reaches descendants (.stat inside .stats), which `> *` does not. */
  .hero:has(.answer) > .wrap > * { text-align: left; }

  /* THE ONE THING text-align COULD NOT REACH. styles.css sets
     `.hero > .wrap > .eyebrow { align-self: center }` inside the flex hero,
     and align-self is layout, not text — so the eyebrow kept sitting mid
     column no matter what the rules above said. General lesson: in a flex
     column, align-self beats BOTH text-align and margin-inline. */
  .hero:has(.answer) > .wrap > .eyebrow { align-self: start; }

  /* Decorative rules under the eyebrow are centred with `margin: 0 auto`;
     re-anchor them to the text edge or they float mid-column. */
  .hero:has(.answer) .eyebrow::after { margin-left: 0; margin-right: auto; }

  /* Stat rows are grid/flex and were centre-justified by the inherited rule. */
  .hero:has(.answer) .stats,
  .hero:has(.answer) .citystats { justify-items: start; }
  .hero:has(.answer) .stat,
  .hero:has(.answer) .citystat { text-align: left; }
  .hero:has(.answer) .search { justify-content: flex-start; }

  /* THE BLOCK-LEVEL COMPANION of the text-align repair (found 2026-08-10 on
     yoga's asana pages). h1, .eyebrow and .lede carry `margin: 0 auto` from
     styles.css, so a headline NARROWER than the column stayed visually
     centred even after its text-align went left — yoga's two-word pose
     titles measured 133px wide with 101px of auto margin on a 375px screen,
     while the lede under them sat at the gutter. Text-align moves the ink;
     these anchor the boxes. Sites with full-width headlines see no change. */
  .hero:has(.answer) h1,
  .hero:has(.answer) .eyebrow,
  .hero:has(.answer) .lede { margin-left: 0; margin-right: auto; }
}

/* A form control does not inherit the hero's centring gracefully: above the
   repair's breakpoint `.hero { text-align: center }` reaches into the search
   field, so the placeholder and everything the reader TYPES sit centred while
   the identical box in the header is left-aligned. Masked while the near-full
   -width placeholder is showing; obvious on the second keystroke. Left at
   every width, like every other input in the ring. */
.hero .search input,
.hero input[type="search"] { text-align: left; }

/* ============================================================================
   THE CAST BAND — Geeta & Amit on the India-cluster sites. 2026-08-09.

   Lives here, in the shared data-site sheet, rather than in one site's own
   stylesheet, because it now renders on ten sites from one place in shell().
   Keeping the CSS site-local would have meant ten copies drifting apart, which
   is the exact failure the registry/apply-global setup exists to prevent.

   It is an editorial band, never a popup or an interstitial: these are
   AdSense-carrying, Google-indexed reference sites, and a wall in front of
   indexed content is both a mobile-usability penalty and an ad-policy risk.
   The moment-of-value ask is the outbound follow gate (followbox.mjs); this is
   the soft one, and it sits AFTER the content, never above it.
   ========================================================================== */
.cast-band {
  border-top: 1px solid var(--gold, var(--accent));
  padding-top: 16px;
}
.cast-band-k {
  font-family: var(--mono);
  font-size: var(--fs-mono, .8125rem);
  letter-spacing: var(--track-label, .11em);
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 6px;
}
.cast-band h2 { font-size: 1.15rem; margin: 0 0 4px; }
.cast-band h2::before { display: none; }   /* the band already has its rule */
.cast-band .sec-note { max-width: 58ch; margin: 0; }

.cast-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 0;
}

/* One pill per face. Deliberately a pill and not a panel: at this size it must
   read as a link, not as an ad unit. */
.cast-card {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  flex: 0 1 auto;
  padding: 7px 16px 7px 7px;
  border: 1px solid var(--line-2, var(--line));
  border-radius: 999px;
  background: var(--surface);
  text-decoration: none;
  line-height: 1.2;
}
.cast-card:hover {
  border-color: var(--accent);
  background: var(--accent-soft, var(--surface-2));
}

.cast-av {
  /* `display: block` is LOAD-BEARING. This is a <span>, and width/height are
     ignored on an inline box — without it the avatar ignores the 42px and
     renders at its intrinsic 200px. That shipped once and Amit reported it as
     "huge faces". Any future avatar wrapper needs the same line. */
  display: block;
  position: relative;
  flex: none;
  width: 42px;
  height: 42px;
}
.cast-av img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 1px solid var(--line);
}
/* the Instagram mark rides the avatar's corner, so the row needs no icon
   column of its own and the face gets the space instead */
.cast-av .ig {
  position: absolute;
  right: -3px;
  bottom: -3px;
  width: 17px;
  height: 17px;
  padding: 2px;
  border-radius: 50%;
  background: var(--surface);
  color: var(--accent);
  box-shadow: 0 0 0 1px var(--line);
}
.cast-tx { display: block; min-width: 0; }
.cast-h {
  display: block;
  font-family: var(--mono);
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -.01em;
}
.cast-r {
  display: block;
  margin-top: 1px;
  font-size: .78rem;
  color: var(--muted);
}
.cast-note { margin: 12px 0 0; }

@media (max-width: 560px) {
  /* full-width pills stack cleanly and stay comfortably tappable */
  .cast-card { flex: 1 1 100%; justify-content: flex-start; padding: 8px 16px 8px 8px; }
}

/* ============================================================================
   THE YOGAPATH BANNER — cluster I, every site except yoga itself. 2026-08-10.

   Amit's brief was "attractive, modern, cinematic, feature Geeta, make people
   want to click it". The cinema here is done with LIGHT, not with motion: a
   warm off-centre glow behind the type, a gold hairline, and a photograph that
   is lit from a window rather than cut out and floated. Nothing animates until
   the pointer arrives, and everything stops under prefers-reduced-motion —
   these are AdSense pages with a Core Web Vitals budget, so an autoplaying
   band would cost more than it earns.

   It is DARK on purpose. Identity A is warm paper end to end, so a single
   ink-dark band is the only object on the page that cannot be mistaken for
   more reference content — the contrast is what makes it read as a doorway.
   Contrast measured: #F6EFE6 on #150710 is 15.4:1, the gold kicker 8.9:1.

   Layout is TWO COLUMNS down to 700px and stacked below it, with the picture
   on top when stacked. The picture must never be the thing that is cropped
   away on a phone: she is the reason the banner works.
   ========================================================================== */
.yoga-banner {
  --yb-ink: #150710;
  --yb-crimson: #A3123E;
  --yb-gold: #E6B25C;
  --yb-paper: #F6EFE6;
  margin: 0;
}
.yoga-banner .yb-link {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: 268px minmax(0, 1fr);
  align-items: stretch;
  border-radius: 16px;
  overflow: hidden;
  text-decoration: none;
  color: var(--yb-paper);
  background:
    radial-gradient(115% 150% at 8% 0%, #48132A 0%, #260A18 46%, var(--yb-ink) 100%);
  box-shadow: var(--shadow-lift, 0 24px 48px -32px rgba(20, 24, 27, .3));
}
/* the sweep — one soft diagonal of warm light across the type side */
.yoga-banner .yb-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(104deg, transparent 38%, rgba(230, 178, 92, .13) 56%, transparent 74%);
  opacity: .9;
}
.yoga-banner .yb-link:hover { box-shadow: 0 1px 0 rgba(20, 24, 27, .06), 0 30px 60px -30px rgba(80, 12, 34, .55); }
.yoga-banner .yb-link:focus-visible { outline: 3px solid var(--yb-gold); outline-offset: 3px; }

/* ---- the picture ---- */
.yoga-banner .yb-art {
  display: block;              /* load-bearing on a <span>, same trap as .cast-av */
  position: relative;
  overflow: hidden;
  min-height: 260px;
}
.yoga-banner .yb-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 16%;    /* holds her face and hands in frame at any crop */
  display: block;
  transform: scale(1.01);
  transition: transform var(--t-slow, 420ms) var(--ease-out, cubic-bezier(.25,1,.4,1));
}
.yoga-banner .yb-link:hover .yb-art img { transform: scale(1.05); }
/* the picture's warm ground meets the dark panel; this feathers the seam so it
   reads as one photograph-lit object rather than two boxes side by side */
.yoga-banner .yb-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(21, 7, 16, .06) 0%, rgba(21, 7, 16, .10) 62%, rgba(21, 7, 16, .78) 100%);
}
/* AI PROVENANCE, on the plate. Same hallmark YogaPath uses on its own pose
   photographs — a picture of a person who does not exist gets labelled where
   the picture is, not only in the small print underneath. */
.yoga-banner .yb-ai {
  position: absolute;
  left: 10px;
  bottom: 10px;
  font-family: var(--mono);
  font-size: .625rem;
  font-weight: 600;
  letter-spacing: .12em;
  padding: 3px 7px;
  border-radius: 4px;
  color: var(--yb-paper);
  background: rgba(21, 7, 16, .62);
  border: 1px solid rgba(246, 239, 230, .3);
}

/* ---- the type ---- */
.yoga-banner .yb-tx {
  position: relative;
  z-index: 1;
  display: block;
  padding: 26px 30px 24px;
  align-self: center;
}
.yoga-banner .yb-k {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-mono, .8125rem);
  letter-spacing: var(--track-label, .11em);
  text-transform: uppercase;
  color: var(--yb-gold);
  margin: 0 0 10px;
}
.yoga-banner .yb-k span { color: var(--yb-paper); }
.yoga-banner .yb-h {
  display: block;
  font-family: var(--serif);
  font-size: clamp(1.35rem, 2.6vw, 1.85rem);
  font-weight: 500;
  line-height: 1.18;
  letter-spacing: -.02em;
  color: #FFFFFF;
  margin: 0 0 10px;
  max-width: 22ch;
}
.yoga-banner .yb-s {
  display: block;
  font-size: .95rem;
  line-height: 1.55;
  color: rgba(246, 239, 230, .84);
  max-width: 54ch;
  margin: 0 0 16px;
}
/* the affordance. A span, not a nested <a> — the whole banner is one link, and
   a link inside a link is invalid HTML that browsers silently unnest. */
.yoga-banner .yb-go {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;                     /* one-handed phone target */
  padding: 0 20px;
  border-radius: 999px;
  background: var(--yb-crimson);
  color: #FFFFFF;
  font-weight: 650;
  font-size: .95rem;
  letter-spacing: -.005em;
  transition: background var(--t-fast, 140ms) ease, transform var(--t-fast, 140ms) ease;
}
.yoga-banner .yb-go svg {
  width: 17px;
  height: 17px;
  transition: transform var(--t-med, 220ms) var(--ease-out, ease);
}
.yoga-banner .yb-link:hover .yb-go { background: #BC1A49; }
.yoga-banner .yb-link:hover .yb-go svg { transform: translateX(4px); }
.yoga-banner .yb-note {
  display: block;
  margin: 14px 0 0;
  font-size: .75rem;
  line-height: 1.5;
  color: rgba(246, 239, 230, .62);
}

@media (max-width: 700px) {
  .yoga-banner .yb-link { grid-template-columns: 1fr; }
  .yoga-banner .yb-art { min-height: 0; height: 208px; }
  .yoga-banner .yb-art img { object-position: 50% 12%; }
  /* stacked, the feather has to run vertically or the photograph ends in a
     hard edge above the headline */
  .yoga-banner .yb-scrim {
    background: linear-gradient(180deg, rgba(21, 7, 16, .04) 0%, rgba(21, 7, 16, .12) 55%, rgba(21, 7, 16, .82) 100%);
  }
  .yoga-banner .yb-tx { padding: 20px 20px 22px; }
  .yoga-banner .yb-go { display: flex; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .yoga-banner .yb-art img,
  .yoga-banner .yb-go,
  .yoga-banner .yb-go svg { transition: none; }
  .yoga-banner .yb-link:hover .yb-art img { transform: scale(1.01); }
}
