/* ===== 00-chrome ===== */
/* ═══════════════════════════════════════════════════════════════════════════
   MINETLY — §0 PERSISTENT CHROME
   art-direction.md §3.1 (top bar) · §3.2 (bottom bar) · §9.2 (CTA surfaces 1+7)

   Depends on css/base.css for the §1 tokens, the reset and the .cta base.
   The subset of this file needed at first paint is duplicated in
   css/critical.css — KEEP THE TWO IN SYNC.

   Colour law check for this file:
     · --grad-cta appears on CTAs only (surfaces 1 and 7). §1.16.1 ✓
     · --spot-gold is used for the price numeral only. §1.16.2 ✓
     · No backdrop-filter except the CINEMA desktop top bar. §1.16.6 / §3.1 ✓
     · No glow on either bar; the only two glows on the page are the hero CTA
       and the price numeral. §1.16.5 ✓
   ═══════════════════════════════════════════════════════════════════════════ */


/* ───────────────────────────────────────────────────────────────────────────
   SKIP LINK — first focusable element on the page (§7)
   ─────────────────────────────────────────────────────────────────────────── */

.skip-link{
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  z-index: var(--z-skip);
  transform: translateY(calc(-100% - var(--s-8)));
  display: inline-flex;
  align-items: center;
  min-height: var(--s-7);                 /* 48px touch floor */
  padding: var(--s-3) var(--s-5);
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-sm);
  background-color: var(--ink-panel);
  color: var(--text-hi);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--tr-btn);
  text-transform: uppercase;
  transition: transform var(--mo-quick) var(--ease-out);
}
.skip-link:focus-visible{ transform: translateY(0); }


/* ───────────────────────────────────────────────────────────────────────────
   §3.1 TOP BAR — --bar-top 56px, position: fixed, --z-bar-top
   ─────────────────────────────────────────────────────────────────────────── */

.chrome-top{
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-bar-top);
  height: var(--bar-top);
  display: flex;
  align-items: center;
  /* §3.1 Ground, mobile: flat rgba(5,5,7,0.94) — NO blur. That rgba is
     --ink-void at 94%; it is written literally because CSS cannot compose an
     alpha from a hex token without color-mix(), which is not in the spec. */
  background-color: rgba(5,5,7,0.94);
  padding-top: env(safe-area-inset-top, 0px);
}

/* §3.1 Border: 1px bottom --edge that fades in past scrollY > 40vh, via
   OPACITY ONLY. js/app.js toggles .is-scrolled. Drawn on a pseudo-element so
   the bar's own height never changes and nothing reflows. */
.chrome-top::after{
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background-color: var(--edge);
  opacity: 0;
  transition: opacity var(--mo-quick) var(--ease-out);
}
.chrome-top.is-scrolled::after{ opacity: 1; }

/* design/mobile-motion.md §C.00 — reading-progress hairline. 2px on the bar's
   bottom edge in --violet-beam (not red: colour law 1; not gold: law 2).
   Pure CSS scroll(root) timeline, run on the compositor: no JS, no scroll
   listener. Without scroll-driven animations, without the engine, under
   reduced motion, or on the lite path it does not exist (display:none). */
.chrome-top__progress{
  display: none;
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  height: 2px;
  background: var(--violet-beam);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}
@supports (animation-timeline: scroll()){
  @media (prefers-reduced-motion: no-preference){
    [data-motion="on"]:not([data-m-lite]) .chrome-top__progress{
      display: block;
      animation: chrome-progress linear both;
      animation-timeline: scroll(root block);   /* after the shorthand, which resets it */
    }
  }
}
@keyframes chrome-progress{
  from{ transform: scaleX(0); }
  to  { transform: scaleX(1); }
}

/* §3.1 Ground, desktop, CINEMA ONLY: rgba(5,5,7,0.72) + blur(15px).
   §1.16.6 — backdrop-filter exists nowhere on STANDARD or STILLS, at any
   breakpoint. The tier is published as <html data-tier="…"> by js/app.js,
   and the document ships data-tier="stills", so with JS off this never
   applies. The filter is declared statically and NEVER animated (§3.1). */
@media (min-width: 900px){
  [data-tier="cinema"] .chrome-top{
    background-color: rgba(5,5,7,0.72);
    -webkit-backdrop-filter: blur(15px);
    backdrop-filter: blur(15px);
  }
}

.chrome-top__inner{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  width: 100%;
}

/* §3.1 Left: MINETLY wordmark, Inter 600, 13px, --tr-mark.
   13px is not on the §1.9 scale; §3.1 states it literally, so it is literal.
   Rendered as a <span>, not a link: it adds no tab stop and no clickable div. */
.chrome-top__mark{
  display: inline-flex;
  align-items: center;
  min-height: var(--s-7);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  line-height: 1;
  letter-spacing: var(--tr-mark);
  text-transform: uppercase;
  color: var(--text-hi);
  white-space: nowrap;
}

/* §3.1 Right, desktop: Course · Instructor · FAQ. Hidden below 900px, which
   also removes them from the tab order — every target is reachable by scroll. */
.chrome-top__nav{ display: none; }

@media (min-width: 900px){
  .chrome-top__nav{
    display: flex;
    align-items: center;
    gap: var(--s-6);              /* 32px ≫ the 8px touch-spacing minimum */
    margin-left: auto;
    margin-right: var(--s-6);
  }
  .chrome-top__nav a{
    display: inline-flex;
    align-items: center;
    min-height: var(--s-7);
    font-size: var(--t-micro);
    font-weight: 500;
    line-height: 1;
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
    color: var(--text-low);       /* 7.5:1 — the floor for real copy */
    transition: color var(--mo-quick) var(--ease-out);
  }
}
@media (min-width: 900px) and (hover: hover){
  .chrome-top__nav a:hover{ color: var(--text-hi); }
}


/* ───────────────────────────────────────────────────────────────────────────
   CTA SURFACE 1 — .cta.cta--bar, data-cta="topbar"
   §9.2 style: "ghost → solid on hero exit". §3.1 tap target ≥ 48 × 112 CSS px.
   ─────────────────────────────────────────────────────────────────────────── */

.cta--bar{
  position: relative;              /* hosts the ::before fill below        */
  isolation: isolate;              /* the fill paints over the ground only */
  min-height: var(--s-7);          /* 48 */
  min-width: 112px;                /* §3.1, literal */
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--edge-strong);
  background-image: none;
  background-color: transparent;
  color: var(--text-hi);
  white-space: nowrap;
}
@media (hover: hover){
  .cta--bar:hover{ background-color: transparent; border-color: var(--text-low); }
}
.cta--bar:active{ background-color: transparent; }

/* js/app.js adds .is-solid the instant the hero CTA exits the viewport,
   via one IntersectionObserver class toggle over --mo-quick (§3.1).
   design/mobile-motion.md §C.00: a gradient cannot transition, so the fill
   lives on a ::before and CROSS-FADES on opacity (compositor-only). The
   bar's own background-image stays none; background-image is never animated.
   inset:-1px so the fill also covers the 1px border that goes transparent. */
.cta--bar::before{
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  background-image: var(--grad-cta);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--mo-quick) var(--ease-out);
}
.cta--bar.is-solid{
  background-image: none;
  border-color: transparent;
  color: var(--on-rec);
}
.cta--bar.is-solid::before{ opacity: 1; }
@media (hover: hover){
  .cta--bar.is-solid:hover{ background-image: none; background-color: var(--rec-red-hot); }
  .cta--bar.is-solid:hover::before{ opacity: 0; }
}
.cta--bar.is-solid:active{ background-image: none; background-color: var(--rec-red-deep); }
.cta--bar.is-solid:active::before{ opacity: 0; }

/* One element, two labels — one tab stop, one accessible name, because a
   display:none label is excluded from the accessible name computation.
   Labels are §9.2 verbatim: "₹1,499 · JOIN" (mobile) / "JOIN NOW — ₹1,499". */
.cta--bar .lbl-wide{ display: none; }
@media (min-width: 900px){
  .cta--bar .lbl-wide  { display: inline; }
  .cta--bar .lbl-narrow{ display: none;   }
}


/* ───────────────────────────────────────────────────────────────────────────
   §3.2 BOTTOM BAR — --bar-bottom 68px, mobile <900px only, --z-bar-bot
   ─────────────────────────────────────────────────────────────────────────── */

.chrome-bot{
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: var(--z-bar-bot);
  display: flex;
  align-items: center;
  gap: var(--s-2);                 /* the 8px touch-spacing minimum, exactly */
  /* min-height, not height, so the bar can never clip the Razorpay trust mark.
     At 360px the contents measure 68px exactly; see the sub-line note below. */
  min-height: var(--bar-bottom);
  padding: var(--s-2) var(--gutter) calc(var(--s-2) + env(safe-area-inset-bottom, 0px));
  /* Flat rgba(5,5,7,0.94). NO backdrop-filter, ever, on this bar. §3.2 */
  background-color: rgba(5,5,7,0.94);
  border-top: 1px solid var(--edge);
  /* Rises from translateY(100%) once the hero CTA leaves the viewport
     (js/app.js adds .is-up). visibility is stepped so the bar is never a
     focusable element sitting off-screen — no keyboard trap (§7).
     design/mobile-motion.md §C.00 + amendment 26: the EXIT accelerates away
     on --mo-exit / --ease-in (ux #14, web #23); the RISE lands on the
     --ease-spring damped spring. The bar's children are never hidden: the
     sticky CTA is the most-used buy control and is always fully opaque.
     Token fallbacks keep this correct if base.css §1.14b is not loaded yet. */
  transform: translateY(100%);
  visibility: hidden;
  transition: transform var(--mo-exit, 200ms) var(--ease-in, var(--ease-out)),
              visibility 0s linear var(--mo-exit, 200ms);
}
.chrome-bot.is-up{
  transform: translateY(0);
  visibility: visible;
  transition: transform var(--mo-spring, var(--mo-base)) var(--ease-spring, var(--ease-out)),
              visibility 0s;
}
/* Overshoot cover: the spring overshoots by ~3.8% of the bar's height, which
   would lift it off the bottom edge for a few frames. This 16px skirt in the
   bar's own ground fills that gap. It is below the fold, so it costs nothing. */
.chrome-bot::after{
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 100%;
  height: 16px;
  background: inherit;
  pointer-events: none;
}

@media (min-width: 900px){
  .chrome-bot{ display: none; }
}

.chrome-bot__block{
  flex: 1 1 auto;
  min-width: 0;                    /* lets the sub-line wrap instead of overflow */
}

/* §3.2 Left: ₹1,499 in --spot-gold, Bebas --t-price-mid.
   Rendered with the .00 — §6: "`.00` is mandatory on this card and in the
   sticky bars", restated in §9.3. This is a price DISPLAY, not a CTA label;
   CTA labels stay verbatim to the §9.2 inventory. */
.chrome-bot__price{
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-price-mid);
  line-height: var(--lh-num);
  letter-spacing: var(--tr-num);
  color: var(--spot-gold);         /* ~10.3:1 on --ink-void */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* §3.2 "Certificate included · Razorpay" at --t-label in --text-low.
   Set in sentence case exactly as the spec writes the string. --t-label's
   usual UPPERCASE + --tr-label treatment is dropped here for two reasons:
   the spec gives this string in sentence case, and tracking is an uppercase
   device — keeping both would push the line past 360px, wrap it, and make
   the bar taller than --bar-bottom, which body's padding-bottom assumes. */
.chrome-bot__sub{
  display: block;
  margin-top: var(--s-1);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: 0;
  color: var(--text-low);          /* 7.5:1 floor */
  white-space: nowrap;
}


/* ───────────────────────────────────────────────────────────────────────────
   CTA SURFACE 7 — .cta.cta--sticky, data-cta="sticky"
   §3.2: JOIN NOW ▸ pill, --grad-cta, 52px tall, min-width 150px.
   The ▸ is inline SVG, never a glyph font and never an emoji.
   ─────────────────────────────────────────────────────────────────────────── */

.cta--sticky{
  flex: 0 0 auto;
  min-height: 52px;                /* §3.2, literal */
  min-width: 150px;                /* §3.2, literal */
  padding-inline: var(--s-4);      /* holds the pill at its 150px minimum at 360px */
}
.cta--sticky .cta__chev{
  flex: none;
  width: 16px; height: 16px;       /* explicit size — 0 CLS */
}

/* ===== 02-statement ===== */
/* ════════════════════════════════════════════════════════════════════════
   § 2 · STATEMENT — "the promise"
   Root class: .s-state          Scene state: beam (§5.7)
   Spec: art-direction.md §4 "§ 2 · STATEMENT", §9.2 CTA surface 3.
   Consumes tokens from §1 only. Defines nothing. Styles nothing outside
   .s-state. No initial hidden state is written here — the section is fully
   readable with JavaScript disabled; the runtime owns the reveal from-state.
   ═════════════════════════════════════════════════════════════════════ */

/* ── The band ─────────────────────────────────────────────────────────── */
.s-state{
  position: relative;
  z-index: var(--z-content);
  background: var(--ink-deep);            /* full-bleed --ink-deep band     */
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
  text-align: center;
}

/* design/mobile-motion.md §C.02 (finding 5): on the tiers that own a live
   canvas, the band lets the `beam` scene state read through its top and
   settles to --ink-deep at 55% by its foot. STILLS and no-JS keep the opaque
   --ink-deep above (the document ships data-tier="stills"). The channels are
   --ink-deep (#0A0A12) exactly. Contrast rule: if --text-hi over the brightest
   beam frame measures under 7:1, raise this alpha; never dim the text. */
html[data-tier="standard"] .s-state,
html[data-tier="cinema"] .s-state{
  background: linear-gradient(to bottom, transparent 0%, rgba(10, 10, 18, 0.55) 100%);
}

.s-state .s-state__inner{
  max-width: var(--maxw);
  margin-inline: auto;
}

/* ── The statement ────────────────────────────────────────────────────── */
/* Bebas 400 at --t-statement, centred, capped at 18ch per line.
   Desktop and mobile are identical — the clamp() is the only scaling. */
.s-state .s-state__line{
  margin: 0 auto;
  max-width: 18ch;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-statement);
  line-height: var(--lh-statement);
  letter-spacing: var(--tr-display);
  color: var(--text-hi);                  /* ~18.9:1 — §7 contrast table    */
  text-wrap: balance;
}

/* One clause per trigger. design/mobile-motion.md §C.02: each clause is a
   `lines` word-mask (Latin Bebas only; the engine splits by WORD, never by
   character). inline-block so the mask host has a box; it still wraps
   internally, so the 18ch cap governs the line, not the clause. */
