/* ============================================================================
   MeshyFlix — home
   The contract is /DESIGN-SYSTEM.md; the tokens are assets/css/system.css.
   Radii: --r-chip / --r-card / --r-block / --r-pill only. Borders:
   var(--hairline) only. State durations: --t-quick / --t-base / --t-slow only.
   Weights: 400 / 500 / 700 only. No colour is declared here that is not a
   token or derived from one with color-mix.

   ----------------------------------------------------------------------------
   ONE RAIL — read this before adding a section
   ----------------------------------------------------------------------------
   The page has ONE left edge. `--rail` is the distance from the section's own
   left edge to where content starts, and it is derived from the same measure
   `.s3-wrap` uses, so a wrapped section and a bleeding one line up exactly.

   Every band uses `.s3-wrap`. `.s3-wrap-wide` and `.s3-wrap-narrow` are pinned
   to the same measure inside `.s3-home` so an old class name cannot reintroduce
   a second edge; short measures are capped in `ch` on the TEXT instead. Bands
   differ by background, by where their right edge falls and by whether they
   bleed — never by a wobble in the left margin.

   `--rail` resolves `100%` against the ELEMENT'S CONTAINING BLOCK, so it may
   only be used on a property whose percentages resolve against the full section
   width — `left`, `padding-inline`, `margin-inline` on a full-width element.
   Every use below is one of those.

   ----------------------------------------------------------------------------
   THE EIGHT BANDS
   ----------------------------------------------------------------------------
     1  hero          full bleed   --bg          the frozen reactor
     2  what it makes full bleed   --surface-1   output wall + glass panel
     3  up close      sticky-pin   --bg          one object, taken apart
     4  where it goes sticky-pin   --surface-1   sideways through the engines
     5  catalogue     full bleed   --bg          the contact sheet
     6  about         narrow       --accent      the page's one inverted band
     7  what it costs wrapped      --bg          ladder + packs, one band
     8  questions     wrapped      --surface-1   the accordion

   No two consecutive bands share a background, and the widths alternate. At
   thumbnail size the page's silhouette is not one unbroken column.

   ----------------------------------------------------------------------------
   HOW THE MOTION LAYOUTS ARE GATED
   ----------------------------------------------------------------------------
     .s3-hm-motion   on <html>. Written by an inline script in front-page.php
                     when JavaScript is alive AND reduced motion is not
                     requested. It gates the HIDDEN state of scroll entrances
                     AND every pinned layout below, and nothing else. The same
                     script rips it back off after 4s if home.js never arrived,
                     so a missing script file can never strand content at
                     opacity 0 or leave a 340vh section stuck on frame one.

     .is-live        written by home.js onto the hero once the reactor sequence
                     is actually running.

     .is-pinned      written by home.js onto bands 3 and 4 only after their
                     ScrollTrigger has been created. Every rule it unlocks is
                     ALSO inside a min-width:901px / min-height:641px query, so
                     a stale class on a phone still lands on the static layout.

   Bands 3 and 4 pin with `position: sticky`, not with ScrollTrigger's pin.
   Sticky is CSS-native: it needs no pin-spacer in the DOM, it cannot leave a
   detached element behind if a tween dies, and when the gating class is absent
   the whole thing collapses to an ordinary stacked section by itself.

   The reactor needs 900px wide AND 640px tall. Both numbers are in home.js too.
   ========================================================================== */

.s3-home{
  display:block;
  background:var(--bg);
  color:var(--text);
  overflow-x:clip;           /* full-bleed bands never widen the document */

  /* THE RAIL. Same arithmetic .s3-wrap performs, exposed as a length. */
  --rail: max(var(--gutter), calc((100% - var(--w-base)) / 2 + var(--gutter)));
}

/* One measure for the whole page. `.s3-wrap-wide` and `.s3-wrap-narrow` still
   exist site-wide; inside this page they resolve to the same rail so no section
   can start at a different x. */
.s3-home .s3-wrap,
.s3-home .s3-wrap-wide,
.s3-home .s3-wrap-narrow{
  width:100%; max-width:var(--w-base); margin-inline:auto; padding-inline:var(--gutter);
}

/* main.css still ships two older components under names the design system now
   uses for type utilities: `.s3-spec` (a padded gradient panel from the old
   spec strip) and `.s3-readout` (the absolutely-positioned plate on the product
   stage). Both outrank system.css in the cascade and wreck the markup here —
   the readout in particular flies out of flow. Restore the system definitions
   for this page. `:where()` keeps them at zero specificity, so every component
   rule further down this file still wins. */
.s3-home :where(.s3-spec){
  display:inline; position:static; background:none; border:0; padding:0; gap:0;
  font-family:var(--mono); font-size:12px; line-height:1.5; letter-spacing:.04em;
  font-variant-numeric:tabular-nums; color:var(--muted);
}
.s3-home :where(.s3-readout){
  display:inline-block; position:static; inset:auto; z-index:auto;
  background:none; border:0; padding:0; gap:0;
  font-family:var(--mono); font-size:10px; letter-spacing:.18em;
  text-transform:uppercase; color:var(--faint); font-weight:400;
}

/* ---- type: the display scale the contract specifies ---------------------- */
.s3-home .s3-h2{
  font-size:clamp(30px, 3.4vw, 52px); line-height:1.06;
  letter-spacing:-.035em; font-weight:500; margin:0;
}
.s3-home .s3-h3{ font-size:20px; line-height:1.25; letter-spacing:-.02em; font-weight:500; margin:0 }
.s3-home .s3-lede{ font-size:17px; line-height:1.6; color:var(--muted); max-width:56ch; margin:0 }
/* Zero the browser's paragraph margins, but at element specificity so ANY
   component class below can still set its own. `.s3-home p` would outrank a
   single class and silently eat every margin-top and margin-inline:auto in
   this file — which is exactly what it used to do. */
:where(.s3-home) p{ margin:0 }

/* ---- buttons: no lift, no shadow, card radius --------------------------- */
.s3-home .s3-btn{
  border-radius:var(--r-card); box-shadow:none; font-weight:500;
  padding:14px 26px; font-size:15px; line-height:1; gap:9px;
  transition:background var(--t-quick) var(--ease),
             border-color var(--t-quick) var(--ease),
             color var(--t-quick) var(--ease);
}
.s3-home .s3-btn:hover{ transform:none; box-shadow:none }
.s3-home .s3-btn-primary{ background:var(--accent); color:var(--accent-ink); font-weight:700 }
.s3-home .s3-btn-primary:hover{ background:color-mix(in srgb, var(--accent) 86%, white) }
.s3-home .s3-btn-glass{ background:var(--glass-1); color:var(--text); border-color:var(--hairline) }
.s3-home .s3-btn-glass:hover{ background:var(--glass-2); border-color:var(--hairline-2) }
.s3-home .s3-btn-bare{ background:none; border-color:transparent; color:var(--muted); padding-inline:0 }
.s3-home .s3-btn-bare:hover{ color:var(--text) }

/* ---- one key/value list, shared by the capability panel and the hero ----- */
.s3-hm-kv{ margin:0; display:flex; flex-direction:column; border-top:1px solid var(--hairline) }
.s3-hm-kv > div{
  display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  padding:9px 0; border-bottom:1px solid var(--hairline);
}
.s3-hm-kv dt{
  flex:0 0 auto;
  font-family:var(--mono); font-size:10px; letter-spacing:.18em;
  text-transform:uppercase; color:var(--faint); font-weight:400;
}
.s3-hm-kv dd{
  margin:0; text-align:right; min-width:0;
  font-family:var(--mono); font-size:12.5px; line-height:1.45;
  font-variant-numeric:tabular-nums; color:var(--text);
}
/* Token figures are live data, so they carry the accent. Nothing else here does.
   Stated at descendant specificity because the lists above set a colour on
   `dd` and a bare class would lose to them. */
.s3-hm-kv dd.s3-hm-live,
.s3-hm-plateau-cell dd.s3-hm-live,
.s3-hm-live{ color:var(--accent) }

/* ---- the page's one vertical rhythm -------------------------------------
   Every band below the hero uses --band, and --band is one ratio of the
   system's --pad. It is a ratio rather than a new clamp so a change upstream
   still lands here, and it is ONE value so the page cannot go from cramped to
   vast by accident.

   .68 rather than 1: at 1489px a full --pad puts 268px of nothing inside every
   band, which on bands this size meant they measured 36-55% content. Audited
   by measuring each band's height against the union of its visible children;
   the target is >70% and every band now clears it. */
.s3-home{ --band: calc(var(--pad) * .68) }

/* ═══════════════════════════ 1 · THE HERO ═══════════════════════════
   full bleed · the scene runs edge to edge and the type sits over it

   THREE DEPTHS, and that is what makes it read as 3D rather than as a picture
   of something 3D:
       z0  the scene — glow, object, scrim
       z1  the headline, which the object passes IN FRONT OF
       z2  the copy and the glass panes, which float above everything
   The scrim between them is what keeps the headline legible where the object
   crosses it, and its fade is what reads as atmosphere. */