.s-state .s-state__clause{
  display: inline-block;
}

/* ── The exit — CTA surface 3, data-cta="statement" ───────────────────── */
/* Ghost text link, never a red button: a second --grad-cta on this screen
   would break colour law 1 (§1.16). */
.s-state .s-state__exit{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  margin-top: var(--s-7);
  min-height: var(--s-7);                 /* 48px — §7 Touch Target Size    */
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-size: var(--t-btn);
  font-weight: 600;
  letter-spacing: var(--tr-btn);
  line-height: var(--lh-label);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-low);                 /* §9.2: text link, --text-low    */
  /* The element also carries .cta (for data-cta and the press contract),
     whose base paints --grad-cta. Cancel it in every state: colour law 1
     (no second red on this screen) and --text-low on red is ~2:1. */
  background-image: none;
  background-color: transparent;
  /* Press is the `scale` lane (mobile-motion.md §B.2 / amendment 18); the
     engine's press rule supplies scale 0.97 on :active. Never `transform`:
     that lane belongs to the reveal. */
  transition:
    color var(--mo-quick) var(--ease-out),
    scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out));
}

.s-state .s-state__exit-label{
  display: inline-block;
}

/* The chevron is the only --spot-gold object in this section. Inline SVG,
   never a glyph or emoji. Decorative, so aria-hidden in the markup. */
.s-state .s-state__chev{
  display: block;
  flex: none;
  color: var(--spot-gold);
  transition: transform var(--mo-quick) var(--ease-out);
}

@media (hover: hover){
  .s-state .s-state__exit:hover{
    color: var(--text-mid);
    background-image: none;
    background-color: transparent;
  }
  .s-state .s-state__exit:hover .s-state__chev{
    transform: translateY(var(--s-1));    /* transform only — §7 banned list */
  }
}

/* §8 item 6 Press: css/motion.css ([data-press="on"] .cta:active) owns the
   scale; the base .cta:active red fill is cancelled here. */
.s-state .s-state__exit:active{
  background-image: none;
  background-color: transparent;
  color: var(--text-hi);
}

/* design/mobile-motion.md §C.02 follow-up: once the exit has resolved, the
   gold chevron nudges down twice (0 -> 4px -> 0) and stops. Uses the
   individual `translate` property so the hover `transform` still composes,
   and fill `both` is harmless (first and last frames are the rest state).
   Keyed off the engine's data-m-in and gated per §B.1. */
@media (prefers-reduced-motion: no-preference){
  [data-motion="on"] .s-state .s-state__exit[data-m-in] .s-state__chev{
    animation: s-state-nudge 900ms var(--ease-in-out) 500ms 2 both;
  }
}
@keyframes s-state-nudge{
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 var(--s-1); }
}

.s-state .s-state__exit:focus-visible{
  outline: 2px solid var(--ring);         /* §7 Focus Appearance             */
  outline-offset: 2px;
}

/* ── Reduced motion ───────────────────────────────────────────────────── */
/* The foundation owns the global duration kill; this only removes the two
   transforms this section introduces, which duration alone does not undo. */
@media (prefers-reduced-motion: reduce){
  .s-state .s-state__exit:hover .s-state__chev{ transform: none; }
}

/* ===== 03-learn ===== */
/* ═══════════════════════════════════════════════════════════════════════════
   § 3 · WHAT YOU'LL LEARN — "the filmstrip"
   Root class: .s-learn        Spec: design/art-direction.md § 3 (+ §1 tokens, §5.7, §7, §8)
   Every selector is nested under .s-learn. No token is redefined here — only consumed.

   Runtime-written custom properties (state, not design values):
     --strip-x   on .learn__track  — desktop xPercent scrub offset, default 0%
     --tilt-x / --tilt-y on .learn__card — pointer tilt, hard-capped to ±6deg in CSS below
     --cf / --cfa on .learn__card   — coverflow JS fallback pose, [-1,1] / |--cf| (js/carousel.js)
     --cf-i      on .learn__dots-ind — active index for the non-scroll-linked indicator
   ═══════════════════════════════════════════════════════════════════════════ */

.s-learn{
  position: relative;
  z-index: var(--z-content);
  padding-block: var(--section-y);
  /* §7 scrim: text blocks over the live canvas sit on an explicit scrim.
     Cards carry their own opaque --ink-panel ground; this protects the rest. */
  background-image: var(--scrim-v);
}

/* ─── content column ─────────────────────────────────────────────────────── */

.s-learn .learn__inner{
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ─── head ───────────────────────────────────────────────────────────────── */

.s-learn .learn__h2{
  margin: 0 0 var(--s-5);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--text-hi);
}

.s-learn .learn__lead{
  margin: 0 0 var(--s-3);
  max-width: var(--measure);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
  color: var(--text-mid);
}

/* "5 areas of skill, delivered as 6 short steps." — the mandatory reconciling line */
.s-learn .learn__note{
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--text-low);
  font-variant-numeric: tabular-nums;
}

/* ─── the strip ──────────────────────────────────────────────────────────── */

.s-learn .learn__strip{
  position: relative;
  margin-top: var(--s-7);
}

/* sprocket notches — 1px --edge dashes along the strip's top and bottom edges (desktop) */
.s-learn .learn__strip::before,
.s-learn .learn__strip::after{
  content: "";
  position: absolute;
  inset-inline: 0;
  height: 1px;
  pointer-events: none;
  display: none;
  background-image: repeating-linear-gradient(
    to right,
    var(--edge) 0 var(--s-3),
    transparent var(--s-3) var(--s-5)
  );
}
.s-learn .learn__strip::before{ top: 0; }
.s-learn .learn__strip::after { bottom: 0; }

/* ─── rail: a real horizontal scroll container (zero JS, keyboard-reachable) ── */

.s-learn .learn__rail{
  position: relative;               /* offsetParent for the cards: carousel.js caches centres */
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /* centre the first and last card in the scrollport at the spec'd 82vw width */
  padding-inline: max(var(--gutter), calc((100% - 82vw) / 2));
  scroll-padding-inline: max(var(--gutter), calc((100% - 82vw) / 2));
}

/* Post-audit: phones hide the scrollbar like §5 does — the dots are the position
   indicator there. At >=900px the dots are hidden, so the bar stays for mouse users. */
@media (max-width: 899.98px){
  .s-learn .learn__rail{ scrollbar-width: none; }
  .s-learn .learn__rail::-webkit-scrollbar{ display: none; }
}
.s-learn .learn__rail:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.s-learn .learn__track{
  display: flex;
  align-items: stretch;
  gap: var(--s-5);
  margin: 0;
  padding: var(--s-4) 0;      /* breathing room so --e2 / tilt are not clipped */
  list-style: none;
}

/* ─── card ───────────────────────────────────────────────────────────────── */

.s-learn .learn__card{
  /* The <li> is layout + snap only and is never transformed: Chrome measures scroll-snap
     areas on the transformed box, so a posed snap item lands off-centre. Everything visual
     (and every transform: coverflow pose, desktop tilt) lives on .learn__face. */
  position: relative;
  flex: 0 0 82vw;
  display: flex;
  scroll-snap-align: center;
}

.s-learn .learn__face{
  position: relative;
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow: hidden;                 /* clips the ghost numeral's bleed */
  display: flex;
  flex-direction: column;
  padding: var(--s-7) var(--s-6);
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  box-shadow: var(--e2);
  transition: border-color var(--mo-quick) var(--ease-out);
}

/* nearest-centre card (mobile carousel) — set by js/carousel.js on settle */
.s-learn .learn__card[data-active] .learn__face{ border-color: var(--edge-lit); }

/* ghost numeral — decorative watermark, bleeds off the top-left corner.
   --text-faint is decorative-only (§1.3); the <ol> carries the real ordering. */
.s-learn .learn__num{
  display: block;
  margin-top: calc(var(--s-5) * -1);
  margin-inline-start: calc(var(--s-4) * -1);
  margin-bottom: var(--s-4);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-ghostnum);
  line-height: var(--lh-num);
  letter-spacing: var(--tr-num);
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  user-select: none;
}

.s-learn .learn__title{
  margin: 0 0 var(--s-3);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--t-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-h3);
  text-transform: uppercase;
  color: var(--text-hi);
  text-wrap: balance;
}

.s-learn .learn__body{
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text-mid);
}

/* ─── progress dots — mobile only, decorative, never interactive ──────────── */

.s-learn .learn__dots{
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-5);
  pointer-events: none;
}

/* The dots are a fixed track; position is shown by one indicator that glides over them
   (§E.6). Nothing here animates a width (art-direction §7 bans layout animation). */
.s-learn .learn__dot{
  display: block;
  inline-size: var(--s-2);
  block-size: var(--s-2);
  border-radius: var(--r-pill);
  background: var(--edge-strong);
}

/* no carousel.js (JS off, ≥900px): the indicator does not exist, the dots are a static hint */
.s-learn .learn__dots-ind{ display: none; }

/* ─── mini-CTA (landing: funnel-3-step-conversion) ───────────────────────── */

.s-learn .learn__cta-wrap{
  margin: var(--s-7) 0 0;
}

.s-learn .learn__cta{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-block-size: var(--s-7);            /* 48px — ux: Touch Target Size */
  padding: var(--s-3) var(--s-6);
  background: transparent;
  border: 1px solid var(--rec-red);
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--t-btn);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-btn);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-hi);
  /* press lives on the `scale` lane (mobile-motion §B.2, §B.5 note, amendment 18) */
  transition:
    background-color var(--mo-quick) var(--ease-out),
    border-color var(--mo-quick) var(--ease-out),
    color var(--mo-quick) var(--ease-out),
    scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out));
}

.s-learn .learn__chev{ font-size: var(--t-micro); line-height: 1; }

.s-learn .learn__cta:hover{
  background-image: var(--grad-cta);
  border-color: var(--rec-red-hot);
  color: var(--on-rec);
}

.s-learn .learn__cta:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.s-learn .learn__cta:active{
  /* §8.6 press scale comes from motion.css (`scale` lane); colour only here */
  background-color: var(--rec-red-deep);
  border-color: var(--rec-red-deep);
  color: var(--on-rec);
}

/* ═══ ≥768px — the card can breathe before the filmstrip kicks in ═════════ */

@media (min-width: 768px){
  .s-learn .learn__track{ gap: var(--s-6); }
}

/* ═══ ≥900px — DESKTOP FILMSTRIP ═════════════════════════════════════════ */

@media (min-width: 900px){

  .s-learn .learn__strip::before,
  .s-learn .learn__strip::after{ display: block; }

  .s-learn .learn__rail{
    /* strip runs wider than --maxw; first card aligns with the content column */
    padding-inline: max(var(--gutter), calc((100% - var(--maxw)) / 2));
    scroll-padding-inline: max(var(--gutter), calc((100% - var(--maxw)) / 2));
    scroll-snap-type: none;
  }

  .s-learn .learn__card{
    flex: 0 0 var(--measure);            /* the card body is a paragraph; cap it at the measure */
    scroll-snap-align: none;
  }

  .s-learn .learn__dots{ display: none; }

  .s-learn .learn__cta-wrap{ margin-top: var(--s-8); }

  /* scroll-scrubbed xPercent translation — runtime opts in with data-scrub="on"
     and writes --strip-x. `overflow-x: clip` keeps overflow-y visible and stops
     an off-screen focus from silently scrolling the rail. */
  .s-learn[data-scrub="on"] .learn__rail{
    overflow-x: hidden;
    overflow-x: clip;
    overflow-y: visible;
  }

  .s-learn[data-scrub="on"] .learn__track{
    transform: translate3d(var(--strip-x, 0%), 0, 0);
    will-change: transform;
  }

  /* PINNED FILMSTRIP. Unpinned, five ~520px cards had only the ~390px of scroll
     in which the strip is fully on screen to travel ~1540px: modules 01, 04 and
     05 were never fully readable on desktop. Now the strip sticks (centred; the
     runtime writes --strip-top) while the page scrolls through --strip-travel of
     padding on its wrapper, and that distance drives the track 1:1 — every
     module glides through the centre of the screen. No ancestor between here and
     the viewport has overflow other than visible/clip, which sticky requires. */
  /* The travel must be CONTENT, not padding: a sticky box may only move within its
     containing block's content box, and padding sits outside it — as padding the
     strip had zero room and never stuck. An in-flow spacer is content. */
  .s-learn[data-scrub="on"] .learn__pin::after{
    content: "";
    display: block;
    height: var(--strip-travel, 0px);
  }
  .s-learn[data-scrub="on"] .learn__strip{
    position: sticky;
    top: var(--strip-top, calc(var(--bar-top) + var(--s-7)));
  }
}

/* ═══ pointer tilt — desktop, fine pointer only, hard-capped at ±6° ═══════ */

@media (min-width: 900px) and (hover: hover) and (pointer: fine){

  .s-learn .learn__card[data-tilt] .learn__face{
    transform:
      perspective(900px)
      rotateX(clamp(-6deg, var(--tilt-x, 0deg), 6deg))
      rotateY(clamp(-6deg, var(--tilt-y, 0deg), 6deg));
    transition:
      transform var(--mo-quick) var(--ease-out),
      border-color var(--mo-quick) var(--ease-out),
      box-shadow var(--mo-quick) var(--ease-out);
  }

  .s-learn .learn__card[data-tilt]:hover .learn__face{
    border-color: var(--edge-lit);
    box-shadow: var(--e3);
  }
}

/* ═══ reduced motion — ux: Reduced Motion (High) ═════════════════════════ */

@media (prefers-reduced-motion: reduce){

  .s-learn .learn__rail{ scroll-behavior: auto; }

  .s-learn .learn__face,
  .s-learn .learn__card[data-tilt] .learn__face,
  .s-learn .learn__card[data-tilt]:hover .learn__face{
    transform: none;
    box-shadow: var(--e2);
  }

  .s-learn[data-scrub="on"] .learn__track{
    transform: none;
    will-change: auto;
  }
  /* the runtime never enables the scrub under reduced motion; belt and braces */
  .s-learn[data-scrub="on"] .learn__pin::after{ height: 0; }
  .s-learn[data-scrub="on"] .learn__strip{ position: relative; }
}


/* ═══ COVERFLOW — design/mobile-motion.md §E, amendments 1, 12, 13, 17 ═══════
   Phones only (<900px). Four layers, each with its own gate:
     1. layout (68vw cards, centred ends)      [data-motion="on"]
     2. per-card pose, native view(inline)     + no-preference + not data-m-lite + view()
     3. per-card pose, JS fallback             [data-cf-js] (carousel.js sets it only when 2 is
                                               unsupported and motion is on)
     4. dots indicator                         [data-cf-ready] (carousel.js, always <900px);
                                               scroll-linked glide only with motion on
   No data-motion (JS off, reduced motion, engine never armed) = today's flat 82vw strip.
   Keyframes carry transform and opacity only, so they stay on the compositor (amendment 13). */

@media (max-width: 899.98px){

  [data-motion="on"] .s-learn .learn__rail{
    padding-inline: calc((100% - 68vw) / 2);
    scroll-padding-inline: calc((100% - 68vw) / 2);
    touch-action: pan-x pan-y;
  }
  [data-motion="on"] .s-learn .learn__track{ gap: var(--s-3); }

  /* The track must span every card: otherwise the cards overflow a 1-card-wide track, the
     rail's inline-end padding is not added to the scrollable width, and the last card can
     never reach centre (worse under coverflow, whose scaled edge cards shrink the overflow). */
  .s-learn .learn__track{ inline-size: max-content; }
  [data-motion="on"] .s-learn .learn__card{ flex-basis: 68vw; }
  [data-motion="on"] .s-learn .learn__face{ padding-inline: var(--s-5); }

  /* indicator: one dot-sized pill over the track of 5 dots (8px dot + 8px gap = --s-4 step) */
  .s-learn[data-cf-ready] .learn__dots-ind{
    display: block;
    position: absolute;
    top: 0;
    inset-inline-start: calc(50% - (2 * 5 - 1) * var(--s-2) / 2);
    inline-size: var(--s-2);
    block-size: var(--s-2);
    border-radius: var(--r-pill);
    background: var(--text-mid);
    translate: calc(var(--cf-i, 0) * var(--s-4)) 0;
  }
}

@media (max-width: 899.98px) and (prefers-reduced-motion: no-preference){

  /* layer promotion only while the rail is on screen (carousel.js IO writes data-cf-live) */
  [data-motion="on"]:not([data-m-lite]) .s-learn .learn__rail[data-cf-live] .learn__face{ will-change: transform; }

  /* fallback indicator glides between settled positions */
  [data-motion="on"] .s-learn[data-cf-ready] .learn__dots-ind{
    transition: translate var(--mo-quick) var(--ease-out);
  }

  /* 3 · JS fallback pose (Chrome < 115, older iOS). Linear in --cf; clamped by carousel.js. */
  [data-motion="on"]:not([data-m-lite]) .s-learn .learn__rail[data-cf-js] .learn__face{
    transform: perspective(1000px) rotateY(calc(var(--cf, 0) * 24deg)) scale(calc(1 - var(--cfa, 0) * .1));
    opacity: calc(1 - var(--cfa, 0) * .28);
    backface-visibility: hidden;
  }
}

@supports (animation-timeline: view()){
  @media (max-width: 899.98px) and (prefers-reduced-motion: no-preference){

    /* 2 · native coverflow: each card's pose is a function of its own position in the rail.
       The untransformed <li> is the timeline subject; its .learn__face is posed. Inset 0:
       measured against the full scrollport, so a resting neighbour sits at ~94.5% of its range
       (fully turned) and the centred card at 50% (flat, full size). */
    [data-motion="on"]:not([data-m-lite]) .s-learn .learn__rail[data-coverflow] .learn__card[data-cf-item]{
      view-timeline: --cf-card inline;
      view-timeline-inset: 0px;
    }
    [data-motion="on"]:not([data-m-lite]) .s-learn .learn__rail[data-coverflow] .learn__card[data-cf-item] > .learn__face{
      animation: learn-cf linear both;
      animation-timeline: --cf-card;
      animation-range: cover 0% cover 100%;
      backface-visibility: hidden;
    }
  }
}

@supports (animation-timeline: view()) and (timeline-scope: --a){
  @media (max-width: 899.98px) and (prefers-reduced-motion: no-preference){

    /* 4 · the indicator glides continuously under the thumb (§E.6, amendment 17a) */
    [data-motion="on"]:not([data-m-lite]) .s-learn{ timeline-scope: --cf-learn; }
    [data-motion="on"]:not([data-m-lite]) .s-learn .learn__rail{ scroll-timeline: --cf-learn inline; }
    [data-motion="on"]:not([data-m-lite]) .s-learn[data-cf-ready] .learn__dots-ind{
      transition: none;
      animation: learn-dots linear both;
      animation-timeline: --cf-learn;
    }
  }
}

@keyframes learn-cf{
  0%  { transform: perspective(1000px) rotateY(24deg)  scale(.9);  opacity: .72; }
  35% { transform: perspective(1000px) rotateY(8deg)   scale(.97); opacity: .9;  }
  50% { transform: perspective(1000px) rotateY(0deg)   scale(1);   opacity: 1;   }
  65% { transform: perspective(1000px) rotateY(-8deg)  scale(.97); opacity: .9;  }
  100%{ transform: perspective(1000px) rotateY(-24deg) scale(.9);  opacity: .72; }
}
@keyframes learn-dots{
  from{ translate: 0 0; }
  to  { translate: calc(4 * var(--s-4)) 0; }
}

/* ===== 04-facts ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 4 · THE FACTS — "the spec sheet"        root: .s-facts
   Tokens consumed from §1 only. Nothing redefined, nothing hardcoded.
   Ground is deliberately TRANSPARENT: the page's --ink-void body shows through
   and the `grid` scene state (points snapped to a 3D lattice) reads between the
   opaque --ink-panel tiles. Text contrast is held by the canvas lerping to
   uOpacity 0.12 behind this section (§7), declared as data-scene-opacity.
   ═══════════════════════════════════════════════════════════════════════ */

.s-facts{
  position: relative;
  z-index: var(--z-content);
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
  background: transparent;
}

.s-facts .facts-inner{
  max-width: var(--maxw);
  margin-inline: auto;
}

/* ── heading ─────────────────────────────────────────────────────────── */

.s-facts .facts-h{
  margin: 0 0 var(--s-6);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--text-hi);
}

@media (min-width: 768px){
  .s-facts .facts-h{ margin-bottom: var(--s-7); }
}

/* ── the tile grid — 2×3 mobile, 3×2 desktop ─────────────────────────── */

.s-facts .facts-grid{
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3);
}

@media (min-width: 768px){
  .s-facts .facts-grid{
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s-4);
  }
}

@media (min-width: 900px){
  .s-facts .facts-grid{ gap: var(--s-5); }
}

/* ── tile ────────────────────────────────────────────────────────────── */

.s-facts .fact{
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-width: 0;
  padding: var(--s-5) var(--s-4);
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: var(--r-md);
}

@media (min-width: 900px){
  .s-facts .fact{ padding: var(--s-6) var(--s-5); }
}

/* At the content cap the tiles go wide; --s-7/--s-6 is the same padding §3's
   curriculum cards carry, so the two card languages match at desktop. */
@media (min-width: 1200px){
  .s-facts .facts-grid{ gap: var(--s-6); }
  .s-facts .fact{ padding: var(--s-7) var(--s-6); }
}

/* the numeral — solid proof lavender (owner 2026-09-23; gold = price only). Solid, not the
   aurora: it reveals through the engine's moving roll mask, and gradient-clipped text can
   drop out on a transformed child mid-animation. */
.s-facts .fact-num{
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-tilenum);
  line-height: var(--lh-num);
  letter-spacing: var(--tr-num);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--proof-solid);
}

/* U+20B9 ₹ is outside Bebas Neue's latin / latin-ext subsets. Set it in Inter
   deliberately rather than letting it fall to an unknown system face. */
.s-facts .fact-rupee{
  font-family: var(--font-body);
  font-weight: 500;
}

.s-facts .fact-label{
  display: block;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-mid);
  text-wrap: balance;
}

/* ── the anchor line (§9.3 — the price appears in the §4 comparison;
      §6 — "The real anchor is stated in §4 and restated here")
      No .00 here: §4 is not a checkout-adjacent surface (§6). ─────────── */

.s-facts .facts-anchor{
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
  margin: var(--s-7) 0 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text-low);
}

.s-facts .facts-anchor-price{
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-price-mid);
  line-height: var(--lh-num);
  letter-spacing: var(--tr-num);
  font-variant-numeric: tabular-nums;
  color: var(--spot-gold);
}

.s-facts .facts-anchor-rest{
  max-width: var(--measure);
}

/* NOTE: §1.9 steps body copy 17px → 18px at ≥768px. That step belongs to the
   foundation (it must redefine --t-body once, globally); this section only
   consumes --t-body and inherits the step. No local px override here. */

/* ===== 05-results ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 5 · RESULTS (reserved, hidden) + the AI sample video carousel
   root: .s-results        Tokens consumed from §1 only. Nothing redefined.
   Ground is --ink-deep: §4 above and §6 below both sit on --ink-void, so the
   proof block reads as its own band without introducing a new colour.
   The carousel is the §3 graft applied to video: native scroll-snap at 82vw,
   no JS, no hijack, keyboard-reachable. The socket keeps the same type scale
   so un-hiding it later needs no design work.
   ═══════════════════════════════════════════════════════════════════════ */

.s-results{
  position: relative;
  z-index: var(--z-content);
  padding-block: var(--section-y);
  background: var(--ink-deep);
}

/* the socket ships hidden and stays hidden whatever else lands above it */
.s-results[hidden]{ display: none; }

.s-results .results__inner{
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ── heading block ───────────────────────────────────────────────────── */

.s-results .results__eyebrow{
  margin: 0 0 var(--s-3);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-low);
}

.s-results .results__h2{
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--text-hi);
  font-variant-numeric: tabular-nums;
  text-wrap: balance;
}

.s-results .results__lead{
  margin: var(--s-4) 0 0;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
  color: var(--text-mid);
}

/* ── @naan_babybro — the social proof link the old site never had ─────── */

.s-results .results__handle{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: var(--s-7);            /* 48px — ux: Touch Target Size (48dp) */
  margin-top: var(--s-3);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: var(--tr-num);
  color: var(--text-low);
  text-decoration: underline;
  text-underline-offset: var(--s-1);
  /* press lives on the `scale` lane (data-m-press, mobile-motion §B.5 note) */
  transition:
    color var(--mo-quick) var(--ease-out),
    scale var(--m-press-t, var(--mo-spring)) var(--m-press-e, var(--ease-spring));
}
.s-results .results__handle:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  color: var(--text-hi);
}
.s-results .results__ig{ flex: none; display: block; }

.s-results .results__note{
  margin: var(--s-2) 0 0;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--text-low);
}

/* ── the rail — a real horizontal scroll container (spec §3 graft) ────── */

.s-results .results__rail{
  position: relative;               /* offsetParent for the slides: carousel.js caches centres */
  margin-top: var(--s-7);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* centre the first and last card in the scrollport at the spec'd 82vw */
  padding-inline: max(var(--gutter), calc((100% - 82vw) / 2));
  scroll-padding-inline: max(var(--gutter), calc((100% - 82vw) / 2));
}
.s-results .results__rail::-webkit-scrollbar{ display: none; }
.s-results .results__rail:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.s-results .results__track{
  display: flex;
  gap: var(--s-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

.s-results .results__slide{
  flex: 0 0 82vw;
  scroll-snap-align: center;
}

.s-results .results__card{
  position: relative;
  margin: 0;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  box-shadow: var(--e2);
  overflow: hidden;
  /* colour only: box-shadow is never transitioned (mobile motion rule: compositor-only) */
  transition: border-color var(--mo-quick) var(--ease-out);
}

/* the card nearest centre lights up — js/carousel.js writes data-active on settle */
.s-results .results__slide[data-active] .results__card,
.s-results .results__card:focus-within{
  border-color: var(--edge-lit);
  box-shadow: var(--e3);
}

/* The poster frame is a lazy <img> under the video (a <video poster> would be
   fetched on first load, ~142KB for three reels 2.9k px down the page). The
   video paints nothing until it has a frame, so the image shows through it. */
.s-results .results__poster{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  background: var(--ink-raised);
  pointer-events: none;
}
.s-results .results__video{
  position: relative;                /* paints above the positioned poster */
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 608 / 1080;          /* the asset's own frame — 0 CLS */
  object-fit: cover;
  background: transparent;
  border-radius: inherit;
}
.s-results .results__video:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

/* corner tag — top-left, so it never collides with the native play control */
.s-results .results__tag{
  position: absolute;
  top: var(--s-3);
  left: var(--s-3);
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  background: var(--ink-void);
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-hi);
  font-variant-numeric: tabular-nums;
  pointer-events: none;              /* never steals a tap from the video */
}
.s-results .results__glyph{
  flex: none;
  display: block;
  width: var(--s-3);
  height: var(--s-3);
}

/* ── progress dots — decorative, never interactive.
      Colour only: width/height are never animated (spec §7 motion bans).
      Position is shown by .results__dots-ind gliding over the fixed dots (§E.6). ── */

.s-results .results__dots{
  position: relative;
  display: flex;
  justify-content: center;
  gap: var(--s-2);
  margin-top: var(--s-5);
}
.s-results .results__dot{
  width: var(--s-2);
  height: var(--s-2);
  border-radius: var(--r-pill);
  background: var(--edge-strong);
  transition: background-color var(--mo-quick) var(--ease-out);
}
.s-results .results__dot[data-active]{ background: var(--text-low); }
.s-results .results__dots-ind{ display: none; }

/* ── the reserved socket's three-up editorial grid ────────────────────── */

.s-results .results__grid{
  display: grid;
  gap: var(--s-5);
  list-style: none;
  margin: var(--s-7) 0 0;
  padding: 0;
}
.s-results .results__result{
  margin: 0;
  background: var(--ink-panel);
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  box-shadow: var(--e2);
  overflow: hidden;
}
.s-results .results__shot{
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 608 / 1080;
  object-fit: cover;
  background: var(--ink-raised);
}
.s-results .results__who{ padding: var(--s-5); }
.s-results .results__name{
  margin: 0 0 var(--s-2);
  font-family: var(--font-body);
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-h3);
  color: var(--text-hi);
}
.s-results .results__quote{
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text-mid);
}

/* ── pointer devices only ─────────────────────────────────────────────── */

@media (hover: hover) and (pointer: fine){
  .s-results .results__handle:hover{ color: var(--text-hi); }
  .s-results .results__card:hover{
    border-color: var(--edge-lit);
    box-shadow: var(--e3);
  }
}

/* ── >=768px: three up, no sideways scroll.
      One step earlier than the page's 900px split on purpose: three 9:16
      videos at 82vw are ~1120px tall on a tablet, which is a wall, not proof.
      Same breakpoint §4 uses for its tile grid.                          ── */