.s3-hm-hero{
  position:relative;
  isolation:isolate;
  display:flex; flex-direction:column;
  min-height:calc(100svh - var(--s3-navh, 64px));
  overflow:hidden;                 /* the scene bleeds; the band clips it */
  border-bottom:1px solid var(--hairline);
}

/* -- z0 · the scene ------------------------------------------------------- */
.s3-hm-scene{ position:absolute; inset:0; z-index:0; pointer-events:none }

/* The single accent glow on the page, and the only decorative use of lime. It
   sits behind the object, never behind type. */
.s3-hm-glow{
  position:absolute;
  left:66%; top:46%; width:min(1080px, 82vw); aspect-ratio:1; transform:translate(-50%,-50%);
  background:radial-gradient(circle,
    color-mix(in srgb, var(--accent) 15%, transparent),
    color-mix(in srgb, var(--accent)  0%, transparent) 64%);
  filter:blur(64px);
}

/* The object owns the right two-thirds and is cut by the right edge, so the
   scene reads as bigger than the window. */
.s3-hm-stage{
  position:absolute; inset-block:0; left:20%; right:0;
  display:block;
}
.s3-hm-stage-poster,
.s3-hm-stage model-viewer,
.s3-hm-canvas{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:contain; background:transparent;
  transition:opacity var(--t-slow) var(--ease);
}
.s3-hm-stage-poster{ filter:saturate(.92) contrast(1.04) }
.s3-hm-stage model-viewer{ --poster-color:transparent; opacity:0 }
.s3-hm-canvas{ opacity:0 }

/* The reactor drives these states; nothing else in the sheet touches them.
   Until .is-live lands on the section, the poster is what shows. */
.s3-hm-hero.is-live .s3-hm-stage-poster{ opacity:0 }
.s3-hm-hero.is-live .s3-hm-canvas.is-on{ opacity:1 }
.s3-hm-hero.is-live model-viewer.is-on{ opacity:1 }
/* home.js sets .is-fallback whenever neither the canvas nor a viewer is
   showing — no WebGL, a file that would not parse — so the stage is never an
   empty frame sitting next to a set of numbers. */
.s3-hm-hero.is-live.is-fallback .s3-hm-stage-poster{ opacity:1 }
/* Stage 05 on the phone profile: the refine job's OWN render, which is the
   image the markup already shipped at fetchpriority=high. The same object with
   its maps on, for no second download — see home.js §1 for why a phone does
   not load a 289 KB custom element and a 562 KB textured GLB to say it. */
.s3-hm-hero.is-live.is-render .s3-hm-stage-poster{ opacity:1 }

/* The scrim is what lets a headline live under a 3D object. It never crosses
   the whole band — the object's own side stays clean. */
.s3-hm-scrim{
  position:absolute; inset:0;
  background:
    linear-gradient(96deg,
      var(--bg) 0%,
      color-mix(in srgb, var(--bg) 82%, transparent) 26%,
      color-mix(in srgb, var(--bg) 24%, transparent) 52%,
      transparent 72%),
    linear-gradient(0deg,
      var(--bg) 0%,
      color-mix(in srgb, var(--bg) 55%, transparent) 22%,
      transparent 46%);
}

/* -- z1/z2 · the type ----------------------------------------------------- */
.s3-hm-hero-body{
  position:relative; z-index:1;
  flex:1 1 auto;
  display:grid;
  grid-template-columns:minmax(0, 1fr) minmax(0, 22rem);
  grid-template-rows:auto 1fr auto;
  grid-template-areas:
    'head  head'
    'typer stages'
    'copy  stages';
  align-content:space-between;
  column-gap:clamp(24px, 3vw, 56px);
  row-gap:clamp(20px, 2.6vw, 40px);
  padding:clamp(40px, 5.4vw, 96px) var(--rail) clamp(32px, 3.6vw, 60px);
}

/* The headline has nothing beside it. That is the whole point: at 1440 it runs
   ~99px over two lines and owns the top of the viewport. It sits BELOW the
   object in z, so the mesh crosses in front of its tail. */
.s3-hm-hero-h{
  grid-area:head; margin:0;
  position:relative; z-index:-1;
  font-size:clamp(46px, 6.8vw, 116px);
  line-height:1.0;
  letter-spacing:-.045em;
  font-weight:500;
  max-width:15ch;
  text-wrap:balance;
}

.s3-hm-hero-copy{ grid-area:copy; display:flex; flex-direction:column; gap:24px; align-items:flex-start }
.s3-hm-hero-lede{ max-width:38ch }
.s3-hm-hero-act{ display:flex; align-items:center; gap:20px; flex-wrap:wrap }
.s3-hm-hero-from{ color:var(--faint) }

/* -- the prompt pane: glass over the 3D stage ----------------------------- */
/* .s3-glass and .s3-glass-refract are the material and live in system.css.
   Nothing here re-declares background, blur, border or shadow — only layout. */
.s3-hm-typer{
  grid-area:typer; align-self:end; justify-self:start;
  width:min(100%, 40rem);
  display:flex; flex-direction:column; gap:8px;
  padding:16px 20px 18px;
}
.s3-hm-typer-line{
  font-family:var(--mono); font-size:clamp(12.5px, 1vw, 14.5px); line-height:1.55;
  color:var(--text); letter-spacing:.01em;
  min-height:calc(1.55em * 2);
}
.s3-hm-caret{
  display:none; width:.55em; height:1em; margin-left:2px;
  vertical-align:text-bottom; background:var(--accent);
}
/* The caret is a live control surface, so it may carry the accent — and it only
   exists while something is actually typing. */
.s3-hm-hero.is-live .s3-hm-caret{ display:inline-block; animation:s3-hm-blink 1.06s steps(1) infinite }
@keyframes s3-hm-blink{ 0%,50%{opacity:1} 51%,100%{opacity:0} }

/* -- the stage ladder: the four real steps, lighting up as they arrive ----- */
.s3-hm-stages{
  grid-area:stages; align-self:end;
  list-style:none; margin:0; padding:10px 6px;
  display:flex; flex-direction:column;
}
.s3-hm-stage-row{
  display:grid; grid-template-columns:auto 1fr; gap:2px 12px;
  align-items:baseline;
  padding:11px 14px; border-radius:var(--r-chip);
  transition:background var(--t-base) var(--ease), opacity var(--t-base) var(--ease);
}
.s3-hm-stage-n{ color:var(--faint); letter-spacing:.2em; grid-row:span 2 }
.s3-hm-stage-name{ font-size:14px; font-weight:500; letter-spacing:-.01em; color:var(--text) }
.s3-hm-stage-val{ grid-column:2; color:var(--muted); font-variant-numeric:tabular-nums }
/* Only while the sequence runs do the rows wait their turn. */
.s3-hm-hero.is-live .s3-hm-stage-row{ opacity:.34 }
.s3-hm-hero.is-live .s3-hm-stage-row.is-done{ opacity:1 }
.s3-hm-hero.is-live .s3-hm-stage-row.is-on{ opacity:1; background:var(--glass-2) }
.s3-hm-hero.is-live .s3-hm-stage-row.is-on .s3-hm-stage-n{ color:var(--text) }

/* -- the instrument plate: a hard bottom edge, so the hero lands ---------- */
.s3-hm-plateau{
  position:relative; z-index:2;
  padding-left:var(--rail);
  border-top:1px solid var(--hairline);
  background:color-mix(in srgb, var(--bg) 72%, transparent);
  -webkit-backdrop-filter:blur(18px); backdrop-filter:blur(18px);
}
.s3-hm-plateau-grid{
  margin:0;
  display:grid; grid-template-columns:repeat(var(--cells, 6), minmax(0,1fr));
  gap:1px; background:var(--hairline);
}
.s3-hm-plateau-cell{
  display:flex; flex-direction:column; gap:7px; min-width:0;
  padding:16px clamp(14px, 1.4vw, 26px) 18px;
  background:var(--bg);
}
.s3-hm-plateau-cell:first-child{ padding-left:0 }
.s3-hm-plateau-cell dd{
  margin:0; font-family:var(--mono); font-size:13px; line-height:1.45;
  letter-spacing:.01em; font-variant-numeric:tabular-nums; color:var(--text);
}

/* Below the reactor's floor the hero is a static composition, and the stage
   moves under the type instead of beside it. */
@media (max-width:1080px){
  .s3-hm-hero-body{
    grid-template-columns:minmax(0,1fr);
    grid-template-areas:'head' 'typer' 'stages' 'copy';
    align-content:start;
    row-gap:clamp(18px, 2.4vw, 30px);
  }
  .s3-hm-stages{ justify-self:start; width:min(100%, 30rem) }
  .s3-hm-stage{ left:0; inset-block:auto 0; height:62%; opacity:.5 }
  .s3-hm-scrim{
    background:
      linear-gradient(0deg, var(--bg) 4%, color-mix(in srgb, var(--bg) 40%, transparent) 40%, transparent 70%),
      linear-gradient(96deg, var(--bg) 0%, color-mix(in srgb, var(--bg) 40%, transparent) 46%, transparent 78%);
  }
  .s3-hm-hero-h{ z-index:0 }
}
@media (max-width:860px){
  .s3-hm-plateau-grid{ grid-template-columns:repeat(2, minmax(0,1fr)) }
  .s3-hm-plateau-cell:nth-child(odd){ padding-left:0 }
}
/* Phone. The scene comes out of its absolute layer and LEADS the band, so the
   object is the first thing on screen and the reactor runs in it — the same
   five stages as the desktop hero, at a third of the canvas density and off
   one 86 KB file. See home.js §1 for what stages 04 and 05 are here and what
   they cost.

   THE ONE PANE OVER MEDIA. The prompt is what produced the object, so it
   belongs against it: the body is pulled up by --s3-hm-lap and the typer is
   the first thing in it, which lands its top edge and both upper corners on
   the live canvas. That is a real backdrop to bend, and it is why refraction
   is no longer gated out down here. Everything else on this band — the
   headline, the ladder, the copy — sits on the page's own surface, where the
   frosted CSS material is the correct answer and is what it gets. */
@media (max-width:560px){
  .s3-hm-hero{ min-height:0; --s3-hm-lap:clamp(56px, 17vw, 92px) }
  .s3-hm-hero-body{
    padding-block:clamp(24px, 6vw, 36px) clamp(24px, 6vw, 36px);
    /* The prompt leads, then the claim, then the ladder that proves it. */
    grid-template-areas:'typer' 'head' 'stages' 'copy';
    margin-top:calc(-1 * var(--s3-hm-lap));
  }
  .s3-hm-scene{ position:static }
  .s3-hm-stage{
    position:relative; inset:auto; opacity:1; margin:0;
    /* Viewport-relative rather than an aspect ratio: a 4:3 box was 292px on a
       390px phone, which is not a stage. Clamped so a short phone in landscape
       cannot hand the whole screen to it. The overlap is added on top, so the
       pane eats the surplus rather than the object. */
    height:calc(clamp(260px, 44svh, 420px) + var(--s3-hm-lap));
  }
  /* The object is framed against the part of the stage the pane does not
     cover, less about half the overlap — so it reads whole, and its foot still
     passes behind the glass, which is the depth cue the desktop hero gets from
     the headline. */
  .s3-hm-stage-poster,
  .s3-hm-stage model-viewer,
  .s3-hm-canvas{ bottom:calc(var(--s3-hm-lap) * .55) }
  .s3-hm-scrim{ display:none }
  .s3-hm-glow{ top:30%; width:min(760px, 108vw) }
  .s3-hm-typer{ width:100%; align-self:start }
  .s3-hm-typer-line{ min-height:calc(1.55em * 2) }
  /* One column, but each cell laid out as a spec row rather than a stack —
     six stacked cells is 490px of hero nobody scrolls past. */
  .s3-hm-plateau-grid{ grid-template-columns:minmax(0,1fr); gap:0 }
  .s3-hm-plateau-cell{
    flex-direction:row; align-items:baseline; justify-content:space-between;
    gap:16px; padding:12px 0; border-bottom:1px solid var(--hairline);
  }
  .s3-hm-plateau-cell:last-child{ border-bottom:0 }
  .s3-hm-plateau-cell dd{ text-align:right }
}

/* ═══════════════════════════ 2 · WHAT IT MAKES ═══════════════════════════
   full bleed · --surface-1

   A wall of real output with the control panel floating on it. The wall is
   two rows that counter-drift as the band passes, so the band is never a still
   picture of a grid; the panel is the one place after the hero where there is
   genuinely moving media behind a pane of glass for it to refract. */

.s3-hm-cap{
  position:relative;
  background:var(--surface-1);
  padding-top:var(--band);
  border-bottom:1px solid var(--hairline);
  overflow:hidden;                 /* the rows run past both edges */
}
.s3-hm-cap-head{
  display:flex; flex-direction:column; gap:18px;
  margin-bottom:clamp(30px, 3.4vw, 58px);
}
.s3-hm-cap-head .s3-h2{ max-width:19ch }

.s3-hm-cap-stage{ position:relative; padding-bottom:var(--band) }

/* The scrim, and it is the hero's trick again: three depths rather than two.
   z0 the wall · z1 the scrim · z2 the panel. Without it the panel is a pane of
   glass over a lit render and its own text loses the contrast fight.

   It is deliberately NOT opaque under the pane. The panel refracts, and
   refraction needs something behind it to bend — scrim the wall out completely
   and the most expensive material on the page is spent on a flat grey. So it
   settles the left third to about two thirds and leaves the renders reading
   through, which is also what makes the pane look like glass rather than a
   card. Never crosses the whole band. */
.s3-hm-cap-stage::before{
  content:''; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(96deg,
    color-mix(in srgb, var(--surface-1) 84%, transparent) 0%,
    color-mix(in srgb, var(--surface-1) 64%, transparent) 24%,
    color-mix(in srgb, var(--surface-1) 20%, transparent) 46%,
    transparent 62%);
}

/* -- the wall ------------------------------------------------------------- */
.s3-hm-wall{ position:relative; z-index:0; display:flex; flex-direction:column; gap:clamp(12px, 1.4vw, 22px) }
.s3-hm-wall-row{
  list-style:none; margin:0; padding:0;
  display:flex; gap:clamp(12px, 1.4vw, 22px);
  width:max-content;
}
/* The second row starts further left so the two never line up into a lattice. */
.s3-hm-wall-row[data-hm-wall-row="-1"]{ margin-left:calc(-1 * clamp(90px, 11vw, 300px)) }