@media (min-width: 768px){
  .s-results .results__rail{
    max-width: var(--maxw);
    margin-inline: auto;
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
    overflow: visible;
    scroll-snap-type: none;
  }
  .s-results .results__track{
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-5);
  }
  .s-results .results__slide{
    flex: none;
    scroll-snap-align: none;
  }
  .s-results .results__dots{ display: none; }

  .s-results .results__grid{
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-6);
  }
}

@media (min-width: 1200px){
  .s-results .results__rail,
  .s-results .results__grid{ margin-top: var(--s-8); }
  .s-results .results__track{ gap: var(--s-6); }
}

/* ── reduced motion: nothing here animates but the colour fades; kill the
      snap's smooth scrolling too (ux: Reduced Motion, High).            ── */

@media (prefers-reduced-motion: reduce){
  .s-results .results__rail{ scroll-behavior: auto; }
  .s-results .results__card,
  .s-results .results__dot,
  .s-results .results__handle{ transition: none; }
}


/* ═══ COVERFLOW — design/mobile-motion.md §E, amendments 1, 12, 13, 15, 17 ═══
   Below 768px only (this rail becomes a static 3-up grid at 768px). Gates:
     layout (68vw, centred ends)          [data-motion="on"]
     per-slide pose, native view(inline)  + no-preference + not data-m-lite + view()
     per-slide pose, JS fallback          [data-cf-js] (carousel.js)
     tap-to-centre, dots indicator        [data-cf-ready] (carousel.js, always <768px, any motion
                                          setting, amendment 12)
   No data-motion (JS off, reduced motion) = today's flat 82vw rail.
   Keyframes carry transform and opacity only (amendment 13). */

@media (max-width: 767.98px){

  [data-motion="on"] .s-results .results__rail{
    padding-inline: calc((100% - 68vw) / 2);
    scroll-padding-inline: calc((100% - 68vw) / 2);
    touch-action: pan-x pan-y;
  }
  [data-motion="on"] .s-results .results__track{ gap: var(--s-3); }

  /* the track spans every slide, so the rail's end padding lets the last reel centre */
  .s-results .results__track{ inline-size: max-content; }
  [data-motion="on"] .s-results .results__slide{ flex-basis: 68vw; }

  /* a side reel's first tap centres it instead of hitting its native controls (§E.4) */
  .s-results[data-cf-ready] .results__slide[data-cf-item]:not([data-active]) .results__video{
    pointer-events: none;
  }
  .s-results[data-cf-ready] .results__slide[data-cf-item]:not([data-active]){ cursor: pointer; }

  /* the old active-dot colour is replaced by the indicator */
  .s-results[data-cf-ready] .results__dot[data-active]{ background: var(--edge-strong); }
  .s-results[data-cf-ready] .results__dots-ind{
    display: block;
    position: absolute;
    top: 0;
    inset-inline-start: calc(50% - (2 * 3 - 1) * var(--s-2) / 2);
    inline-size: var(--s-2);
    block-size: var(--s-2);
    border-radius: var(--r-pill);
    background: var(--text-low);
    translate: calc(var(--cf-i, 0) * var(--s-4)) 0;
  }
}

@media (max-width: 767.98px) and (prefers-reduced-motion: no-preference){

  [data-motion="on"]:not([data-m-lite]) .s-results .results__rail[data-cf-live] .results__card{ will-change: transform; }

  [data-motion="on"] .s-results[data-cf-ready] .results__dots-ind{
    transition: translate var(--mo-quick) var(--ease-out);
  }

  [data-motion="on"]:not([data-m-lite]) .s-results .results__rail[data-cf-js] .results__card{
    transform: perspective(1000px) rotateY(calc(var(--cf, 0) * 24deg)) scale(calc(1 - var(--cfa, 0) * .1));
    opacity: calc(1 - var(--cfa, 0) * .28);
    backface-visibility: hidden;
  }
}

@supports (animation-timeline: view()){
  @media (max-width: 767.98px) and (prefers-reduced-motion: no-preference){
    /* the <li> is the untransformed snap item and timeline subject; its figure is posed
       (Chrome measures snap areas on the transformed box) */
    [data-motion="on"]:not([data-m-lite]) .s-results .results__rail[data-coverflow] .results__slide[data-cf-item]{
      view-timeline: --cf-slide inline;
      view-timeline-inset: 0px;
    }
    [data-motion="on"]:not([data-m-lite]) .s-results .results__rail[data-coverflow] .results__slide[data-cf-item] > .results__card{
      animation: results-cf linear both;
      animation-timeline: --cf-slide;
      animation-range: cover 0% cover 100%;
      backface-visibility: hidden;
    }
  }
}

@supports (animation-timeline: view()) and (timeline-scope: --a){
  @media (max-width: 767.98px) and (prefers-reduced-motion: no-preference){
    [data-motion="on"]:not([data-m-lite]) .s-results--showcase{ timeline-scope: --cf-showcase; }
    [data-motion="on"]:not([data-m-lite]) .s-results--showcase .results__rail{ scroll-timeline: --cf-showcase inline; }
    [data-motion="on"]:not([data-m-lite]) .s-results--showcase[data-cf-ready] .results__dots-ind{
      transition: none;
      animation: results-dots linear both;
      animation-timeline: --cf-showcase;
    }
  }
}

@keyframes results-cf{
  0%  { transform: perspective(1000px) rotateY(24deg)  scale(.9);  opacity: .72; }
  35% { transform: perspective(1000px) rotateY(8deg)   scale(.97); opacity: .9;  }
  50% { transform: perspective(1000px) rotateY(0deg)   scale(1);   opacity: 1;   }
  65% { transform: perspective(1000px) rotateY(-8deg)  scale(.97); opacity: .9;  }
  100%{ transform: perspective(1000px) rotateY(-24deg) scale(.9);  opacity: .72; }
}
@keyframes results-dots{
  from{ translate: 0 0; }
  to  { translate: calc(2 * var(--s-4)) 0; }
}

/* ===== 06-offer ===== */
/* ════════════════════════════════════════════════════════════════════════
   § 6 · THE OFFER — "the ticket".  THE MONEY SECTION.
   Root class: .s-offer          Scene state: converge (§5.7)
   Spec: art-direction.md §4 "§ 6 · THE OFFER", §5.6 (certificate disc),
         §9.2 CTA surface 5, §9.3 price framing, §1.16 colour laws.

   Consumes tokens from §1 only — defines none, redefines none. Styles nothing
   outside .s-offer. No initial hidden state: the price, the CTA and every
   claim are readable with JavaScript disabled; css/motion.css owns the
   reveal from-states for [data-m] (design/mobile-motion.md §B). The one
   from-state in this file, the leader draw, is gated per §B.1.
   ═════════════════════════════════════════════════════════════════════ */

/* ── The band ─────────────────────────────────────────────────────────── */
/* Full-bleed --ink-void. The background is left TRANSPARENT rather than
   painted: --ink-void IS the page ground and IS the renderer's clear colour
   (§5.2 setClearColor(0x050507)), so transparent renders identically while
   letting the `converge` slab and the certificate disc read through. An
   opaque fill here would occlude the one scene state this section exists to
   show. Contrast is unaffected — all copy sits on the opaque card. */
.s-offer{
  position: relative;
  z-index: var(--z-content);
  background: transparent;
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
}

.s-offer .s-offer__inner{
  max-width: var(--maxw);
  margin-inline: auto;
}

/* ── The card ─────────────────────────────────────────────────────────── */
/* Mobile: calc(100% - 2*--gutter) — delivered by the band's padding-inline
   plus width:100%. Desktop: capped at the spec's 460px, centred. */
.s-offer .s-offer__card{
  position: relative;
  z-index: var(--z-raised);
  width: 100%;
  max-width: 460px;
  margin-inline: auto;
  padding: var(--s-9) var(--s-5) var(--s-7);
  background: var(--ink-panel);
  border: 1px solid var(--spot-gold-dim);
  border-radius: var(--r-lg);
  box-shadow: var(--e4);
  text-align: center;
}

/* ── Certificate sticker ──────────────────────────────────────────────── */
/* Tilted -4deg over the top-right corner. transform only — nothing here
   animates width/height/top/left (§7 banned list). The card's --s-9 top
   padding is the room the sticker occupies, so it never collides with the
   h2. Max-width is composed from tokens (--s-10 + --s-7 = 176px).

   transform-origin is the BOTTOM-RIGHT corner deliberately: rotating about
   the top-right swings the bottom-right corner outward by height*sin(4deg),
   which at 360px pushes the sticker past the viewport edge and creates a
   horizontal page scroll. Pinned at the bottom-right, no corner ever travels
   further right than the untransformed box. */
.s-offer .s-offer__badge{
  position: absolute;
  z-index: var(--z-raised);
  top: calc(-1 * var(--s-4));
  right: calc(-1 * var(--s-2));
  max-width: calc(var(--s-10) + var(--s-7));
  margin: 0;
  padding: var(--s-3) var(--s-4);
  background-image: var(--grad-price);
  color: var(--on-gold);                  /* the ONLY text colour on gold    */
  border-radius: var(--r-sm);
  box-shadow: var(--e2);
  /* The resting tilt lives on the individual `rotate` property (mobile-
     motion.md §B.2 lane `rotate`), so the reveal engine's `transform`
     from-state composes with it instead of flattening the sticker. */
  rotate: -4deg;
  transform-origin: 100% 100%;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 600;
  line-height: var(--lh-label);
  text-align: left;
  text-wrap: balance;
}

/* Browsers without individual transform properties keep the old tilt. */
@supports not (rotate: 0deg){
  .s-offer .s-offer__badge{ transform: rotate(-4deg); }
}

/* ── Title + sub ──────────────────────────────────────────────────────── */
.s-offer .s-offer__title{
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-statement);
  line-height: var(--lh-statement);
  letter-spacing: var(--tr-display);
  color: var(--text-hi);                  /* ~18.9:1 — §7 contrast table     */
}

.s-offer .s-offer__sub{
  margin: var(--s-2) 0 0;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: var(--lh-label);
  color: var(--text-mid);                 /* ~11.5:1                         */
  text-wrap: balance;
}

/* ── The price — the largest object on this screen ────────────────────── */
/* One of exactly two glowing DOM elements on the page (§1.16 law 5): the
   hero CTA carries --glow-cta, this numeral carries --glow-price. The offer
   CTA below therefore carries NO glow. */
.s-offer .s-offer__price{
  margin: var(--s-5) 0 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-price);
  line-height: var(--lh-num);
  letter-spacing: var(--tr-num);
  font-variant-numeric: tabular-nums;     /* §2 — every numeral              */
  color: var(--proof-solid);              /* fallback; the spans wear the aurora (.u-proof) */
  /* mobile-motion.md amendment 23: the price arrives as a centred `card`
     focus-pull. No roll, no shimmer: both would clip the glow. */
  transform-origin: 50% 50%;
}

/* .00 is mandatory here and never dropped for typographic tidiness (§6).
   0.45em, baseline-aligned — the spec's own number. */
/* the violet glow, on the gradient text itself (see the colour note above) */
.s-offer .s-offer__price .u-proof{ filter: drop-shadow(0 0 22px rgba(192,132,252,.30)); }

.s-offer .s-offer__dec{
  font-size: 0.45em;
  vertical-align: baseline;
}

/* The only permitted phrasing (§10.2). The source says "comes with NO time
   limit!" — it does not say the forbidden L-word, and neither does this file. */
/* LIMITED OFFER · ~~₹2,000.00~~ — sits directly over the big price, centred with
   the card head. The price below then needs less top margin to stay one group. */
.s-offer .s-offer__was{
  margin: var(--s-5) 0 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  line-height: var(--lh-label);
}
.s-offer .s-offer__was .price-was{ font-size: var(--t-body); }
/* only while the row is actually showing: a hidden sibling still matches `+` */
html[data-offer="on"] .s-offer .s-offer__was + .s-offer__price{ margin-top: var(--s-2); }

.s-offer .s-offer__note{
  margin: var(--s-2) 0 0;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: var(--lh-label);
  color: var(--text-low);                 /* ~7.5:1 — the floor for copy     */
}

/* ── The program description (verbatim) ───────────────────────────────── */
.s-offer .s-offer__body{
  margin-block: var(--s-6) 0;
  margin-inline: auto;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text-mid);
  text-align: left;
}

/* ── What is inside — dot-leader rows ─────────────────────────────────── */
/* The leaders are drawn in CSS, not typed as runs of full stops, so each row
   reads as "Introduction, 2 steps" to a screen reader and reflows at 360px. */
.s-offer .s-offer__inside{
  margin: var(--s-6) 0 0;
  padding: 0;
  list-style: none;
  text-align: left;
  border-top: 1px solid var(--edge);
}

.s-offer .s-offer__row{
  display: flex;
  align-items: flex-end;
  gap: var(--s-2);
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--edge);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
}

.s-offer .s-offer__row-name{
  color: var(--text-mid);
}

/* --text-faint is the page's one DECORATIVE-ONLY value (§1.3). A dot leader
   is exactly that: it is not copy and not a label, so it is the correct
   token here — --edge-strong disappears entirely at this dot size. */
.s-offer .s-offer__leader{
  flex: 1 1 var(--s-5);
  min-width: var(--s-5);
  margin-block-end: var(--s-1);
  border-bottom: 1px dotted var(--text-faint);
}

/* design/mobile-motion.md §C.06 Enter 2 follow-up: as each row resolves, its
   dotted leader DRAWS left to right (clip-path, lane reserved for exactly
   this in §B.2). The from-state exists only on an armed, not-yet-revealed
   row, under the engine's gate and no-preference (§B.1); everywhere else the
   leader is simply there. The draw starts 120ms after its row's own delay. */
@media (prefers-reduced-motion: no-preference){
  [data-motion="on"] .s-offer .s-offer__row[data-m-armed]:not([data-m-in]) .s-offer__leader{
    clip-path: inset(0 100% 0 0);
  }
  [data-motion="on"] .s-offer .s-offer__row[data-m-in] .s-offer__leader{
    clip-path: inset(0 0 0 0);
    transition: clip-path var(--mo-slow) var(--ease-out) calc(var(--m-delay, 0ms) + 120ms);
  }
  [data-motion="on"] .s-offer .s-offer__row[data-m-instant] .s-offer__leader{
    transition: none;
  }
}

.s-offer .s-offer__row-val{
  flex: none;
  white-space: nowrap;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-hi);
}

.s-offer .s-offer__more{
  margin: var(--s-3) 0 0;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--text-low);
  text-align: left;
}

/* ── CTA surface 5 — the money button ─────────────────────────────────── */
/* Full-width, 60px, --grad-cta. No glow (§1.16 law 5). No red anywhere else
   in this section, so this is the only exit (§1.16 law 1). */
.s-offer .s-offer__cta{
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 60px;
  margin-block-start: var(--s-7);
  padding-inline: var(--s-5);
  border: 0;
  border-radius: var(--r-pill);
  background-image: var(--grad-cta);
  color: var(--on-rec);                   /* #FFFFFF on --rec-red = 4.70:1   */
  font-family: var(--font-body);
  font-size: var(--t-btn);
  font-weight: 600;
  letter-spacing: var(--tr-btn);
  line-height: var(--lh-label);
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  /* Press is the `scale` lane (mobile-motion.md §B.2 / amendment 18). While
     the CTA is armed for its `rise`, css/motion.css's shared list owns
     `transition`; this list applies before arming and after data-m-done. */
  transition:
    background-color var(--mo-quick) var(--ease-out),
    scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out));
}

@media (hover: hover){
  .s-offer .s-offer__cta:hover{
    background-image: none;
    background-color: var(--rec-red-hot);
  }
}

.s-offer .s-offer__cta:active{
  background-image: none;
  background-color: var(--rec-red-deep);
  /* §8 item 6 Press: scale 0.97 comes from css/motion.css (lane `scale`). */
}

.s-offer .s-offer__cta:focus-visible{
  outline: 2px solid var(--ring);         /* §7 Focus Appearance             */
  outline-offset: 2px;
}

/* ── The real anchor ──────────────────────────────────────────────────── */
/* The comparison is not "1,499 vs 4,999" — it is "1,499 vs an afternoon and
   zero tool costs". Never a fabricated higher number, never a strike-through,
   never a countdown (§6 price framing). The price fragment is gold because
   it IS the price (§1.16 law 2). */
.s-offer .s-offer__anchor{
  margin: var(--s-4) 0 0;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: var(--lh-label);
  font-variant-numeric: tabular-nums;
  color: var(--text-low);
  text-wrap: balance;
}

.s-offer .s-offer__anchor-price{
  color: var(--spot-gold);
  font-weight: 600;
}

/* ── Razorpay trust mark ──────────────────────────────────────────────── */
/* A bordered chip, not 11px grey under a button. No red, no gold — the scam
   refutation is the name itself. */
.s-offer .s-offer__trust{
  margin: var(--s-5) 0 0;
}

.s-offer .s-offer__chip{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: var(--lh-label);
  color: var(--text-low);
}

.s-offer .s-offer__lock{
  display: block;
  flex: none;
  color: var(--text-low);
}

.s-offer .s-offer__mark{
  color: var(--text-hi);
  font-weight: 600;
  letter-spacing: var(--tr-h3);
}

/* ── Questions? ───────────────────────────────────────────────────────── */
.s-offer .s-offer__mail{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  margin: var(--s-3) 0 0;
  font-family: var(--font-body);
  font-size: var(--t-micro);
  line-height: var(--lh-label);
  color: var(--text-low);
}

.s-offer .s-offer__mail a{
  display: inline-flex;
  align-items: center;
  min-height: var(--s-7);                 /* 48px — §7 Touch Target Size     */
  padding-inline: var(--s-1);
  color: var(--text-mid);
  text-decoration: underline;
  text-underline-offset: 2px;
  /* data-m-press (§C.06 Touch): scale lane appended per §B.5. */
  transition: color var(--mo-quick) var(--ease-out),
              scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out));
}

@media (hover: hover){
  .s-offer .s-offer__mail a:hover{ color: var(--text-hi); }
}

.s-offer .s-offer__mail a:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ── Wider than a small phone ─────────────────────────────────────────── */
/* At 360px the card's inner width has to carry one unbroken Tamil sub-line
   ("முழுமையாக தமிழில் · Pre-recorded"), so the side padding is --s-5 there
   and only opens up to --s-6 once there is room. */
@media (min-width: 480px){
  .s-offer .s-offer__card{
    padding-inline: var(--s-6);
  }
}

/* ── Reduced motion ───────────────────────────────────────────────────── */
/* The foundation kills durations globally. Every from-state in this file is
   gated on no-preference, and press lives in css/motion.css (no-preference
   only), so reduced motion needs nothing here: the page is its final state.
   The sticker's static -4deg tilt is layout, not motion, and stays. */

/* ===== 07-ledger ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 7 · WHAT THIS IS / WHAT THIS IS NOT — paper room      root: .s-ledger

   The page inverts here: --paper ground, --paper-ink type, 19.3:1.
   Tokens consumed from §1 only — nothing redefined, no raw hex, no raw px
   for anything §1 covers.

   Colour law 1 is held: there is no --rec-red in this file at all.
   The ✓ is --gold-ink (5.3:1, the only gold permitted on light, §1.16 law 3);
   the ✕ is --paper-dim (4.9:1, token-legal at ≥16px only — every element
   carrying it here is --t-body 17/18px or larger, or is aria-hidden).

   Ground must be fully OPAQUE: the fixed WebGL canvas sits at --z-canvas
   behind the whole page and this section is its hard clip (scene state
   `off`, --z-content). Never make this background translucent.
   ═══════════════════════════════════════════════════════════════════════ */

.s-ledger{
  position: relative;
  z-index: var(--z-content);
  isolation: isolate;                       /* nothing composites through the paper */
  background-color: var(--paper);           /* opaque — this is the canvas clip     */
  color: var(--paper-ink);
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
}

.s-ledger .ledger-inner{
  max-width: var(--maxw);
  margin-inline: auto;
}

/* ── heading ─────────────────────────────────────────────────────────── */

.s-ledger .ledger-h{
  margin: 0 0 var(--s-6);
  max-width: 18ch;                          /* two Bebas lines at every width */
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--paper-ink);
  text-wrap: balance;
}

@media (min-width: 900px){
  .s-ledger .ledger-h{
    margin-bottom: var(--s-7);
    max-width: 24ch;
  }
}

/* ── column headers — desktop only, decorative (aria-hidden in markup) ── */

.s-ledger .ledger-head{
  display: none;
}

@media (min-width: 900px){
  .s-ledger .ledger-head{
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s-6);
    padding-bottom: var(--s-3);
  }

  .s-ledger .ledger-head-col{
    margin: 0;
    font-family: var(--font-body);
    font-weight: 500;
    font-size: var(--t-label);
    line-height: var(--lh-label);
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
    color: var(--paper-stone);              /* 7.3:1 — secondary copy on paper */
  }

  /* the vertical rule runs unbroken from the header through the last row */
  .s-ledger .ledger-head-col--no{
    border-inline-start: 1px solid var(--paper-rule);
    padding-inline-start: var(--s-6);
  }
}

/* ── the rows ────────────────────────────────────────────────────────── */

.s-ledger .ledger-rows{
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Spec asks for 28px row gaps. 28px is not a §1 token, so each row carries
   --s-4 (16px) top and bottom — 32px between adjacent row text blocks across
   the hairline. Nearest token; see return summary. */
.s-ledger .ledger-row{
  padding-block: var(--s-4);
  border-block-start: 1px solid var(--paper-rule);
}

.s-ledger .ledger-row:last-child{
  border-block-end: 1px solid var(--paper-rule);
}

/* ── a cell ──────────────────────────────────────────────────────────── */

.s-ledger .ledger-cell{
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  font-variant-numeric: tabular-nums;       /* §2 — "6", "3" */
}

.s-ledger .ledger-cell--yes{
  color: var(--paper-ink);                  /* 19.3:1 */
}

/* The "what it is not" column is deliberately the quieter voice — it is a
   narrowing, not a boast. --paper-dim is token-sanctioned for the ✕ side and
   is only ever used here at --t-body (17px mobile / 18px ≥768px), never below
   the 16px floor §1.2 sets on it. */
.s-ledger .ledger-cell--no{
  color: var(--paper-dim);                  /* 4.9:1, ≥16px only */
  margin-block-start: var(--s-2);
  margin-inline-start: var(--s-5);          /* mobile indent — reads as a pair */
}

.s-ledger .ledger-text{
  min-width: 0;
  text-wrap: pretty;
}

/* ── the marks — inline SVG only, never an emoji glyph ───────────────── */

.s-ledger .ledger-mark{
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  /* optical centring against the first line of --t-body at --lh-body */
  margin-block-start: var(--s-1);
}

.s-ledger .ledger-mark--yes{
  color: var(--gold-ink);                   /* the ONLY gold allowed on paper */
}

.s-ledger .ledger-mark--no{
  color: var(--paper-dim);
}

/* ── the "no counterpart" placeholder ────────────────────────────────── */

/* On mobile a lone em dash would read as a broken row; the pair is simply
   the ✓ line on its own. The cell returns on desktop to hold the grid. */
.s-ledger .ledger-cell--empty{
  display: none;
}

.s-ledger .ledger-dash{
  color: var(--paper-rule);
  font-size: var(--t-body);
  line-height: var(--lh-body);
}

/* ── screen-reader labels — scoped, the foundation owns no utility here ─ */

.s-ledger .ledger-sr{
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ═══ DESKTOP ≥900px — two columns inside --maxw ══════════════════════ */

@media (min-width: 900px){

  .s-ledger .ledger-row{
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s-6);
    align-items: start;
  }

  .s-ledger .ledger-cell--no{
    margin-block-start: 0;
    margin-inline-start: 0;
    border-inline-start: 1px solid var(--paper-rule);
    padding-inline-start: var(--s-6);
    /* the rule must span the row's full padded height, not just the text */
    align-self: stretch;
    margin-block: calc(-1 * var(--s-4));
    padding-block: var(--s-4);
  }

  .s-ledger .ledger-cell--empty{
    display: flex;
  }
}

/* ═══ WIDE ≥1200px — a little more air between the two arguments ══════ */

@media (min-width: 1200px){
  .s-ledger .ledger-row{ column-gap: var(--s-7); }
  .s-ledger .ledger-cell--no{ padding-inline-start: var(--s-7); }
  .s-ledger .ledger-head{ column-gap: var(--s-7); }
  .s-ledger .ledger-head-col--no{ padding-inline-start: var(--s-7); }
}

/* No transitions, no hover states, no infinite loops live in this section.
   Its motion (design/mobile-motion.md §C.07): the heading fades, the rows
   resolve one by one as each reaches the trigger line (a fade group, 50ms
   batch stagger; the marks arrive with their row, amendment 27), and the
   lights lift off the paper as it enters (below). */

/* ── "Lights up" — scroll-linked, decorative ─────────────────────────────
   The dark edge of the previous room lifts off the paper as it enters: one
   aria-hidden span, first child of the section, exactly the section's top
   padding tall (amendment 28), so it can never sit over .ledger-h. Its
   opacity is scrubbed 1 -> 0 across its own view() entry by css/motion.css
   ([data-m-scrub="lights"], compositor-only). The REST state is opacity 0,
   declared here too, so with no JS, no motion.css, reduced motion, or no
   scroll-driven animation support it is simply not there. */
.s-ledger .ledger-lights{
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 0;
  height: var(--section-y);
  background: linear-gradient(to bottom, var(--ink-void), transparent);
  opacity: 0;
  pointer-events: none;
}

/* ===== 08-instructor ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 8 · THE INSTRUCTOR — "the face"          root: .s-teacher
   Every value below is a §1 token. Nothing redefined, nothing hardcoded.

   Ground is deliberately TRANSPARENT. §8 says "Back on --ink-void" — the page
   body already paints --ink-void, so painting it again here would be the same
   colour while hard-clipping the particle field the section explicitly asks to
   have behind the portrait ("the particle field behind it… The 3D is the frame;
   the face is the argument"). Text contrast over the live canvas is held two
   ways, both required by §7: the canvas lerps to uOpacity 0.12 behind this
   section (declared as data-scene-opacity), and every text block sits on an
   explicit scrim — --scrim-v on mobile, --scrim-h on desktop.
   ═══════════════════════════════════════════════════════════════════════ */

.s-teacher{
  position: relative;
  z-index: var(--z-content);
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
  background: transparent;
}

.s-teacher .teacher__inner{
  max-width: var(--maxw);
  margin-inline: auto;
  display: grid;
  gap: var(--s-7);
}

/* ── the asymmetric split (§8) ────────────────────────────────────────────
   Mobile: portrait full-width first, copy under — source order already is
   that order, so nothing is reordered and the tab order matches the visual
   order (§7, Keyboard Navigation).
   Desktop ≥900px: portrait 5/12 left · 1/12 gutter · copy 6/12 right. */

@media (min-width: 900px){
  .s-teacher .teacher__inner{
    grid-template-columns: 5fr 1fr 6fr;
    column-gap: 0;
    row-gap: 0;
    align-items: center;
  }
  .s-teacher .teacher__figure{ grid-column: 1; }
  .s-teacher .teacher__body{   grid-column: 3; }
}

/* ══ THE CREATOR AND HIS CREATIONS ═══════════════════════════════════════════
   The portrait is already graded in the image itself (build/portrait/): full
   colour, night bokeh, --rec-red / --spot-gold rim light. The old CSS duotone is
   gone — it flattened exactly the colour that now carries the brand.

   Geometry. The stage insets the frame (--pad-l / --pad-r) so both reel cards can
   overhang the photograph WITHOUT leaving the column: at 375px nothing crosses
   the gutter, so there is no horizontal scroll. Padding and `left` percentages
   both resolve against the figure's width, so the handle's centre maths holds. */

.s-teacher .teacher__figure{
  margin: 0;
  min-width: 0;
}

.s-teacher .teacher__stage{
  --pad-l: 10%;
  --pad-r: 14%;
  position: relative;
  max-width: 34rem;
  margin-inline: auto;
  padding: var(--s-5) var(--pad-r) var(--s-6) var(--pad-l);   /* bottom: room for the handle */
}

.s-teacher .teacher__frame{
  position: relative;
  overflow: hidden;                          /* fallback for browsers without overflow:clip */
  overflow: clip;                            /* clip, not hidden: never a scroll container (mobile-motion B.3) */
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--e4);
  background-color: var(--ink-deep);        /* shows only until the image decodes */
}

.s-teacher .teacher__photo{
  display: block;
  width: 100%;
  height: auto;
}