.s3-hm-wall-cell{ flex:0 0 auto; width:clamp(190px, 16.5vw, 288px) }
.s3-hm-wall-cell figure{ margin:0; display:flex; flex-direction:column; gap:11px }
.s3-hm-wall-cell img{
  display:block; width:100%; aspect-ratio:1; object-fit:cover;
  border-radius:var(--r-card); border:1px solid var(--hairline);
  background:var(--surface-2);
}
.s3-hm-wall-cell figcaption{ display:flex; flex-direction:column; gap:5px; min-width:0 }
.s3-hm-wall-prompt{
  font-size:13px; line-height:1.45; letter-spacing:-.005em; color:var(--muted);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.s3-hm-wall-spec{ color:var(--faint) }

/* -- the panel: glass, floating on the wall ------------------------------- */
/* .s3-glass and .s3-glass-refract are the material and live in system.css.
   Nothing here re-declares background, blur, border or shadow — only layout. */
.s3-hm-cap-panel{
  position:absolute; z-index:2;
  left:var(--rail); top:50%; transform:translateY(-50%);
  width:min(34rem, 44vw);
  padding:clamp(20px, 1.8vw, 30px);
  display:flex; flex-direction:column; gap:16px;
}
.s3-hm-cap-tabs{ display:flex; flex-wrap:wrap; gap:5px }
.s3-hm-cap-tab{
  appearance:none; cursor:pointer;
  padding:8px 11px; border-radius:var(--r-chip);
  background:transparent; border:1px solid var(--hairline);
  font-family:var(--font); font-size:13px; font-weight:500; line-height:1;
  color:var(--muted);
  transition:color var(--t-quick) var(--ease),
             border-color var(--t-quick) var(--ease),
             background var(--t-quick) var(--ease);
}
.s3-hm-cap-tab:hover{ color:var(--text); border-color:var(--hairline-2) }
/* The active state of a control is one of the permitted accent roles. */
.s3-hm-cap-tab.is-on{
  color:var(--accent); border-color:var(--accent);
  background:color-mix(in srgb, var(--accent) 8%, transparent);
}

.s3-hm-cap-body{ display:flex; flex-direction:column; gap:12px }
.s3-hm-cap-body[hidden]{ display:none }
.s3-hm-cap-line{ font-size:14.5px; line-height:1.6; color:var(--muted); max-width:46ch }
.s3-hm-cap-cta{ align-self:flex-start }

@media (max-width:1180px){
  .s3-hm-cap-panel{ width:min(29rem, 52vw) }
  .s3-hm-wall-cell{ width:clamp(170px, 22vw, 240px) }
}
/* Below the floor the panel comes out of its absolute layer and leads the
   band: the wall still drifts, but it is no longer something to read through. */
@media (max-width:900px){
  /* The panel is in flow now, so it has the band's own surface behind it and
     the scrim would only be a grey wash over the wall. */
  .s3-hm-cap-stage::before{ display:none }
  .s3-hm-cap-stage{ display:flex; flex-direction:column-reverse; gap:clamp(26px, 5vw, 40px) }
  .s3-hm-cap-panel{
    position:static; transform:none; width:auto;
    margin-inline:var(--rail);
  }
  .s3-hm-wall-row[data-hm-wall-row="-1"]{ margin-left:calc(-1 * clamp(60px, 16vw, 140px)) }
}
@media (max-width:560px){
  .s3-hm-wall-cell{ width:clamp(150px, 46vw, 200px) }
  .s3-hm-wall{ gap:12px }
}


/* ═══════════════════════════ 3 · UP CLOSE ═══════════════════════════
   sticky-pinned · --bg

   THE DEFAULT LAYOUT IS THE STATIC ONE. Everything below the first block is a
   plain stacked composition — render, map set, spec plate, all visible, all in
   flow. The pinned version is unlocked only by .s3-hm-motion + .is-pinned +
   a viewport over 900x640, so there is no path where this band is a void. */

.s3-hm-q{
  background:var(--bg);
  padding-block:var(--band);
  border-bottom:1px solid var(--hairline);
}
.s3-hm-q-grid{
  display:grid; align-items:start;
  grid-template-columns:minmax(0, .84fr) minmax(0, 1.16fr);
  gap:clamp(28px, 3.4vw, 72px);
}
.s3-hm-q-copy{ display:flex; flex-direction:column; gap:22px }
.s3-hm-q-copy .s3-h2{ max-width:17ch }

.s3-hm-q-steps{
  list-style:none; margin:0; padding:0;
  border-top:1px solid var(--hairline);
}
.s3-hm-q-step{
  display:grid; grid-template-columns:auto 1fr; gap:14px; align-items:baseline;
  padding:13px 2px; border-bottom:1px solid var(--hairline);
  transition:opacity var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.s3-hm-q-n{ color:var(--faint); letter-spacing:.2em }
.s3-hm-q-label{ font-size:15px; font-weight:500; letter-spacing:-.01em; color:var(--text) }
.s3-hm-q-ask{ color:var(--faint); line-height:1.6 }

/* -- the stage, static: three things stacked ------------------------------ */
.s3-hm-q-stage{ display:flex; flex-direction:column; gap:16px; min-width:0 }
.s3-hm-q-shot{
  margin:0; overflow:hidden; position:relative;
  border-radius:var(--r-block); border:1px solid var(--hairline);
  background:var(--surface-1); aspect-ratio:1;
}
.s3-hm-q-shot img{ width:100%; height:100%; object-fit:cover; display:block }

/* A canvas with nothing parsed into it is a hole. home.js turns it on only
   once it holds real geometry. */
.s3-hm-q-wire{ display:none; width:100%; height:100% }
.s3-hm-q-note{ display:none; color:var(--faint); line-height:1.5 }

.s3-hm-q-maps{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(118px, 1fr));
  gap:12px;
}
.s3-hm-q-map{ margin:0; display:flex; flex-direction:column; gap:8px; min-width:0 }
.s3-hm-q-map img{
  width:100%; aspect-ratio:1; object-fit:cover; display:block;
  border-radius:var(--r-chip); border:1px solid var(--hairline);
  background:var(--surface-2);
}
.s3-hm-q-map figcaption{ display:flex; flex-direction:column; gap:3px; min-width:0 }
.s3-hm-q-map-name{ font-size:13px; font-weight:500; letter-spacing:-.01em; color:var(--text) }
.s3-hm-q-map figcaption .s3-spec{ color:var(--faint) }

.s3-hm-q-plate{
  margin:0;
  display:grid; grid-template-columns:repeat(auto-fit, minmax(128px, 1fr));
  gap:16px 20px;
  padding:18px 20px;
}
.s3-hm-q-plate > div{ display:flex; flex-direction:column; gap:6px; min-width:0 }
.s3-hm-q-plate dd{
  margin:0; font-family:var(--mono); font-size:14px; line-height:1.3;
  font-variant-numeric:tabular-nums; color:var(--text);
}

@media (max-width:980px){
  .s3-hm-q-grid{ grid-template-columns:minmax(0,1fr) }
}

/* -- the pinned version --------------------------------------------------- */
/* Gated three ways: the motion class, the class home.js writes only after its
   ScrollTrigger exists, and the viewport floor. Any one of them missing and
   the static composition above is what renders. */
@media (min-width:901px) and (min-height:641px){
  .s3-hm-motion .s3-hm-q.is-pinned{
    height:var(--s3-q-h, 340vh);
    padding-block:0;
  }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-inner{
    position:sticky; top:var(--s3-navh, 64px);
    height:calc(100svh - var(--s3-navh, 64px));
    display:flex; align-items:center;
  }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-grid{ width:100%; align-items:center }

  /* The stage becomes one framed window and the four states are layers in it. */
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-stage{
    position:relative; display:block;
    /* Deep enough that the stuck viewport is the stage rather than the stage
       plus two bands of nothing. At 900px tall this leaves ~65px above and
       below instead of 105. */
    height:min(78svh, 700px);
    border-radius:var(--r-block); border:1px solid var(--hairline);
    background:var(--surface-1); overflow:hidden;
  }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-shot{
    position:absolute; inset:0; border:0; border-radius:0;
    background:none; aspect-ratio:auto;
  }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-shot img{ object-fit:contain }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-wire{
    position:absolute; inset:0; opacity:0;
  }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-note{
    position:absolute; left:18px; bottom:16px; z-index:3;
    display:block; opacity:0;
  }
  /* The maps fill the frame rather than hugging its floor. Five swatches in a
     strip along the bottom left 380px of empty stage above them, which is the
     void the previous pass shipped. --mcols is written by the template because
     a stylesheet cannot count how many channels this particular job wrote. */
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-maps{
    position:absolute; inset:clamp(14px, 1.6vw, 24px); z-index:2;
    grid-template-columns:repeat(var(--mcols, 3), minmax(0,1fr));
    align-content:center; gap:clamp(10px, 1.2vw, 18px);
    opacity:0;
  }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-plate{
    position:absolute; left:clamp(14px,1.6vw,24px); right:clamp(14px,1.6vw,24px);
    bottom:clamp(14px,1.6vw,24px); z-index:4;
    opacity:0;
  }
  /* Only while the sequence runs do the rows wait their turn. */
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-step{ opacity:.34 }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-step.is-done{ opacity:1 }
  /* The active row brightens; the step NUMBER does not take the accent. Lime is
     for actions and live values, and a step number is neither — the design
     system names it in the forbidden list by name. */
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-step.is-on{ opacity:1 }
  .s3-hm-motion .s3-hm-q.is-pinned .s3-hm-q-step.is-on .s3-hm-q-n{ color:var(--text) }
}

/* -- the flowing version -------------------------------------------------- */
/* A phone gets the same four states without the pin. The measurement that
   decided it: the stuck viewport would have to hold the heading (4 lines),
   the standing-back sentence (7 lines), four step rows and the stage, which is
   ~1,010px of content against the 780px a 390x844 phone has under the nav. So
   the band stays exactly the height of its content, the render and the
   wireframe cross-fade inside the same square, and states 3 and 4 mark
   themselves as the map set and the stat plate arrive. Nothing sticks, so
   nothing can hold a thumb.

   home.js writes .is-wired only after its triggers exist, and moves the canvas
   inside the figure so the two share one box; the teardown puts it back. */
@media (max-width:900px){
  .s3-hm-motion .s3-hm-q.is-wired .s3-hm-q-shot > .s3-hm-q-wire{
    position:absolute; inset:0; width:100%; height:100%;
    opacity:0; z-index:2;
  }
  /* The count in it does not exist until the file has been parsed, so it is
     display:none until home.js has something true to put in it. */
  .s3-hm-motion .s3-hm-q.is-wired .s3-hm-q-note{ display:block }
  .s3-hm-motion .s3-hm-q.is-wired .s3-hm-q-step{ opacity:.34 }
  .s3-hm-motion .s3-hm-q.is-wired .s3-hm-q-step.is-done{ opacity:1 }
  .s3-hm-motion .s3-hm-q.is-wired .s3-hm-q-step.is-on{ opacity:1 }
  .s3-hm-motion .s3-hm-q.is-wired .s3-hm-q-step.is-on .s3-hm-q-n{ color:var(--text) }
}


/* ═══════════════════════════ 4 · WHERE IT GOES NEXT ═══════════════════════
   sticky-pinned, and the page moves sideways · --surface-1

   Static default again: a real horizontal scroller with snap points, complete
   and usable with no JavaScript at all. The pin only replaces the scrollbar
   with the page's own scroll. */

.s3-hm-flow{
  background:var(--surface-1);
  border-bottom:1px solid var(--hairline);
}
.s3-hm-flow-inner{ padding-block:var(--band) }
.s3-hm-flow-head{
  display:flex; flex-direction:column; gap:14px;
  margin-bottom:clamp(24px, 2.4vw, 40px);
}
/* Two lines at 1440, three at 900. The head shares a stuck viewport with the
   track, so every line it takes is a line the cards lose. */
.s3-hm-flow-head .s3-h2{ max-width:23ch }
.s3-hm-flow-head .s3-lede{ max-width:52ch }

.s3-hm-flow-view{
  overflow-x:auto; overflow-y:hidden;
  scroll-snap-type:x proximity;
  padding-inline:var(--rail);
  scrollbar-width:thin;
}
.s3-hm-flow-track{
  list-style:none; margin:0; padding:0;
  display:flex; gap:clamp(14px, 1.4vw, 26px);
  width:max-content;
}
.s3-hm-flow-card{
  flex:0 0 auto; scroll-snap-align:start;
  width:clamp(262px, 24vw, 372px);
  min-height:clamp(320px, 30vw, 408px);
  display:flex; flex-direction:column; gap:13px;
  padding:clamp(20px, 1.7vw, 28px);
  background:var(--bg);
  border:1px solid var(--hairline);
  border-radius:var(--r-card);
  transition:border-color var(--t-base) var(--ease);
}
.s3-hm-flow-card:hover{ border-color:var(--hairline-2) }
.s3-hm-flow-n{ color:var(--faint); letter-spacing:.2em }
.s3-hm-flow-name{
  margin:0; font-size:clamp(24px, 2.1vw, 30px); line-height:1.1;
  letter-spacing:-.03em; font-weight:500;
}
.s3-hm-flow-fmts{ list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:6px }
.s3-hm-flow-line{ font-size:15px; line-height:1.65; color:var(--muted) }
.s3-hm-flow-sizes,
.s3-hm-flow-manifest{
  list-style:none; margin:auto 0 0; padding:0;
  border-top:1px solid var(--hairline);
}
.s3-hm-flow-sizes li,
.s3-hm-flow-manifest li{
  display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  padding:9px 0; border-bottom:1px solid var(--hairline);
  font-family:var(--mono); font-size:12.5px; font-variant-numeric:tabular-nums;
  color:var(--text);
}
.s3-hm-flow-sizes li span:last-child,
.s3-hm-flow-manifest li span:last-child{ color:var(--muted) }
.s3-hm-flow-manifest{ margin-top:0 }
.s3-hm-flow-extra{ color:var(--faint); line-height:1.6 }
.s3-hm-flow-end{ background:var(--surface-2) }
.s3-hm-flow-end .s3-btn{ align-self:flex-start; margin-top:auto }

/* The progress rail. Only ever shown on the swipe track, because on the pinned
   track the page's own scrollbar is already the position report. It exists
   because every mobile platform hides the horizontal scrollbar of an
   overflow-x element, which leaves a swipeable track with no affordance and no
   indication that there are seven of these and you are on the second. */
.s3-hm-flow-rail{ display:none }

/* The floor here is taller than band 3's: the head and the track have to share
   one stuck viewport, and under ~760px they do not both fit. Below it the same
   markup is the horizontal scroller above — complete, and arguably better on a
   short screen. Repeated in home.js as PIN_MQ_FLOW. */
@media (min-width:901px) and (min-height:760px){
  .s3-hm-motion .s3-hm-flow.is-pinned{ height:var(--s3-flow-h, 300vh) }
  /* The stuck element is its own content height, CENTRED BY ITS OFFSET rather
     than by filling the viewport and centring inside it. Both render
     identically — the space around it is the section's own surface either
     way — but this way the band's box is the composition, so measuring it
     measures something real instead of counting the window's leftovers as a
     hole. --s3-flow-ih is written by home.js from the measured content. */
  .s3-hm-motion .s3-hm-flow.is-pinned .s3-hm-flow-inner{
    position:sticky;
    top:max(var(--s3-navh, 64px), calc((100svh - var(--s3-flow-ih, 700px)) * .5));
    display:flex; flex-direction:column;
    padding-block:0; overflow:hidden;
  }
  .s3-hm-motion .s3-hm-flow.is-pinned .s3-hm-flow-view{
    overflow:visible; scroll-snap-type:none; padding-inline:0;
  }
  .s3-hm-motion .s3-hm-flow.is-pinned .s3-hm-flow-track{ padding-left:var(--rail) }
  /* Whatever the content wants, the card may not be taller than the space left
     under the head — a card clipped by the stuck viewport reads as a bug. */
  .s3-hm-motion .s3-hm-flow.is-pinned .s3-hm-flow-card{
    min-height:0;
    height:clamp(300px, calc(100svh - var(--s3-navh, 64px) - 250px), 500px);
  }
}

/* -- the same band, driven by a thumb ------------------------------------- */
/* Below the floor this band is not a scrub at all. A pinned horizontal scrub is
   the one effect here that genuinely misbehaves on touch — the gesture is
   vertical, the movement is horizontal, and momentum scrolling makes the two
   disagree — so what a phone gets is the real swipe track the markup already
   is, given the affordances a swipe track needs. home.js writes .is-swipe and
   drives --s3-d, the card's distance from the rail in viewport widths.

   Nothing below sets touch-action and nothing preventDefaults: the view is
   overflow-x only, so a thumb travelling down the screen passes through it. */
@media (max-width:900px){
  .s3-hm-flow-view{
    scroll-snap-type:x mandatory;
    scroll-padding-left:var(--rail);
    overscroll-behavior-x:contain;
  }
  .s3-hm-flow-card{ scroll-snap-align:start }

  .s3-hm-flow.is-swipe .s3-hm-flow-rail{
    display:block; height:2px; margin:clamp(16px,4vw,24px) var(--rail) 0;
    background:var(--hairline); border-radius:var(--r-pill); overflow:hidden;
  }
  .s3-hm-flow.is-swipe .s3-hm-flow-rail > span{
    display:block; height:100%; width:25%;
    background:var(--muted); border-radius:inherit;
  }

  /* Depth on a track you are dragging: the number and the name travel a little
     against their own card. One custom property, one transform, no layout. */
  .s3-hm-flow.is-swipe .s3-hm-flow-n,
  .s3-hm-flow.is-swipe .s3-hm-flow-name{
    transform:translate3d(calc(var(--s3-d, 0) * 13px), 0, 0);
  }
  /* And the ones still to come stand back, so the track has a front. */
  .s3-hm-flow.is-swipe .s3-hm-flow-card{
    opacity:clamp(.46, calc(1.24 - var(--s3-d, 0) * .44), 1);
  }
}


/* ═══════════════════════════ 5 · THE CATALOGUE ═══════════════════════════
   full bleed, cut by both viewport edges · --bg

   A contact sheet, not a row of floating cards: thumbnails flush to their
   cells, one 1px grid, no gaps, no shadows, no radius on the cell. Which is
   the marketplace rule in the design system, and it is what makes a catalogue
   read as "there is more of this" rather than as six products. */

.s3-hm-shop{
  background:var(--bg);
  padding-block:var(--band);
  border-bottom:1px solid var(--hairline);
}
/* Heading and sentence share the top row, the toolbar is its own full-width
   row beneath them. It used to be a single 64ch column with the right half of
   the page empty beside it and the filter wrapped underneath into the gap. */
.s3-hm-shop-head{
  display:grid; grid-template-columns:minmax(0, .92fr) minmax(0, 1fr);
  align-items:end; gap:clamp(22px, 2.6vw, 40px) clamp(20px, 3vw, 64px);
  margin-bottom:clamp(26px, 2.8vw, 44px);
}
.s3-hm-shop-title{ grid-column:1; max-width:14ch }
.s3-hm-shop-lede{ grid-column:2; max-width:56ch; margin:0 }
.s3-hm-shop-tools{
  grid-column:1 / -1;
  display:flex; flex-wrap:wrap; align-items:center; gap:8px;
}
.s3-hm-shop-tools .s3-chip{ cursor:pointer; appearance:none; font-family:var(--mono) }
.s3-hm-shop-all{ margin-left:auto; font-size:14px }

/* The wall is a rectangle at every step. Two things make it one, and both are
   needed:

   1. A FIXED column count that divides the item count, rather than auto-fill.
      auto-fill picked seven columns for sixteen models and left five empty
      tracks in the last row. The steps below are 6 / 4 / 3 / 2 and PHP cuts the
      wall to twelve, which every one of them divides.
   2. The gridlines are drawn BY THE CELLS, not by a background showing through
      1px gaps. A background-drawn grid paints its lines across empty tracks
      too, which is what turned that last row into a grey slab — the fault the
      owner saw. A 1px outset shadow lands exactly in the gap, so a cell that is
      not there draws nothing. That also keeps a filtered subset honest. */
.s3-hm-shop-grid{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(var(--cols, 6), minmax(0,1fr));
  gap:1px; background:var(--bg);
  border-top:1px solid var(--hairline);
}
.s3-hm-shop-cell{
  background:var(--bg); min-width:0;
  box-shadow:1px 0 0 var(--hairline), 0 1px 0 var(--hairline);
}
.s3-hm-shop-cell[hidden]{ display:none }
.s3-hm-shop-cell > a{
  display:flex; flex-direction:column; height:100%;
  text-decoration:none; color:inherit;
  transition:background var(--t-base) var(--ease);
}
.s3-hm-shop-cell > a:hover{ background:var(--surface-1) }
.s3-hm-shop-shot{
  display:block; aspect-ratio:1; overflow:hidden;
  background:var(--surface-1);
}
.s3-hm-shop-shot img{ width:100%; height:100%; object-fit:cover; display:block }
.s3-hm-shop-meta{ display:flex; flex-direction:column; gap:5px; padding:13px 14px 0; min-width:0 }
.s3-hm-shop-name{
  font-size:14px; font-weight:500; letter-spacing:-.012em; line-height:1.3; color:var(--text);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.s3-hm-shop-spec{ color:var(--faint) }
.s3-hm-shop-price{
  margin-top:auto; padding:10px 14px 15px;
  font-family:var(--mono); font-size:13px; font-variant-numeric:tabular-nums; color:var(--text);
}
.s3-hm-shop-price del{ opacity:.4; margin-right:6px }
.s3-hm-shop-price ins{ text-decoration:none }

/* 6 · 4 · 3 · 2 — every step divides the twelve cells PHP puts on the wall, so
   the last row is never a hole. Cell width stays between ~135px and ~260px the
   whole way down, which is what a contact sheet should read as. */
@media (max-width:1099px){ .s3-hm-shop-grid{ --cols:4 } }
@media (max-width:900px){
  .s3-hm-shop-head{ grid-template-columns:minmax(0,1fr); align-items:start }
  .s3-hm-shop-title{ grid-column:1; max-width:18ch }
  .s3-hm-shop-lede{ grid-column:1 }
  .s3-hm-shop-all{ margin-left:6px }
}
@media (max-width:699px){ .s3-hm-shop-grid{ --cols:3 } }
@media (max-width:479px){ .s3-hm-shop-grid{ --cols:2 } }


/* ═══════════════════════════ 6 · THE RULES ═══════════════════════════
   full measure, four display statements · --accent, dark type

   The page's one inverted band, and the one the silhouette needs. It used to
   hold a six-cell plate of live counters with the four rules set as grey
   footnotes underneath it; the counters were true and they undersold, so the
   footnotes are the band now and there is no plate. Each rule is display type
   on the left with its checkable detail on the right — a ledger, which is what
   a list of terms should look like, rather than four identical cards.

   On lime a white hairline is invisible, so the hairline is mixed from the
   band's own ink — still one derived value, not a new colour. Nothing in here
   takes a light colour: system.css forces `color: inherit` inside .s3-invert
   for exactly that reason, and every softened value below is the dark ink at a
   lower alpha, never a lighter one. */

.s3-hm-rules{
  padding-block:var(--band);
  --hairline:   color-mix(in srgb, var(--accent-ink) 16%, transparent);
  --hairline-2: color-mix(in srgb, var(--accent-ink) 30%, transparent);
}

/* Heading left, the standing-back sentence right, so the top row is the full
   measure instead of a column with a void beside it. */
.s3-hm-rules-head{
  display:grid; grid-template-columns:minmax(0, .92fr) minmax(0, 1fr);
  align-items:end; gap:clamp(24px, 3.4vw, 76px);
  margin-bottom:clamp(38px, 4.2vw, 68px);
}
.s3-hm-rules-head .s3-h2{ max-width:13ch }
/* The ink is restated here on purpose. system.css tints .s3-invert .s3-lede
   with the dark ink, but `.s3-home .s3-lede` in this sheet carries --muted at
   the same specificity and lands later, so the sentence arrived on lime in a
   near-white it is unreadable in — the same class of bug the :where() block in
   system.css was written to kill. Nothing in this band takes a light value. */
.s3-hm-rules-head .s3-lede{
  max-width:52ch; font-size:17px; line-height:1.62;
  color:color-mix(in srgb, var(--accent-ink) 74%, transparent);
}

/* padding-inline is .s3-wrap's job; only the block axis and the marker are
   reset here, or the gutter would be stripped off the list. */
.s3-hm-rules-list{ list-style:none; margin-block:0; padding-block:0 }

.s3-hm-rule{
  display:grid; grid-template-columns:minmax(0, 1.02fr) minmax(0, 1fr);
  align-items:start; gap:clamp(14px, 3vw, 76px);
  padding-block:clamp(26px, 2.9vw, 44px);
  border-top:1px solid var(--hairline);
}
.s3-hm-rule:last-child{ border-bottom:1px solid var(--hairline) }

/* The statement. This is the size the four rules were always worth. */
.s3-hm-rule-t{
  margin:0; max-width:13ch;
  font-size:clamp(26px, 3.05vw, 46px); line-height:1.04; letter-spacing:-.038em;
  font-weight:500;
}
.s3-hm-rule-b{ display:flex; flex-direction:column; gap:14px; max-width:52ch }
.s3-hm-rule-b p{
  margin:0; font-size:16px; line-height:1.65;
  color:color-mix(in srgb, var(--accent-ink) 76%, transparent);
}
/* The one mono role: a readout beside a statement. Not every rule has one, and
   that is deliberate — an even grid of four is what this band was rescued from.
   Two classes, because `.s3-hm-rule-b p` above outranks a single one and would
   otherwise set this line in body size and body ink. */
.s3-hm-rule-b .s3-hm-rule-spec{
  color:color-mix(in srgb, var(--accent-ink) 56%, transparent);
  font-size:12.5px; line-height:1.5;
}

@media (max-width:900px){
  .s3-hm-rules-head{ grid-template-columns:minmax(0,1fr); align-items:start }
  .s3-hm-rules-head .s3-h2{ max-width:16ch }
}
/* Below this the two columns of a row would each be too narrow to set type in,
   so the statement sits over its detail and keeps its display size. */
@media (max-width:760px){
  .s3-hm-rule{ grid-template-columns:minmax(0,1fr); gap:14px }
  .s3-hm-rule-t{ max-width:18ch }
  .s3-hm-rule-b{ max-width:56ch }
}
@media (max-width:480px){
  .s3-hm-rule{ padding-block:24px }
  .s3-hm-rule-t{ font-size:26px; letter-spacing:-.03em; max-width:none }
}


/* ═══════════════════════════ 7 · WHAT IT COSTS ═══════════════════════════
   rail-left, split, chrome cropped by the right edge and the fold · --bg

   The ladder and the packs, one band. They answer the same question and
   splitting them put a second invoice on the page. */

.s3-hm-studio{
  position:relative;
  background:var(--bg);
  padding-block:var(--band);
  overflow:hidden;                 /* this is what crops the app */
  border-bottom:1px solid var(--hairline);
}
.s3-hm-studio-grid{
  display:grid; align-items:start;
  grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(32px, 4vw, 72px);
}
.s3-hm-studio-copy{ display:flex; flex-direction:column; gap:22px; padding-top:8px }
.s3-hm-studio-copy .s3-lede{ max-width:42ch }
.s3-hm-studio-foot{ color:var(--muted); max-width:40ch; line-height:1.7; font-size:13px }

/* -- the packs, as a price strip rather than three sales cards ------------ */
.s3-hm-pack-strip{
  list-style:none; margin:4px 0 0; padding:0;
  display:grid; grid-template-columns:repeat(3, minmax(0,1fr));
  gap:1px; background:var(--hairline);
  border:1px solid var(--hairline); border-radius:var(--r-card);
  overflow:hidden;
}
.s3-hm-pack{ background:var(--surface-1); min-width:0 }
.s3-hm-pack > a{
  display:flex; flex-direction:column; gap:6px; height:100%;
  padding:18px 16px 20px; text-decoration:none; color:inherit;
  transition:background var(--t-quick) var(--ease);
}
.s3-hm-pack > a:hover{ background:var(--surface-2) }
.s3-hm-pack-name{ font-size:14px; font-weight:500; letter-spacing:-.01em; color:var(--text) }
.s3-hm-pack-price{ font-size:26px; line-height:1.05; letter-spacing:-.035em; font-weight:500 }
.s3-hm-pack-price .woocommerce-Price-amount{ font-weight:500 }
.s3-hm-pack-price del{ opacity:.4; font-size:.6em; margin-right:6px }
.s3-hm-pack-price ins{ text-decoration:none }
/* Token counts are live wallet figures. */
.s3-hm-pack-tokens{ color:var(--accent) }
.s3-hm-pack-each{ color:var(--faint); margin-top:auto; padding-top:6px }
.s3-hm-packs-note{ color:var(--faint); line-height:1.6 }

/* Browser chrome. It runs past the container's right edge and past the band's
   bottom edge, so it reads as the application rather than a picture of one. */
.s3-hm-chrome{
  /* How far the panel runs past the container on the right. Everything inside
     is padded by the same amount, so the SURFACE is cut by the viewport and the
     CONTENT never is — a figure that scrolls off the edge is just a bug. */
  --s3-hm-bleed:clamp(24px, 9vw, 190px);

  position:relative;
  margin-right:calc(-1 * var(--s3-hm-bleed));
  /* Cut through the caption row, not through the thumbnails. A deeper crop
     took the top off the renders and read as a clipping bug rather than as an
     application running past the fold. */
  margin-bottom:calc(-1 * clamp(74px, 7vw, 128px));
  background:var(--surface-2);
  border:1px solid var(--hairline);
  border-radius:var(--r-card);
  overflow:hidden;
  transform-origin:50% 100%;
}
.s3-hm-chrome-bar{
  display:flex; align-items:center;
  padding:10px 14px;
  padding-right:calc(14px + var(--s3-hm-bleed));
  border-bottom:1px solid var(--hairline);
  background:var(--surface-3);
}
.s3-hm-chrome-addr{
  display:block; flex:1; min-width:0;
  padding:6px 12px; border-radius:var(--r-pill);
  background:var(--bg); border:1px solid var(--hairline);
  color:var(--faint); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.s3-hm-app{
  padding:clamp(18px, 2vw, 26px);
  padding-right:calc(clamp(18px, 2vw, 26px) + var(--s3-hm-bleed));
}

/* Tabs. A real switcher doing navigational work, in place of an eyebrow. */
.s3-hm-tabs{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:22px }
.s3-hm-tab{
  appearance:none; cursor:pointer;
  padding:8px 14px; border-radius:var(--r-chip);
  background:transparent; border:1px solid var(--hairline);
  font-family:var(--font); font-size:13px; font-weight:500; line-height:1;
  color:var(--muted);
  transition:color var(--t-quick) var(--ease), border-color var(--t-quick) var(--ease), background var(--t-quick) var(--ease);
}
.s3-hm-tab:hover{ color:var(--text); border-color:var(--hairline-2) }
.s3-hm-tab.is-on{
  color:var(--accent); border-color:var(--accent);
  background:color-mix(in srgb, var(--accent) 7%, transparent);
}

.s3-hm-panel[hidden]{ display:none }

.s3-hm-input{ display:flex; flex-direction:column; gap:8px; padding-bottom:20px }
.s3-hm-prompt{
  font-size:15px; line-height:1.5; color:var(--muted);
  padding:14px 16px; border-radius:var(--r-chip);
  background:var(--bg); border:1px solid var(--hairline);
}

/* The cost ladder itself. */
.s3-hm-steps{ list-style:none; margin:0; padding:0; border-top:1px solid var(--hairline) }
.s3-hm-step{ border-bottom:1px solid var(--hairline) }
.s3-hm-step,
.s3-hm-step-toggle{
  display:grid; align-items:baseline;
  grid-template-columns:minmax(0,1fr) auto auto;
  gap:8px 18px;
  padding:15px 2px;
  width:100%; text-align:left;
}
.s3-hm-step.has-toggle{ padding:0 }
.s3-hm-step-toggle{
  appearance:none; cursor:pointer; background:none; border:0; color:inherit;
  font-family:var(--font); font-size:inherit;
  border-radius:var(--r-chip);
  transition:background var(--t-quick) var(--ease);
}
.s3-hm-step-toggle:hover{ background:var(--glass-1) }
.s3-hm-step-name{ font-size:15px; font-weight:500; letter-spacing:-.01em }
.s3-hm-step-toggle .s3-hm-step-name{ position:relative; padding-left:38px }
/* The switch: a real control state, so it may carry the accent. Pill radius —
   the only radius a control this small is allowed. */
.s3-hm-step-toggle .s3-hm-step-name::before{
  content:''; position:absolute; left:0; top:.16em;
  width:26px; height:15px; border-radius:var(--r-pill);
  border:1px solid var(--hairline-2); background:transparent;
  transition:background var(--t-quick) var(--ease), border-color var(--t-quick) var(--ease);
}
.s3-hm-step-toggle .s3-hm-step-name::after{
  content:''; position:absolute; left:4px; top:calc(.16em + 4px);
  width:9px; height:9px; border-radius:var(--r-pill);
  background:var(--faint);
  transition:transform var(--t-quick) var(--ease), background var(--t-quick) var(--ease);
}
.s3-hm-step-toggle.is-on .s3-hm-step-name::before{ background:var(--accent); border-color:var(--accent) }
.s3-hm-step-toggle.is-on .s3-hm-step-name::after{ background:var(--accent-ink); transform:translateX(11px) }
.s3-hm-step-toggle:not(.is-on){ color:var(--faint) }
.s3-hm-step-toggle:not(.is-on) .s3-hm-step-cost{ color:var(--faint) }

/* Token costs are live figures, so they get the accent. */
.s3-hm-step-cost{ color:var(--accent); white-space:nowrap; font-weight:400 }
.s3-hm-step-time{ color:var(--faint); white-space:nowrap; min-width:6ch; text-align:right }

.s3-hm-total{
  display:flex; align-items:baseline; justify-content:space-between; gap:18px;
  padding:18px 2px 0;
}
.s3-hm-total-val{ color:var(--accent); font-size:19px; font-weight:400 }

.s3-hm-measured{ margin-top:14px; color:var(--faint); line-height:1.6; max-width:56ch }

/* The output row is what the crop cuts through. */
.s3-hm-out{
  display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:12px;
  margin-top:26px; padding-top:22px; border-top:1px solid var(--hairline);
}
.s3-hm-out-item{ display:flex; flex-direction:column; gap:9px; min-width:0; margin:0 }
.s3-hm-out-item img{
  width:100%; aspect-ratio:1; object-fit:cover;
  border-radius:var(--r-chip); border:1px solid var(--hairline);
  background:var(--bg); filter:saturate(.5);
}
.s3-hm-out-item figcaption{
  color:var(--faint); line-height:1.5;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}

@media (max-width:980px){
  .s3-hm-studio-grid{ grid-template-columns:1fr }
  .s3-hm-chrome{
    --s3-hm-bleed:var(--gutter);
    margin-bottom:calc(-1 * clamp(64px, 9vw, 110px));
  }
  .s3-hm-out{ grid-template-columns:repeat(3, minmax(0,1fr)) }
  .s3-hm-out-item:nth-child(4){ display:none }
}
@media (max-width:600px){
  /* Three tiles stacked is three lots of padding. Each becomes a spec row:
     name and tokens on the left, price on the right. */
  .s3-hm-pack-strip{ grid-template-columns:minmax(0,1fr) }
  .s3-hm-pack > a{
    display:grid; grid-template-columns:minmax(0,1fr) auto;
    align-items:baseline; gap:2px 14px; padding:13px 14px;
  }
  .s3-hm-pack-name{ grid-column:1 }
  .s3-hm-pack-price{ grid-column:2; grid-row:1 / span 2; font-size:22px; align-self:center }
  .s3-hm-pack-tokens{ grid-column:1 }
  .s3-hm-pack-each{ grid-column:1; margin-top:0; padding-top:0 }
  .s3-hm-step, .s3-hm-step-toggle{ grid-template-columns:minmax(0,1fr) auto }
  .s3-hm-step-time{ grid-column:2; text-align:right }
  .s3-hm-out{ grid-template-columns:repeat(2, minmax(0,1fr)) }
  .s3-hm-out-item:nth-child(3){ display:none }
  /* On a phone the app panel is the whole column width, so its row rhythm can
     come in — desktop's 15px step padding was 50px of slack down the band. */
  .s3-hm-app{ padding:15px }
  .s3-hm-step, .s3-hm-step-toggle{ padding-block:12px }
  .s3-hm-input{ padding-bottom:14px }
  .s3-hm-total{ padding-top:14px }
  .s3-hm-out{ margin-top:18px; padding-top:16px }
}


/* ═══════════════════════════ 8 · THE QUESTIONS ═══════════════════════════
   two-pane reader, full measure · --surface-1

   Was: a heading and five accordion rows in the left half with the right half
   of the page empty. Now: the questions are an index down the left and the
   selected answer fills the pane on the right, so the band occupies its own
   width and opening a question fills the void instead of creating one.

   No JavaScript. A radio group carries the selection; :has() on the grid maps
   the checked radio to its pane. Where :has() is unsupported the @supports
   block never applies and every pane is simply on the page at once — the same
   five answers, stacked, which is a layout rather than a failure. */

.s3-hm-qa{ background:var(--surface-1); padding-block:var(--band) }

.s3-hm-qa-grid{
  display:grid;
  grid-template-columns:minmax(0, .88fr) minmax(0, 1.12fr);
  grid-template-rows:auto auto;
  align-items:start; gap:clamp(28px, 3.4vw, 72px);
}
.s3-hm-qa-index{ grid-column:1; grid-row:1; display:flex; flex-direction:column }
.s3-hm-qa-panes{ grid-column:2; grid-row:1; align-self:stretch; min-width:0 }

.s3-hm-qa-head{ max-width:13ch; margin:0 0 clamp(24px, 2.6vw, 38px) }

/* ------------------------------------------------------------- the index -- */

.s3-hm-qa-list{ border-top:1px solid var(--hairline) }
.s3-hm-qa-row{ position:relative; border-bottom:1px solid var(--hairline) }

/* Focusable, never display:none — a hidden radio that cannot take focus takes
   the keyboard with it. */
.s3-hm-qa-radio{
  position:absolute; width:1px; height:1px; margin:0;
  opacity:0; pointer-events:none;
}
.s3-hm-qa-row label{
  display:block; cursor:pointer;
  padding:clamp(17px, 1.5vw, 22px) 26px clamp(17px, 1.5vw, 22px) 0;
  font-size:17px; font-weight:500; letter-spacing:-.018em; line-height:1.35;
  color:var(--muted);
  transition:color var(--t-quick) var(--ease);
}
.s3-hm-qa-row label:hover{ color:var(--text) }

/* The active state of a control — which is one of the two things accent is
   for. One question is active at a time, so this is one accent instance. */
.s3-hm-qa-radio:checked + label{ color:var(--accent) }
.s3-hm-qa-radio:checked + label::after{
  content:""; position:absolute; right:2px; top:50%;
  width:16px; height:1px; margin-top:-1px;
  background:var(--accent);
}
.s3-hm-qa-radio:focus-visible + label{
  outline:2px solid var(--accent); outline-offset:-2px; border-radius:2px;
}

/* -------------------------------------------------------------- the pane -- */

.s3-hm-qa-panes{
  display:flex; flex-direction:column;
  background:var(--surface-2);
  border:1px solid var(--hairline); border-radius:var(--r-card);
  min-height:clamp(320px, 30vw, 470px);
}
.s3-hm-qa-pane{
  display:flex; flex-direction:column; gap:18px;
  padding:clamp(26px, 2.6vw, 44px);
}
.s3-hm-qa-q{
  margin:0; max-width:22ch;
  font-size:clamp(22px, 1.9vw, 30px); line-height:1.12; letter-spacing:-.03em;
  font-weight:500; color:var(--text);
}
/* The answer is the band's body copy, not a footnote, and it is the thing that
   has to fill the pane — a short answer set at 17px left 200px of nothing
   inside it. Bigger type on a shorter measure fills the same box with the same
   words. */
.s3-hm-qa-a{
  margin:0; max-width:42ch;
  font-size:clamp(17px, 1.4vw, 20px); line-height:1.62; color:var(--muted);
}
/* The pane's foot: the readout on the left, the surface that proves the answer
   on the right. It is pinned to the bottom edge, which is what stops a short
   answer leaving the pane half-empty — and the link is the point of the band,
   since every one of these answers is checkable somewhere on this site. */
.s3-hm-qa-plate{
  margin-top:auto; padding-top:20px;
  border-top:1px solid var(--hairline);
  display:flex; flex-wrap:wrap; align-items:baseline;
  justify-content:space-between; gap:10px 20px;
}
.s3-hm-qa-spec{ font-size:13px; color:var(--faint) }
.s3-hm-qa-go{
  flex:0 0 auto;
  font-size:14px; font-weight:500; color:var(--muted); text-decoration:none;
  transition:color var(--t-quick) var(--ease);
}
.s3-hm-qa-go:hover{ color:var(--text) }
.s3-hm-qa-go::after{ content:" →" }

/* One selected pane where the browser can correlate the radio with it. The
   base state above leaves every pane visible, which is the fallback. */
@supports selector(:has(*)){
  .s3-hm-qa-pane{ display:none }
  .s3-hm-qa-grid:has(.s3-hm-qa-r0:checked) .s3-hm-qa-pane:nth-child(1),
  .s3-hm-qa-grid:has(.s3-hm-qa-r1:checked) .s3-hm-qa-pane:nth-child(2),
  .s3-hm-qa-grid:has(.s3-hm-qa-r2:checked) .s3-hm-qa-pane:nth-child(3),
  .s3-hm-qa-grid:has(.s3-hm-qa-r3:checked) .s3-hm-qa-pane:nth-child(4),
  .s3-hm-qa-grid:has(.s3-hm-qa-r4:checked) .s3-hm-qa-pane:nth-child(5),
  .s3-hm-qa-grid:has(.s3-hm-qa-r5:checked) .s3-hm-qa-pane:nth-child(6){
    display:flex; flex:1 1 auto;
  }
}

/* -------------------------------------------------------------- the foot -- */

.s3-hm-qa-foot{
  grid-column:1 / -1; grid-row:2; margin:0;
  padding-top:clamp(20px, 2vw, 30px);
  border-top:1px solid var(--hairline);
  font-size:15px; line-height:1.6; color:var(--muted);
}
.s3-hm-qa-foot a{
  color:var(--text); text-decoration:none;
  border-bottom:1px solid var(--hairline);
  transition:border-color var(--t-quick) var(--ease);
}
.s3-hm-qa-foot a:hover{ border-color:var(--hairline-2) }

@media (max-width:900px){
  .s3-hm-qa-grid{ grid-template-columns:minmax(0,1fr) }
  .s3-hm-qa-index{ grid-column:1; grid-row:1 }
  .s3-hm-qa-panes{ grid-column:1; grid-row:2; min-height:0 }
  .s3-hm-qa-foot{ grid-row:3 }
}
@media (max-width:560px){
  .s3-hm-qa-row label{ font-size:16px; padding-right:22px }
  .s3-hm-qa-a{ font-size:16px }
}

/* ═══════════════════════════ motion ═══════════════════════════

   Scroll entrances marked [data-hm-rise] and [data-hm-split] are hidden ONLY
   while <html> carries .s3-hm-motion, which the inline script in front-page.php
   adds when JavaScript is alive and reduced motion is not requested — and
   removes again if home.js has not reported for duty within four seconds.
   home.js drops it too if GSAP's ticker never advances. There is no path where
   a stylesheet on its own leaves content at opacity 0.

   The site-wide entrances (.s3-anim) belong to main.js and are not restated. */

/* `:not(.is-in)` is load-bearing, not decoration. The entrance tween ends with
   clearProps, which strips the inline opacity it was holding at 1 — and without
   this the rule below would immediately re-hide the element it had just
   revealed. home.js adds .is-in before it tweens, so by the time clearProps
   runs the rule no longer matches. Every path that reveals content statically
   adds the same class. */
.s3-hm-motion [data-hm-rise]:not(.is-in),
.s3-hm-motion [data-hm-split]:not(.is-in){ opacity:0 }

/* The per-word mask home.js builds inside a [data-hm-split] heading. Written
   here rather than inline so the shape of the reveal is a stylesheet decision;
   the spans do not exist until the script makes them. */
.s3-hm-w{ display:inline-block; overflow:hidden; vertical-align:top }
.s3-hm-w > span{ display:inline-block }

@media (prefers-reduced-motion: reduce){
  /* The reactor never starts, so the hero is simply the finished render and its
     four real figures. Nothing hides, nothing pins, nothing blinks. */
  .s3-hm-hero{ min-height:0 }
  .s3-hm-hero.is-live .s3-hm-stage-poster{ opacity:1 }
  .s3-hm-caret,
  .s3-hm-hero.is-live .s3-hm-caret{ display:none }
  .s3-hm-hero.is-live .s3-hm-stage-row,
  .s3-hm-hero.is-live .s3-hm-stage-row.is-on{ opacity:1; background:none }
  .s3-hm-stage-poster, .s3-hm-stage model-viewer, .s3-hm-canvas{ transition:none }
  [data-hm-rise], [data-hm-split]{ opacity:1 !important; transform:none !important }

  /* Belt and braces: .s3-hm-motion is never written under reduced motion, so
     the pinned rules cannot match — but if the preference is flipped mid-session
     these unwind whatever was already on the element. */
  .s3-hm-q.is-pinned, .s3-hm-flow.is-pinned{ height:auto !important }
  .s3-hm-q-inner, .s3-hm-flow-inner{ position:static !important; height:auto !important }
  .s3-hm-q-shot, .s3-hm-q-maps, .s3-hm-q-plate, .s3-hm-q-note{
    position:static !important; opacity:1 !important;
  }
  .s3-hm-q-wire{ display:none !important }
  .s3-hm-q-shot img, .s3-hm-q-map{
    transform:none !important; opacity:1 !important; clip-path:none !important;
  }
  .s3-hm-flow-track{ transform:none !important }

  /* The two mobile compositions, unwound the same way. Neither can be built
     under the preference — home.js never reaches pinned() — but a mid-session
     flip has to find them harmless. */
  .s3-hm-q.is-wired .s3-hm-q-step{ opacity:1 !important }
  .s3-hm-flow.is-swipe .s3-hm-flow-card{ opacity:1 !important; clip-path:none !important }
  .s3-hm-flow.is-swipe .s3-hm-flow-n,
  .s3-hm-flow.is-swipe .s3-hm-flow-name{ transform:none !important }
  .s3-hm-flow-view{ scroll-snap-type:x proximity !important }
}