/* ── the reel cards: two of his real AI videos, tilted like prints on a desk ── */
.s-teacher .teacher__reel{
  --tilt: 0deg;
  position: absolute;
  z-index: var(--z-raised);
  display: block;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  border: 2px solid var(--text-hi);
  border-radius: var(--r-md);
  box-shadow: var(--e4);
  background-color: var(--ink-deep);
  color: var(--text-hi);
  /* LANES (mobile-motion B.2): transform = the pop reveal, rotate = this resting
     tilt, scale = press (data-m-press). Never declare transform here. The tilt
     pivots on --m-origin — the corner that touches the frame — in every mode. */
  rotate: var(--tilt);
  transition: rotate var(--mo-base) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.s-teacher .teacher__reel--a{ --tilt:  7deg; top: 9%;    right: 1%; width: 28%; }
.s-teacher .teacher__reel--b{ --tilt: -6deg; bottom: 19%; left: 0;   width: 25%; }

.s-teacher .teacher__reel-img{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.s-teacher .teacher__reel-play{
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r-pill);
  background-color: var(--ink-void);
  opacity: .82;
}

.s-teacher .teacher__reel-tag{
  position: absolute;
  right: var(--s-2);
  bottom: var(--s-2);
  padding: 2px var(--s-2);
  border-radius: var(--r-pill);
  /* translucent GROUND, opaque TEXT: element opacity would dim the label too */
  background-color: var(--ink-void);
  background-color: color-mix(in srgb, var(--ink-void) 86%, transparent);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 10px;
  line-height: 1.4;
  letter-spacing: var(--tr-label);
}

@media (hover: hover){
  .s-teacher .teacher__reel:hover{ rotate: 0deg; }    /* straightens under the pointer */
}
.s-teacher .teacher__reel:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 3px;
}

/* ── the handle: sits across the frame's bottom edge, centred on the frame ── */
.s-teacher .teacher__handle{
  position: absolute;
  z-index: var(--z-raised);
  left: calc(var(--pad-l) + (100% - var(--pad-l) - var(--pad-r)) / 2);
  bottom: var(--s-6);
  translate: -50% 50%;                       /* NOT transform: the rise reveal owns transform */
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;                          /* touch target */
  padding: 0 var(--s-4);
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-pill);
  background-color: var(--ink-panel);
  box-shadow: var(--e3);
  color: var(--text-hi);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--t-micro);
  letter-spacing: .2px;
  white-space: nowrap;
  text-decoration: none;
  transition: border-color var(--mo-quick) var(--ease-out);
}
.s-teacher .teacher__handle-icon{ flex: none; color: var(--spot-gold); }
@media (hover: hover){
  .s-teacher .teacher__handle:hover{ border-color: var(--spot-gold); }
}
.s-teacher .teacher__handle:focus-visible{ outline: 2px solid var(--ring); outline-offset: 3px; }


/* ══ THE COPY ═════════════════════════════════════════════════════════════ */

.s-teacher .teacher__body{
  position: relative;
  min-width: 0;
}

/* The explicit scrim under the text (§7). It bleeds out to the page gutter so
   the gradient never ends inside the measure. --scrim-v on mobile. */
.s-teacher .teacher__body::before{
  content: "";
  position: absolute;
  inset: calc(var(--s-6) * -1) calc(var(--gutter) * -1);
  z-index: -1;
  background-image: var(--scrim-v);
  pointer-events: none;
}

/* Desktop: the copy is the RIGHT column, so --scrim-h (which runs dark→clear
   left to right, authored for §1's left-hand text column) is mirrored rather
   than re-specified. Mirroring consumes the token instead of inventing a
   second gradient. */
@media (min-width: 900px){
  .s-teacher .teacher__body::before{
    inset: calc(var(--s-7) * -1) calc(var(--gutter) * -1);
    background-image: var(--scrim-h);
    transform: scaleX(-1);
  }
}

.s-teacher .teacher__eyebrow{
  margin: 0 0 var(--s-3);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-low);
}

.s-teacher .teacher__h2{
  margin: 0 0 var(--s-5);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--text-hi);
  text-wrap: balance;
}

@media (min-width: 900px){
  .s-teacher .teacher__h2{ margin-bottom: var(--s-6); }
}

.s-teacher .teacher__copy{
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text-mid);
}

.s-teacher .teacher__copy + .teacher__copy{
  margin-top: var(--s-4);
}

/* ══ THE FOUR NUMBERS, INLINE AND SMALL (§8, §9.4) ════════════════════════
   The hero strip sets them large with 900m+ largest; here they are a single
   quiet line beside the story. --text-low is the floor for real copy (7.5:1);
   the figures are --spot-gold, the evidence colour (colour law 2). */

.s-teacher .teacher__proof{
  list-style: none;
  margin: var(--s-6) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  /* ≥8px between adjacent targets (ux: Touch Spacing) — --s-3 is 12px. */
  column-gap: var(--s-3);
  row-gap: var(--s-2);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--text-low);
}

@media (min-width: 900px){
  .s-teacher .teacher__proof{ margin-top: var(--s-7); }
}

.s-teacher .teacher__proof-item{
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  min-width: 0;
}

/* The interpuncts between the four. Decorative: the `/ ""` alternative-text
   form keeps them out of the accessibility tree where it is supported, and
   they are drawn on the item, never typed into the copy. */
.s-teacher .teacher__proof-item + .teacher__proof-item::before{
  content: "\00B7" / "";
  margin-right: var(--s-1);
  color: var(--text-faint);   /* §1.3 — decorative only, and this is one */
}

.s-teacher .teacher__proof-num{
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-price-mid);
  line-height: var(--lh-num);
  letter-spacing: var(--tr-num);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  text-transform: uppercase;
  color: var(--proof-solid);              /* .u-proof adds the aurora */
}

.s-teacher .teacher__proof-label{
  color: var(--text-low);
}

/* The two numbers that have a public destination are links — the old site had
   none, which made its whole credibility claim unverifiable from the page. */
.s-teacher .teacher__proof-link{
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  /* WCAG Target Size (web): ≥24 CSS px of tappable height. */
  min-height: 24px;
  padding-block: var(--s-1);
  color: inherit;
  text-decoration: none;
  transition: color var(--mo-quick) var(--ease-out);
}

@media (hover: hover){
  .s-teacher .teacher__proof-link:hover{ color: var(--text-hi); }
  .s-teacher .teacher__proof-link:hover .teacher__proof-label{ color: var(--text-hi); }
}


.s-teacher .teacher__proof-icon{
  flex: none;
  /* baseline-aligned inside a flex row: nudge the optical centre onto the
     text baseline without a magic offset. */
  align-self: center;
  color: var(--text-low);
}

@media (hover: hover){
  .s-teacher .teacher__proof-link:hover .teacher__proof-icon{ color: var(--text-hi); }
}

/* ===== 09-faq ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 9 · FAQ — "still thinking?"                       root: .s-faq
   Paper room #2 (spec §1.2 / §7 "sunlight mitigation" / §9).

   Colour discipline: this section carries .room-paper, which REBINDS the ink
   tokens to their paper equivalents in css/base.css. That is why nothing here
   needs to reach for a raw hex, and why colour law §1.16.3 (--spot-gold is
   forbidden on light) cannot be broken from inside this file — --spot-gold
   already resolves to --gold-ink here. Where spec §9 names --gold-ink and
   --paper-* explicitly, this file names them explicitly too.

   Every value below is a §1 custom property, with exactly three documented
   exceptions, all of them numbers the spec itself states:
     · min-height: 56px      — spec §9 states this literally for <summary>.
     · 2px marker stroke     — the line weight spec §7 mandates for the focus
                               ring ("outline: 2px solid var(--ring)"). The
                               nearest spacing token, --s-1 (4px), is a slab
                               at 24px and reads as a fat plus, not a hairline.
     · 45deg marker rotation — the geometry that turns + into ×, per spec §9.
   ═══════════════════════════════════════════════════════════════════════ */

.s-faq{
  /* .room-paper supplies: background --paper, colour --paper-ink,
     position: relative, z-index: var(--z-content), isolation: isolate.
     The opaque ground is what hard-clips the canvas (scene state `off`). */
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
}

.s-faq .s-faq__inner{
  max-width: var(--maxw);
  margin-inline: auto;
}


/* ── heading ─────────────────────────────────────────────────────────────
   Bebas on paper. §9: "Answers in --paper-ink". The heading is an answer's
   worth of weight, so it takes --paper-ink too, not --paper-stone. */

.s-faq .s-faq__h2{
  margin: 0 0 var(--s-6);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--paper-ink);
}

@media (min-width: 768px){
  .s-faq .s-faq__h2{ margin-bottom: var(--s-7); }
}


/* ── the list ────────────────────────────────────────────────────────────
   §9: "Rows separated by 1px --paper-rule." The list also takes a rule top
   and bottom, which is the same ledger hairline language §7 uses — the two
   paper rooms must read as the same room seen twice. */

.s-faq .s-faq__list{
  border-top: 1px solid var(--paper-rule);
}

.s-faq .s-faq__item{
  border-bottom: 1px solid var(--paper-rule);
}


/* ── <summary> — the tappable row ────────────────────────────────────────
   ux: Compact Control Semantics (Critical) — native disclosure, zero
   clickable divs. list-style is removed on both engines and the marker is
   drawn as pseudo-elements on .s-faq__marker (spec §9).

   Grid: the answer and its eyebrow occupy column 1; the marker holds
   column 2 across both rows so it stays optically centred on the row at
   every width, including when the question wraps to three lines at 360px. */

.s-faq .s-faq__summary{
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--s-4);
  row-gap: var(--s-2);
  /* ux: Touch Target Size — spec §9 states 56px for this control. */
  min-height: 56px;
  padding-block: var(--s-5);
  cursor: pointer;
  list-style: none;
  -webkit-tap-highlight-color: transparent;
}

/* Safari / older WebKit still paint the default triangle without this. */
.s-faq .s-faq__summary::-webkit-details-marker{ display: none; }

/* The eyebrow: the verbatim question (spec §10.5). Rendered in its source
   case — these are quoted sentences, not labels, and an all-caps sentence is
   a rendering change to verbatim copy. Size and tracking stay on the label
   tokens the spec names. */
.s-faq .s-faq__q{
  grid-column: 1;
  grid-row: 1;
  /* No --measure cap: at 12px the longest question is ~55 characters, which
     sits on one line from 768px up. Capping it would force a pointless
     two-line wrap in a 1200px column. Below 768px the grid column caps it. */
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  /* DEVIATION, declared: spec §9 asks for --paper-dim here, but §1.2 marks
     --paper-dim "4.9:1, >=16px only" and --t-label is 12px. Holding the 7:1
     floor §7 says "is the number we hold", this takes the nearest token that
     is legal at 12px: --paper-stone, 7.3:1. Size is unchanged. */
  color: var(--paper-stone);
}

/* The answer — the whole point of the inversion. A scanning thumb that never
   taps still gets PRE-RECORDED / NO. ALL FREE / UNDER 3 HOURS / BUILT FOR
   ZERO, which is all four objections dead in four glances. */
.s-faq .s-faq__a{
  grid-column: 1;
  grid-row: 2;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-answer);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  /* §2: tabular figures on every numeral — "UNDER 3 HOURS." */
  font-variant-numeric: tabular-nums;
  color: var(--paper-ink);
  text-wrap: balance;
  /* Declared here, not on :hover, so it never outranks css/motion.css's
     shared reveal transition while this answer is armed (§B.5). */
  transition: color var(--mo-quick) var(--ease-out);
}


/* ── the marker — a --gold-ink + rotating to × over --mo-quick (spec §9) ──
   Drawn as two pseudo-element bars, not a glyph and not an icon font, so it
   is unaffected by font loading and costs zero bytes. Rotating BOTH bars 45°
   about the same centre turns + into ×; transform only (§7 "Banned outright:
   anything animating width/height/top/left"). */

.s-faq .s-faq__marker{
  grid-column: 2;
  grid-row: 1 / span 2;
  position: relative;
  width: var(--s-5);
  height: var(--s-5);
  align-self: center;
  justify-self: end;
}

.s-faq .s-faq__marker::before,
.s-faq .s-faq__marker::after{
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background-color: var(--gold-ink);
  transition: transform var(--mo-quick) var(--ease-out),
              background-color var(--mo-quick) var(--ease-out);
}

/* horizontal bar */
.s-faq .s-faq__marker::before{
  width: var(--s-5);
  height: 2px;
  transform: translate(-50%, -50%) rotate(0deg);
}

/* vertical bar */
.s-faq .s-faq__marker::after{
  width: 2px;
  height: var(--s-5);
  transform: translate(-50%, -50%) rotate(0deg);
}

/* open: + → × */
.s-faq .s-faq__item[open] > .s-faq__summary > .s-faq__marker::before{
  transform: translate(-50%, -50%) rotate(45deg);
}
.s-faq .s-faq__item[open] > .s-faq__summary > .s-faq__marker::after{
  transform: translate(-50%, -50%) rotate(45deg);
}


/* ── the disclosed body — the verbatim answer ────────────────────────────
   §9: "bodies in --paper-stone at --t-body". Capped at --measure so the
   paragraph never runs the full 1200px column at desktop. */

.s-faq .s-faq__body{
  padding-bottom: var(--s-5);
  /* keeps the answer visually attached to its own row, not the next one */
  padding-inline-end: calc(var(--s-5) + var(--s-4));
}

.s-faq .s-faq__body > p{
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--paper-stone);
  font-variant-numeric: tabular-nums;
}

/* §8 motion grammar — "things resolve into focus; nothing slides in from
   off-screen". One element, user-initiated, opacity + a 12px settle.
   The global prefers-reduced-motion block in base.css collapses this to
   0.01ms, so the body simply appears. */
@keyframes s-faq-resolve{
  from{ opacity: 0; transform: translateY(var(--s-3)); }
  to  { opacity: 1; transform: translateY(0); }
}

.s-faq .s-faq__item[open] > .s-faq__body{
  animation: s-faq-resolve var(--mo-base) var(--ease-out) both;
}


/* ── hover / press ───────────────────────────────────────────────────────
   Hover is gated behind (hover: hover) so it never sticks on a phone.
   §8.6 Press — design/mobile-motion.md §C.09 puts data-m-press on the
   summary, so the whole row takes the page's scale(0.97) press on the
   `scale` lane (css/motion.css), released on the spring. The marker no
   longer presses on its own: that would double it. */

@media (hover: hover){
  .s-faq .s-faq__summary:hover .s-faq__a{
    color: var(--paper-stone);
  }
  .s-faq .s-faq__summary:hover .s-faq__marker::before,
  .s-faq .s-faq__summary:hover .s-faq__marker::after{
    background-color: var(--paper-ink);
  }
}

/* The global :focus-visible ring (2px solid var(--ring), which .room-paper
   rebinds to --paper-ink) applies to <summary> unchanged. It is declared
   here only to give the full-bleed row a radius that matches the page's
   card language instead of a hard rectangle. */
.s-faq .s-faq__summary:focus-visible{
  border-radius: var(--r-md);
}


/* ── breakpoints ─────────────────────────────────────────────────────────
   360px  : single column, question wraps to 2–3 lines, answer at 30px, the
            24px marker sits hard right inside the --gutter (20px floor).
   768px  : --t-body steps to 18px from the foundation; row padding opens up.
   1024px : the eyebrow and answer sit on one optical line with the marker
            far right — the editorial-ledger read the paper rooms share.
   1440px : capped by --maxw; the body paragraph is capped by --measure so
            the column never becomes a 1200px-wide line of text. */

@media (min-width: 768px){
  .s-faq .s-faq__summary{
    column-gap: var(--s-5);
    padding-block: var(--s-6);
  }
  .s-faq .s-faq__body{
    padding-bottom: var(--s-6);
    padding-inline-end: calc(var(--s-6) + var(--s-5));
  }
}

@media (min-width: 1024px){
  .s-faq .s-faq__marker{
    width: var(--s-6);
    height: var(--s-6);
  }
  .s-faq .s-faq__marker::before{ width: var(--s-6); }
  .s-faq .s-faq__marker::after{  height: var(--s-6); }
}

/* ===== 10-final ===== */
/* ════════════════════════════════════════════════════════════════════════
   § 10 · FINAL CTA — "the cut"
   Root class: .s-final          Scene state: bloom (§5.7)
   Spec: art-direction.md §4 "§ 10 · FINAL CTA", §5.11 (the shared WebP),
         §9.2 CTA surface 6, §1.16 colour laws.

   Consumes tokens from §1 only; defines none. Styles nothing outside
   .s-final. No initial hidden state is written here — the section is fully
   readable with JavaScript disabled and the runtime owns every from-state.
   ═════════════════════════════════════════════════════════════════════ */

/* ── The room ─────────────────────────────────────────────────────────── */
/* Full-bleed, back to --ink-void after §9's paper room. The ground colour
   lives on the plate below, NOT on this element: on CINEMA the plate fades
   to 0 and the live canvas — whose clear colour is 0x050507, i.e. exactly
   --ink-void (§5.2) — becomes the ground. Either way the room is ink-void.

   100dvh (not svh — the hero is the single documented svh exception, §1)
   with padding-block as the floor, so the copy is never clipped on a short
   landscape viewport; min-height only ever grows. */
.s-final{
  position: relative;
  z-index: var(--z-content);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
  /* design/mobile-motion.md finding 13 / §C.10: `clip`, not `hidden`. hidden
     makes this a scroll container, which kills the view() timeline the
     plate's dolly runs on. clip crops the 1.10 overscan identically.
     `hidden` stays first as the fallback for engines without clip. */
  overflow: hidden;
  overflow: clip;
  text-align: center;
}

/* ── Layer 0 · the plate (§5.11 — one download, two jobs) ─────────────── */
/* The same file the STILLS hero uses. Relative to css/sections.css.
   Declared here rather than as an <img> because <head> already preloads it:
   this repaints from cache, adds no request, and reserves no layout box, so
   it contributes 0 CLS by construction. */
.s-final .s-final__still{
  position: absolute;
  inset: 0;
  z-index: var(--z-canvas);
  pointer-events: none;
  background-color: var(--ink-void);
  background-image: url("../assets/hero-still.webp");
  background-size: cover;
  background-position: 50% 30%;
  background-repeat: no-repeat;
  transition: opacity var(--mo-scene) var(--ease-soft);
}

/* "Heavily darkened by --scrim-v" (§4 §10). --scrim-v alone is .95 ink at the
   bottom but only .10 at the top, which would leave the headline sitting on an
   undarkened frame. A second --scrim-v, flipped, darkens the top by the same
   token. No new value is introduced — the token is composed with itself.
   It lives INSIDE the plate so it fades out with the plate on CINEMA. */
.s-final .s-final__still::after{
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--scrim-v);
  transform: scaleY(-1);
}

/* CINEMA is the only tier that still owns a canvas by this point in the page
   (§9: the renderer is destroyed on STANDARD once §7's paper ground lands), so
   it is the only tier where the plate stands down to reveal the `bloom` state.
   With JS off, on STILLS, and after swapToStatic() sets data-tier="stills",
   the plate is visible — which is the whole reason the WebP had to be the same
   composition as the scene. */
[data-tier="cinema"] .s-final .s-final__still{
  opacity: 0;
}

/* ── Layer 1 · the text scrim (§7) ────────────────────────────────────── */
/* Present on every tier. On CINEMA the bloom explodes past the camera at
   uOpacity 1.0 directly behind this copy, and §7 requires text over the live
   canvas to sit on an explicit scrim verified against the brightest frame —
   not a dim screenshot. --scrim-v is the mobile/centred-column scrim and this
   section is centred at every breakpoint, so it is also the desktop scrim. */
.s-final .s-final__scrim{
  position: absolute;
  inset: 0;
  z-index: var(--z-scrim);
  pointer-events: none;
  background-image: var(--scrim-v);
}

/* ── The content column ───────────────────────────────────────────────── */
.s-final .s-final__inner{
  position: relative;
  z-index: var(--z-content);
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
}

/* ── The headline ─────────────────────────────────────────────────────── */
/* Bebas --t-hero, centred, two hard-stopped lines. All-caps Latin only —
   Bebas has no lowercase and no Tamil (§2), and nothing here needs either. */
.s-final .s-final__headline{
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-hero);
  line-height: var(--lh-hero);
  letter-spacing: var(--tr-display);
  color: var(--text-hi);                  /* ~18.9:1 — §7 contrast table     */
}

/* Hard stops, not wraps: the two lines are the rhyme. display:block also gives
   the runtime a box for its y:12px reveal transform. */
.s-final .s-final__line{
  display: block;
}

/* ── CTA surface 6 (§9.2) — 64px, --grad-cta, data-cta="final" ────────── */
/* --s-8 IS 64px. Deliberately no box-shadow: colour law 5 permits exactly two
   glowing DOM elements on the page (the hero CTA and the §6 price numeral),
   and this is neither. */
.s-final .s-final__cta{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  width: 100%;                            /* full-width at 360px             */
  min-height: var(--s-8);
  margin-top: var(--s-7);
  padding: var(--s-3) var(--s-7);
  border: 0;
  border-radius: var(--r-pill);
  background-image: var(--grad-cta);
  background-color: var(--rec-red);
  color: var(--on-rec);                   /* 4.70:1 at >=16px/600 — §7       */
  font-family: var(--font-body);
  font-size: var(--t-btn);
  font-weight: 600;
  letter-spacing: var(--tr-btn);
  line-height: var(--lh-label);
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  font-variant-numeric: tabular-nums;     /* §2 — every numeral              */
  /* Press is the `scale` lane (mobile-motion.md §B.2 / amendment 18). While
     the CTA is armed for its `rise`, css/motion.css's shared list owns
     `transition`; this list applies before arming and after data-m-done. */
  transition:
    background-color var(--mo-quick) var(--ease-out),
    scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out));
}

@media (min-width: 900px){
  .s-final .s-final__cta{
    width: auto;
    min-width: 340px;
  }
}

@media (hover: hover){
  .s-final .s-final__cta:hover{
    background-image: none;
    background-color: var(--rec-red-hot);
  }
}

.s-final .s-final__cta:active{
  background-image: none;
  background-color: var(--rec-red-deep);
  /* §8 item 6 Press: scale 0.97 comes from css/motion.css (lane `scale`). */
}

.s-final .s-final__cta:focus-visible{
  outline: 2px solid var(--ring);         /* §7 Focus Appearance             */
  outline-offset: 2px;
  border-radius: var(--r-pill);
}

/* ── The microline ────────────────────────────────────────────────────── */
/* Price · length · time · language · tool cost. Every one of these is a
   restatement of a verbatim source fact carried down from §4 and §6, which is
   what judge concern 36 requires of a loud claim: it shares its viewport with
   the evidence. Wraps to two lines at 360px and that is intended. */
.s-final .s-final__microline{
  margin: var(--s-5) auto 0;
  max-width: var(--measure);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 400;
  line-height: var(--lh-body);
  color: var(--text-low);                 /* ~7.5:1 — the floor for copy     */
  font-variant-numeric: tabular-nums;
  text-wrap: balance;
}

/* Colour law 2 — gold is the evidence colour, and the price is evidence.
   No --glow-price here; that token belongs to the §6 numeral alone (law 5). */
.s-final .s-final__price{
  color: var(--spot-gold);                /* ~10.3:1                         */
  font-weight: 600;
}

/* ── Razorpay trust mark ──────────────────────────────────────────────── */
/* The same bordered chip as the offer card, so the mark reads as one object
   seen twice rather than two different treatments. No red, no gold — the scam
   refutation is the name itself. */
.s-final .s-final__trust{
  margin: var(--s-4) 0 0;
}

.s-final .s-final__chip{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: var(--lh-label);
  color: var(--text-low);
}

.s-final .s-final__lock{
  display: block;
  flex: none;
  color: var(--text-low);
}

.s-final .s-final__mark{
  color: var(--text-hi);
  font-weight: 600;
  letter-spacing: var(--tr-h3);
}

/* ── Desktop ──────────────────────────────────────────────────────────── */
/* The composition does not change shape at any breakpoint — "the cut" is a
   centred title card at 360, 768, 1024 and 1440 alike. Only the type scale
   and the CTA width move, and both are already handled by clamp() and the
   900px rule above. The plate is re-weighted because the WebP is a 3:4
   portrait crop and a wide viewport otherwise pushes the subject off-frame. */
@media (min-width: 900px){
  .s-final .s-final__still{
    background-position: 50% 45%;
  }
  .s-final .s-final__microline{
    font-size: var(--t-body);
  }
}

/* ── Reduced motion ───────────────────────────────────────────────────── */
/* The foundation owns the global duration kill. Press and the plate's dolly
   live in css/motion.css behind no-preference, so they never run here. The
   plate carries no drift loop — §7 caps the page at one infinite decorative
   loop and the hero's still already spends it. */
@media (prefers-reduced-motion: reduce){
  .s-final .s-final__still{ transition: none; }
}

/* ===== 11-contact ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 11 · CONTACT                                    root: .s-contact
   Tokens consumed from §1 only. Nothing redefined, nothing invented.

   The two literal numbers in this file are both given verbatim by spec §11:
   the 52px control height and the 17px text size (= --t-body). Everything
   else — every colour, space, radius, shadow, duration and easing — is a §1
   custom property.

   COLOUR: no --rec-red / --grad-cta anywhere in this section. Colour law 1
   reserves red for the seven checkout CTAs, and on mobile the sticky bottom
   bar's JOIN NOW pill is on screen while this form is — a second red here
   would mean two reds on one screen. The Submit control is therefore an
   --edge-strong bordered button on --ink-raised, and the error signal is
   --spot-gold (see the note above .contact-error).
   ═══════════════════════════════════════════════════════════════════════ */

.s-contact{
  position: relative;
  z-index: var(--z-content);
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
  background: var(--ink-void);
  border-top: 1px solid var(--edge);   /* separates this band from §10's full-bleed */

  /* Local (not a §1 token, not a redefinition): the inline error glyph, drawn as an
     SVG mask so its colour stays a token instead of a hardcoded fill. */
  --contact-err-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="%23000" d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 3.1a.95.95 0 01.95 1l-.22 3.6a.73.73 0 01-1.46 0l-.22-3.6A.95.95 0 018 4.1zm0 6.3a1.05 1.05 0 110 2.1 1.05 1.05 0 010-2.1z"/></svg>');
}

.s-contact .contact-inner{
  max-width: var(--maxw);
  margin-inline: auto;
}

/* Desktop: asymmetric 5/12 · 7/12 split — the same split language §8 uses, so the
   page does not acquire a new grid in its last content section. */
@media (min-width: 900px){
  .s-contact .contact-inner{
    display: grid;
    grid-template-columns: 5fr 7fr;
    gap: var(--s-8);
    align-items: start;
  }
}


/* ── intro column ─────────────────────────────────────────────────────── */

.s-contact .contact-h{
  margin: 0 0 var(--s-5);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
  color: var(--text-hi);
}

.s-contact .contact-direct{
  margin: 0 0 var(--s-7);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
  font-size: var(--t-body);
  line-height: var(--lh-body);
}

@media (min-width: 900px){
  .s-contact .contact-direct{ margin-bottom: 0; }
}

.s-contact .contact-direct__q{ color: var(--text-low); }

/* The shortcut most people will actually tap. >=48px tall, >=24 CSS px of
   tappable inline text (ux: Touch Target Size). */
.s-contact .contact-mail{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 48px;
  padding-block: var(--s-2);
  padding-inline: 0;
  color: var(--text-hi);
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
  text-decoration: underline;
  text-decoration-color: var(--edge-strong);
  text-underline-offset: var(--s-1);
  overflow-wrap: anywhere;
  transition: color var(--mo-quick) var(--ease-out),
              text-decoration-color var(--mo-quick) var(--ease-out);
}

@media (hover: hover){
  .s-contact .contact-mail:hover{
    color: var(--text-hi);
    text-decoration-color: var(--spot-gold);
  }
}

/* Press: data-m-press in the markup puts it on the motion.css scale lane (spring, not snap). */

.s-contact .contact-mail__icon{
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--text-low);
}


/* ── form shell ───────────────────────────────────────────────────────── */

.s-contact .contact-form{
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

.s-contact .contact-row{
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

/* Paired fields go two-up from 768px. Below that they stack — at 360px a
   half-width name field would be ~150px, which is not a usable input. */
@media (min-width: 768px){
  .s-contact .contact-row--pair{
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4);
  }
}

.s-contact .contact-field{
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-width: 0;
}

/* design/mobile-motion.md §C.11 — focus ink line. A 2px --text-hi rule that
   draws across the input's bottom edge (scaleX 0 -> 1, left to right) while
   the field has focus. It is decoration shown on focus, not a from-state, so
   it needs no gate; the global reduced-motion rule makes it simply appear.
   Geometry: `order: 1` seats it straight after the input (the error line is
   `order: 2`), and the negative top margin (gap + its own 2px) lays it ON the
   input's bottom border, so the field's height and the error's position are
   exactly what they were without it (ux #19: zero layout shift). Inset by
   --r-sm so it never overhangs the rounded corners. */
.s-contact .contact-field::after{
  content: "";
  order: 1;
  display: block;
  height: 2px;
  margin: calc(-1 * var(--s-2) - 2px) var(--r-sm) 0;
  background: var(--text-hi);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--mo-quick) var(--ease-out);
  pointer-events: none;
}
.s-contact .contact-field:focus-within::after{ transform: scaleX(1); }


/* ── label ────────────────────────────────────────────────────────────── */
/* Visible label above the input — never a placeholder standing in for one. */

.s-contact .contact-label{
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-mid);   /* ~11.5:1 — above the --text-low floor */
}

/* The required marker. Not red (colour law 1) and not gold (colour law 2
   reserves gold for evidence); --text-low is 7.5:1 and is the correct
   weight for a glyph that only ever accompanies a visible word. */
.s-contact .contact-req{
  margin-left: var(--s-1);
  color: var(--text-low);
}


/* ── input ────────────────────────────────────────────────────────────── */
/* §11 verbatim: --ink-raised, 1px --edge, 52px tall, --r-sm, 17px text.
   17px also keeps iOS from zooming the viewport on focus. */

.s-contact .contact-input{
  width: 100%;
  min-height: 52px;
  padding: var(--s-3) var(--s-4);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 17px;
  line-height: var(--lh-body);
  letter-spacing: normal;
  color: var(--text-hi);
  background: var(--ink-raised);
  border: 1px solid var(--edge);
  border-radius: var(--r-sm);
  appearance: none;
  transition: border-color var(--mo-quick) var(--ease-out),
              background-color var(--mo-quick) var(--ease-out);
}

.s-contact .contact-input::placeholder{ color: var(--text-faint); }

@media (hover: hover){
  .s-contact .contact-input:hover{ border-color: var(--edge-strong); }
}

/* Focus — §11 verbatim, and base.css already ships the same rule globally.
   Restated here with the field's own radius so the ring traces the control. */
.s-contact .contact-input:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-color: var(--edge-strong);
  border-radius: var(--r-sm);
}

.s-contact .contact-input--area{
  min-height: calc(var(--s-10) + var(--s-5));   /* 128 + 24 = 152px */
  resize: vertical;
}

/* --tr-num tabular figures on the phone field; base.css already targets
   input[type="tel"], this only adds the tracking the token defines. */
.s-contact .contact-input--tel{ letter-spacing: var(--tr-num); }


/* ── inline error, BELOW the input (ux: Forms / Error Placement) ───────── */
/* §11: "Never a red border alone, never a summary-only error." Both halves
   ship: a message under the field AND a border change, and neither is red.
   TOKEN APPROXIMATION — colour law 1 forbids --rec-red outside a checkout
   CTA and colour law 2 calls gold the evidence colour. An error still needs
   a distinct signal, so this uses --spot-gold (10.3:1 on ink) for the text
   and --spot-gold-dim for the invalid border: the nearest tokens that read
   as "attention" without putting a second red on the screen. */

.s-contact .contact-error{
  order: 2;                 /* after the focus ink line, see .contact-field::after */
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  margin: 0;
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--spot-gold);
}

/* Empty until the runtime writes a message into it. Collapsed with the sr-only
   pattern rather than `display: none` — an aria-live region that is display:none
   is removed from the accessibility tree, and some screen readers then miss the
   first message written into it. This keeps the region live and costs no layout
   (it is out of flow, so the ::before icon leaves no footprint either). */
.s-contact .contact-error:empty{
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.s-contact .contact-error::before{
  content: "";
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: var(--s-1);
  background-color: currentColor;
  -webkit-mask-image: var(--contact-err-icon);
          mask-image: var(--contact-err-icon);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}

.s-contact .contact-input[aria-invalid="true"]{
  border-color: var(--spot-gold-dim);
}

.s-contact .contact-input[aria-invalid="true"]:focus-visible{
  border-color: var(--spot-gold);
}


/* ── submit ───────────────────────────────────────────────────────────── */

.s-contact .contact-submit-row{
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  margin-top: var(--s-2);
}

@media (min-width: 768px){
  .s-contact .contact-submit-row{
    flex-direction: row;
    align-items: center;
    gap: var(--s-4);
  }
}

.s-contact .contact-submit{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  width: 100%;
  min-height: 52px;
  padding-inline: var(--s-6);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--t-btn);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-btn);
  text-transform: uppercase;
  color: var(--text-hi);
  background: var(--ink-raised);
  border: 1px solid var(--edge-strong);
  border-radius: var(--r-pill);
  box-shadow: var(--e1);
  cursor: pointer;
  /* data-m-press (§C.11): press is the `scale` lane, never `transform`
     (mobile-motion.md §B.2 / §B.5). With JS the engine's press rule owns this
     list; its trailing colour transitions cover border and background. */
  transition: scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out)),
              border-color var(--mo-quick) var(--ease-out),
              background-color var(--mo-quick) var(--ease-out);
}

@media (min-width: 768px){
  /* min-width composed from §1.11 spacing tokens (128 + 48) rather than a new number */
  .s-contact .contact-submit{ width: auto; min-width: calc(var(--s-10) + var(--s-7)); }
}

@media (hover: hover){
  .s-contact .contact-submit:hover{
    background-color: var(--indigo-deep);
    border-color: var(--edge-lit);
  }
}

/* Press: scale 0.97 from css/motion.css ([data-press="on"] [data-m-press]:active). */

.s-contact .contact-submit:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-pill);
}

/* Sending / disabled. aria-disabled keeps the control focusable, so the UI is
   never frozen; `:disabled` is styled too in case the runtime uses it. */
.s-contact .contact-submit[aria-disabled="true"],
.s-contact .contact-submit:disabled{
  color: var(--text-low);
  background: var(--ink-panel);
  border-color: var(--edge);
  box-shadow: none;
  cursor: not-allowed;
}

.s-contact .contact-submit[aria-disabled="true"]:active,
.s-contact .contact-submit:disabled:active{ scale: none; }


/* ── in-button spinner (§11) — inline SVG, no emoji ───────────────────── */

.s-contact .contact-submit__spinner{
  display: none;
  flex: none;
  width: 20px;
  height: 20px;
  color: currentColor;
}

.s-contact .contact-submit[aria-busy="true"] .contact-submit__spinner{
  display: block;
  transform-origin: 50% 50%;
  animation: s-contact-spin 900ms linear infinite;
}

@keyframes s-contact-spin{
  to{ transform: rotate(360deg); }
}

/* base.css's global reduce block already collapses the duration; this makes the
   result deliberate — a static ring rather than a stalled one-frame spin. */
@media (prefers-reduced-motion: reduce){
  .s-contact .contact-submit[aria-busy="true"] .contact-submit__spinner{
    animation: none;
  }
}


/* ── status line — the non-visual half of the loading state ───────────── */

.s-contact .contact-status{
  margin: 0;
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--text-low);
}

/* Same reasoning as .contact-error:empty — role="status" must stay in the tree. */
.s-contact .contact-status:empty{
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* ===== 12-footer ===== */
/* ═══════════════════════════════════════════════════════════════════════
   § 12 · FOOTER                                          root: .s-footer
   Spec: art-direction.md §4 "§ 12 · FOOTER", §1 tokens, §7 accessibility.

   Ground is --ink-deep (§12). Every value below is a §1 custom property —
   no raw hex, no raw px for anything §1 covers. Nothing is redefined.
   Nothing outside .s-footer is styled; no reset, no global rule.

   NO --rec-red and NO --grad-cta in this file. §9.2 closes the CTA
   inventory at seven surfaces and the footer is not one of them
   (colour law 1: red is the only exit, and its last appearance is §10).
   NO --spot-gold either: gold is the evidence colour (law 2) and there is
   no evidence in a footer — the numbers live in §1 and §8.

   NO glow anywhere: colour law 5 permits exactly two glowing DOM elements
   on the page, the hero CTA and the price numeral. Neither is here.

   The fixed WebGL canvas sits at --z-canvas behind the whole page; this
   band is opaque (scene state `off`) and must stay that way.

   Mobile clearance: the §3.2 sticky bottom bar is fixed over this footer
   below 900px. css/base.css already pads <body> by
   calc(var(--bar-bottom) + env(safe-area-inset-bottom)), so the copyright
   line is never covered. This file must not add a second allowance.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── The band ───────────────────────────────────────────────────────── */

.s-footer{
  position: relative;
  z-index: var(--z-content);
  isolation: isolate;                 /* nothing composites through the ground */
  background-color: var(--ink-deep);  /* §12 — opaque, this is the canvas clip */
  color: var(--text-mid);
  border-top: 1px solid var(--edge);
  /* --section-y above (it scales down on a short phone, --s-9 would not),
     --s-8 below: the page ends here, it does not need a full rhythm unit
     of dead space under the copyright. Both are §1.11 tokens.            */
  padding-block: var(--section-y) var(--s-8);
  padding-inline: var(--gutter);
}

.s-footer .footer-inner{
  max-width: var(--maxw);
  margin-inline: auto;
}

/* ── Top block: brand + links ───────────────────────────────────────── */
/* Mobile-first single column. Verified at 360px: --gutter resolves to its
   20px floor, leaving a 320px content box — "Terms and Conditions" at
   --t-body fits on one line, and the two 48px icons plus their 8px gap
   occupy 104px.                                                          */

.s-footer .footer-top{
  display: flex;
  flex-direction: column;
  gap: var(--s-7);
}

@media (min-width: 900px){
  .s-footer .footer-top{
    display: grid;
    grid-template-columns: 5fr 7fr;   /* brand 5/12, links 7/12 — §8's split */
    gap: var(--s-8);
    align-items: start;
  }
}

/* ── Wordmark ───────────────────────────────────────────────────────── */
/* Same construction as the §3.1 top-bar wordmark — Inter 600, uppercase,
   --tr-mark — set larger here because this is the sign-off, not chrome.
   Bebas is not used: §2 reserves the display face for large headlines,
   and a 19px all-caps wordmark is not one.                              */

.s-footer .footer-mark{
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--t-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-mark);
  text-transform: uppercase;
  color: var(--text-hi);              /* ~18.5:1 on --ink-deep */
}

/* ── Tagline ────────────────────────────────────────────────────────── */

.s-footer .footer-tagline{
  margin-top: var(--s-3);
  max-width: var(--measure);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text-low);             /* §12 mandates --text-low. 7.7:1 here. */
}

/* ── Social ─────────────────────────────────────────────────────────── */
/* Inline SVG only — no emoji, no icon font. The links carry an .sr-only
   label, so the SVG itself is aria-hidden in the markup.                */

.s-footer .footer-social{
  display: flex;
  gap: var(--s-2);                    /* ux: Touch Spacing — 8px minimum */
  margin-top: var(--s-5);
}

.s-footer .footer-social-link{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* ux: Touch Target Size — 48dp floor. --s-7 IS 48px. */
  width: var(--s-7);
  height: var(--s-7);
  border: 1px solid var(--edge);
  border-radius: var(--r-pill);
  color: var(--text-low);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    color var(--mo-quick) var(--ease-out),
    border-color var(--mo-quick) var(--ease-out),
    scale var(--m-press-t, var(--mo-instant)) var(--m-press-e, var(--ease-out));
}

@media (hover: hover){
  .s-footer .footer-social-link:hover{
    color: var(--text-hi);
    border-color: var(--edge-strong);
  }
}

/* §8.6 Press: data-m-press (mobile-motion.md §C.12) — scale 0.97 on the
   `scale` lane comes from css/motion.css, released on the spring. */

/* ── Link list ──────────────────────────────────────────────────────── */
/* A plain <ul>, not a <nav> — see the markup header. The spec prints this
   list with "·" separators; that is the spec document's own list notation
   and it is realised here as whitespace rather than glyphs, because a
   generated-content dot is announced by some screen readers and a real
   48px tap target needs the gap more than it needs a divider.           */

.s-footer .footer-links{
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--s-5);
  row-gap: var(--s-2);                /* ux: Touch Spacing — 8px minimum */
}

@media (min-width: 900px){
  .s-footer .footer-links{
    justify-content: flex-end;
  }
}

.s-footer .footer-links a{
  display: inline-flex;
  align-items: center;
  min-height: var(--s-7);             /* 48dp */
  font-size: var(--t-body);
  line-height: var(--lh-h3);
  color: var(--text-mid);             /* ~11.3:1 on --ink-deep */
  text-decoration: none;
  text-underline-offset: var(--s-1);
  -webkit-tap-highlight-color: transparent;
  transition:
    color var(--mo-quick) var(--ease-out);
}

@media (hover: hover){
  .s-footer .footer-links a:hover{
    color: var(--text-hi);
    text-decoration: underline;
  }
}

/* Press: data-m-press in the markup (motion.css scale lane). */

/* ── The rule ───────────────────────────────────────────────────────── */

.s-footer .footer-rule{
  border: 0;
  border-top: 1px solid var(--edge);
  margin-block: var(--s-7);
}

/* ── Bottom row: email + copyright ──────────────────────────────────── */

.s-footer .footer-bottom{
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

@media (min-width: 900px){
  .s-footer .footer-bottom{
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-5);
  }
}

/* Most people will just tap the address, so it is underlined from the
   start rather than on hover only (§11 uses the same reasoning). */
.s-footer .footer-mail a{
  display: inline-flex;
  align-items: center;
  min-height: var(--s-7);             /* 48dp */
  font-size: var(--t-body);
  line-height: var(--lh-h3);
  color: var(--text-mid);
  text-decoration: underline;
  text-underline-offset: var(--s-1);
  word-break: break-word;             /* never overflows the 320px box at 360 */
  -webkit-tap-highlight-color: transparent;
  transition:
    color var(--mo-quick) var(--ease-out);
}

@media (hover: hover){
  .s-footer .footer-mail a:hover{ color: var(--text-hi); }
}

/* Press: data-m-press in the markup (motion.css scale lane). */

.s-footer .footer-copy{
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  color: var(--text-low);             /* 7.7:1 — above the 7:1 floor */
  font-variant-numeric: tabular-nums; /* §2 — every numeral, including 2025 */
}

/* ── Focus ──────────────────────────────────────────────────────────── */
/* base.css owns the global :focus-visible ring; restated here so this
   section is correct in isolation. ux: Focus Appearance, 2px --ring. */

.s-footer a:focus-visible{
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.s-footer .footer-social-link:focus-visible{
  border-radius: var(--r-pill);
}

/* Reduced motion: press lives on the motion.css scale lane, which is inside
   (prefers-reduced-motion: no-preference), so nothing to undo here. */
