/* Self-hosted variable faces: Google Sans Flex (display) + Inter (body) */
@font-face{font-family:"Google Sans Flex";font-style:normal;font-weight:100 1000;font-display:swap;
  src:url("../fonts/google-sans-flex.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:normal;font-weight:100 900;font-display:swap;
  src:url("../fonts/inter-roman.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:italic;font-weight:100 900;font-display:swap;
  src:url("../fonts/inter-italic.woff2") format("woff2")}
/* ==== tokens/ground.css ==== */
/* Ground and ink.

   Two grounds, not two themes. `paper` is the default and the house canvas. `slab` is a
   real dark ground used for exactly two things: full-bleed media blocks, and the Delivery
   console. It is not a night mode and there is no toggle.

   Every ratio below is measured against WCAG AA (4.5 body / 3.0 large), not eyeballed.
   Values were solved to pass, then written down — several are two steps darker than they
   "looked right" because the measurement said so. */

:root{
  /* ---- Paper ground ------------------------------------------------------ */
  --paper:#FFFFFF;        /* the house canvas — white (TK: no paper tan)           */
  --paper-sunk:#F4F4F3;   /* recessed panels, table stripes, inset wells            */
  --paper-raised:#FFFFFF; /* the one surface that sits above paper — menus, dialogs */
  --rule:#E4E4E2;         /* hairlines and column rules. 1.50 — never carries text  */
  --rule-firm:#C9C9C6;    /* the emphasised rule, for a division that matters       */

  --ink:#14170F;          /* 15.89 — body and display                              */
  --ink-mid:#4A4F42;      /*  7.40 — secondary text                                */
  --ink-quiet:#686D5D;    /*  4.68 — tertiary, captions, disabled labels           */

  /* ---- Slab ground ------------------------------------------------------- */
  --slab:#373737;         /* TK: charcoal, not near-black. The dark ground.        */
  --slab-raised:#434343;  /* a panel lifted off slab — by tone, never by shadow    */
  --rule-on-slab:#525252; /* hairline on charcoal — lighter than the ground        */
  --rule-firm-on-slab:#666666;

  --quiet-on-slab:#A7A7A7;/*  4.95 on #373737 — tertiary on dark                   */

  /* ---- Green ------------------------------------------------------------- */
  /* The green is a PLATE, not an accent. It appears as large flat fill carrying
     a label, the way plant signage colour-codes a panel. Either big or absent.
     Never a hairline, never a glow, never a 1px rail.

     Measured, and this is the rule that fell out of it: pure white on the brand
     green is 2.24 and #F2F0E9 is 1.96 — nothing light can pass at any size.
     Therefore GREEN PLATES ALWAYS CARRY INK. Ink on plate measures 8.10. */
  --plate:#8CBC43;        /* the brand green, measured off the supplied mark        */
  --plate-fg:var(--ink);  /* what a plate's text is. ALWAYS paired with --plate —   */
                          /* hardcoding ink in the component made the plate         */
                          /* invisible the moment a band re-pointed --plate to ink. */
  --plate-deep:#6E9A2E;   /* the pressed/active plate                              */
  --plate-ink:#4E6D18;    /*  5.23 — green as TEXT on paper, where a plate is too loud */
  --plate-on-slab:#8CBC43;/*  8.67 — the same green reads correctly on dark        */
  --plate-wash:#E4EFD2;   /* the only tint permitted: a selected row on paper      */

  /* ---- Signal ------------------------------------------------------------ */
  /* Colour is spent on structure and on failure. Nothing else. There is no
     "success green" — success is the absence of a flag, and live state is motion
     (see motion.css). */
  --flag:#C8391A;         /*  4.54 on paper — failure                              */
  --flag-on-slab:#E8664A; /*  5.94 on slab                                         */
  --flag-wash:#F7E2DD;
  --hold:#8F5B06;         /*  5.02 on paper — waiting, warning, needs you          */
  --hold-on-slab:#D99413; /*  7.57 on slab                                         */
  --hold-wash:#F6EBD8;

  /* ---- Alphas, for edges over unknown ground ----------------------------- */
  --ink-a06:rgba(20,23,15,.06);
  --ink-a12:rgba(20,23,15,.12);
  --ink-a24:rgba(20,23,15,.24);
  --ink-a72:rgba(20,23,15,.72);
  --paper-a08:rgba(255,255,255,.08);
  --paper-a16:rgba(255,255,255,.16);
  --paper-a72:rgba(255,255,255,.72);

  /* ---- Resolved roles ---------------------------------------------------- */
  /* Product code reads these. They flip inside [data-ground="slab"] below, which
     is how one component renders correctly on either ground with no variant prop. */
  --bg:var(--paper);
  --bg-sunk:var(--paper-sunk);
  --bg-raised:var(--paper-raised);
  --fg:var(--ink);
  --fg-mid:var(--ink-mid);
  --fg-quiet:var(--ink-quiet);
  --edge:var(--rule);
  --edge-firm:var(--rule-firm);
  --signal-fail:var(--flag);
  --signal-hold:var(--hold);
}

/* The PLATE BAND — green as a whole section, which is what the rule meant.

   "Green is either big or absent" cannot be honoured by a 90px chip. A plate
   band is an entire full-bleed section in brand green carrying black type, the
   way a plant colour-codes a whole panel rather than putting a green sticker on
   a grey one. A page should hit one of these, once, and it should be impossible
   to miss.

   Rules on green are ink at low alpha, never a lighter or darker green — a green
   rule on a green field is mud, and --plate-deep against --plate measures 1.4. */
.sp-band-plate{
  --bg:var(--plate);
  --bg-sunk:#84B23E;
  --bg-raised:#96C455;
  --fg:var(--ink);
  --fg-mid:rgba(20,23,15,.74);
  --fg-quiet:rgba(20,23,15,.58);
  --edge:rgba(20,23,15,.22);
  --edge-firm:rgba(20,23,15,.42);
  /* A plate ON the green band inverts: ink field, paper text. Both halves move
     together — that is the entire reason --plate-fg exists. */
  --plate:var(--ink);
  --plate-fg:var(--paper);
  --plate-wash:rgba(20,23,15,.10);
  --signal-fail:#5E1305;
  background:var(--plate,#8CBC43);
  color:var(--ink);
}
/* The band's own background has to be literal — --plate is re-pointed above. */
.sp-band-plate{background:#8CBC43}

/* The slab scope — ONE definition, four ways in.

   Put any of these on an element and everything inside renders for dark ground,
   including components that know nothing about it.

     [data-ground="slab"]        explicit, for a one-off block
     .sp-slab                    a dark band inside a paper page
     .sp-field                   the WebGL hero (see registers.css for its paint)
     [data-register="delivery"]  the console, whole-surface

   This list is deliberately one rule rather than four copies. It started as
   copies, and .sp-field was missed: the hero's secondary button resolved
   --fg to ink and rendered near-invisible on the dark ground. A duplicated
   token map will always drift like that eventually. */
[data-ground="slab"],
.sp-slab,
.sp-field,
.sp-panel-ink,
[data-register="delivery"]{
  --bg:var(--slab);
  --bg-sunk:var(--slab);
  --bg-raised:var(--slab-raised);
  --fg:var(--paper);
  --fg-mid:#B9BEAE;
  --fg-quiet:var(--quiet-on-slab);
  --edge:var(--rule-on-slab);
  --edge-firm:var(--rule-firm-on-slab);
  --signal-fail:var(--flag-on-slab);
  --signal-hold:var(--hold-on-slab);
  --plate-wash:#414A33;   /* selected-row tint, lifted onto the charcoal ground    */
  --flag-wash:#4A342E;
  --hold-wash:#463A1F;
  color:var(--fg);
  background:var(--bg);
}


/* ==== tokens/type.css ==== */
/* Type.

   Archivo carries everything. Its width axis (62–125) is the identity mechanic:
   display lines fill their measure exactly by varying WIDTH, never by letter-spacing
   and never by changing size. See lib/justify.js.

   Instrument Serif appears only in words a person actually said. Spout never speaks
   in the serif.

   Sizes are chosen per role, not generated from a ratio. Body is 15.5px rather than
   15 — a deliberate half-pixel up, because most Spout readers are on a phone in
   daylight and the extra is worth more than the tidiness of a round number. */

:root{
  /* ---- Width axis --------------------------------------------------------- */
  /* The five stops the system actually sets. justify.js interpolates freely
     between --wdth-tight and --wdth-full; everything else snaps to these. */
  --wdth-tight:68;    /* display, dense tabular heads                            */
  --wdth-narrow:84;   /* subheads, dense UI                                      */
  --wdth-normal:100;  /* body, controls — the resting width                      */
  --wdth-wide:112;    /* editorial calm, lede paragraphs                         */
  --wdth-full:125;    /* the wall. One per page at most.                         */

  /* ---- Weight ------------------------------------------------------------- */
  --w-light:300;
  --w-book:400;
  --w-medium:500;
  --w-semi:600;
  --w-heavy:800;

  /* ---- Size — chosen per role -------------------------------------------- */
  --t-micro:11px;     /* rail numbers, version stamp, table meta                 */
  --t-fine:12.5px;    /* captions, helper text, legend                           */
  --t-small:14px;     /* dense UI, table cells, secondary controls               */
  --t-base:15.5px;    /* body                                                    */
  --t-lede:20px;      /* one lead paragraph per page                             */
  --t-h3:24px;
  /* Big type sizes against its CONTAINER, not the viewport.

     This is not a preference. Width-justification only reads as a type wall when
     each line holds roughly 3–5 words. Size against the viewport and a headline
     in a 520px column inside a 1280px page gets 110px type, falls to one word per
     line, and cannot be justified at all — measured, and it looked broken.

     `cqi` is 1% of the nearest query container's inline size. Where no container
     exists the units fall back to the small viewport, which is the old behaviour,
     so this degrades rather than breaks. Wrap a justified headline in .sp-block. */
  --t-h2:clamp(26px,4.4cqi,44px);
  --t-h1:clamp(34px,7.4cqi,78px);
  --t-display:clamp(38px,11.2cqi,132px);

  /* ---- Leading ------------------------------------------------------------ */
  --lead-block:0.88;  /* display blocks — lines nearly touch, the block is a shape */
  --lead-head:1.06;
  --lead-snug:1.26;   /* subheads, card titles                                   */
  --lead-body:1.5;    /* generous. Read on a phone, in sunlight.                 */
  --lead-dense:1.35;  /* table rows, log lines                                   */

  /* ---- Tracking — changes with size, as it must -------------------------- */
  /* Large type needs negative tracking or it falls apart; small type needs
     positive or it closes up. These are per-role, applied by the roles below. */
  --track-display:-0.03em;
  --track-h1:-0.022em;
  --track-h2:-0.016em;
  --track-h3:-0.01em;
  --track-body:0em;
  --track-small:0.005em;
  --track-micro:0.02em;

  /* ---- Figures ------------------------------------------------------------ */
  /* Tabular everywhere a number sits in a column. Slashed zero on the machine
     face so 0 and O never confuse in a run ID. */
  --figures-tabular:"tnum" 1,"lnum" 1;
  --figures-machine:"tnum" 1,"zero" 1;
}

/* ---- Roles ---------------------------------------------------------------- */
/* Apply as classes. Every one sets family, size, leading, tracking and width
   together — there is no way to take the size without the optical correction. */

.sp-display{
  font-family:var(--face-spine);
  font-size:var(--t-display);
  font-weight:var(--w-heavy);
  font-stretch:calc(var(--wdth-tight) * 1%);
  line-height:var(--lead-block);
  letter-spacing:var(--track-display);
  text-wrap:balance;
}
.sp-h1{
  font-family:var(--face-spine);
  font-size:var(--t-h1);
  font-weight:var(--w-semi);
  font-stretch:calc(var(--wdth-narrow) * 1%);
  line-height:var(--lead-head);
  letter-spacing:var(--track-h1);
  text-wrap:balance;
}
.sp-h2{
  font-family:var(--face-spine);
  font-size:var(--t-h2);
  font-weight:var(--w-semi);
  font-stretch:calc(var(--wdth-narrow) * 1%);
  line-height:var(--lead-snug);
  letter-spacing:var(--track-h2);
  text-wrap:balance;
}
.sp-h3{
  font-family:var(--face-spine);
  font-size:var(--t-h3);
  font-weight:var(--w-medium);
  font-stretch:100%;
  line-height:var(--lead-snug);
  letter-spacing:var(--track-h3);
}
.sp-lede{
  font-family:var(--face-spine);
  font-size:var(--t-lede);
  font-weight:var(--w-light);
  font-stretch:calc(var(--wdth-wide) * 1%);
  line-height:var(--lead-body);
  letter-spacing:var(--track-body);
  max-width:38ch;
}
.sp-body{
  font-family:var(--face-spine);
  font-size:var(--t-base);
  font-weight:var(--w-book);
  font-stretch:100%;
  line-height:var(--lead-body);
  letter-spacing:var(--track-body);
  max-width:62ch;
}
.sp-small{
  font-family:var(--face-spine);
  font-size:var(--t-small);
  font-weight:var(--w-book);
  line-height:var(--lead-dense);
  letter-spacing:var(--track-small);
}
.sp-fine{
  font-family:var(--face-spine);
  font-size:var(--t-fine);
  font-weight:var(--w-book);
  line-height:var(--lead-dense);
  letter-spacing:var(--track-small);
  color:var(--fg-quiet);
}
.sp-micro{
  font-family:var(--face-spine);
  font-size:var(--t-micro);
  font-weight:var(--w-medium);
  line-height:1.2;
  letter-spacing:var(--track-micro);
  font-variant-numeric:tabular-nums lining-nums;
}

/* The serif. Words a person actually said — nothing else, ever.
   Optical size correction is manual: Instrument Serif has no opsz axis, so the
   italic gets a shade more tracking at small sizes to stop it closing up. */
.sp-said{
  font-family:var(--face-said);
  font-weight:400;
  line-height:1.22;
  letter-spacing:-0.012em;
  font-size:clamp(24px,3.2vw,42px);
}
.sp-said-sm{
  font-family:var(--face-said);
  font-weight:400;
  line-height:1.36;
  letter-spacing:0.004em;
  font-size:var(--t-lede);
}
.sp-said em,.sp-said-sm em{font-style:italic}

/* The machine. Delivery register only — requires tokens/fonts-delivery.css. */
.sp-machine{
  font-family:var(--face-machine);
  font-size:var(--t-small);
  font-weight:var(--w-book);
  line-height:var(--lead-dense);
  letter-spacing:-0.02em;   /* Martian Mono runs wide; pull it back in tables */
  font-feature-settings:var(--figures-machine);
}
.sp-machine-micro{
  font-family:var(--face-machine);
  font-size:var(--t-micro);
  font-weight:var(--w-book);
  line-height:1.3;
  letter-spacing:-0.01em;
  font-feature-settings:var(--figures-machine);
}

/* Numbers in columns align. Always. */
.sp-figures{font-variant-numeric:tabular-nums lining-nums}

/* Hanging punctuation — quotes and bullets sit outside the measure so the
   left edge stays optically straight. Supported in Safari; harmless elsewhere. */
.sp-hang{hanging-punctuation:first last}


/* ==== tokens/grid.css ==== */
/* Structure — the rail and the field.

   An asymmetric two-part grid. A fixed left RAIL carries section numbers, labels
   and plates. A FIELD beside it carries content. Column rules are visible
   hairlines, as on a drawing sheet.

   The rail is what makes a marketing page and a console page recognisably the
   same system: on Studio it holds section numbers, on Delivery the run index, on
   Everyday it collapses to a top strip.

   Spacing is a chosen sequence, not a 4px ladder — roughly ×1.44, tuned so the
   rail rhythm and the type scale land on the same values. Where a component needs
   13px, it writes 13px. */

:root{
  --s-1:2px;
  --s-2:4px;
  --s-3:6px;
  --s-4:9px;
  --s-5:13px;
  --s-6:18px;
  --s-7:26px;
  --s-8:38px;
  --s-9:54px;
  --s-10:78px;
  --s-11:112px;
  --s-12:160px;

  /* ---- The rail ----------------------------------------------------------- */
  --rail-w:clamp(64px,9vw,138px);
  --rail-gap:clamp(18px,2.4vw,38px);

  /* ---- Page ---------------------------------------------------------------- */
  --gutter:clamp(18px,4vw,54px);
  --page-max:1512px;
  --measure:62ch;      /* body */
  --measure-lede:38ch; /* lede — deliberately shorter, it is read once */

  /* ---- Section rhythm ------------------------------------------------------ */
  /* Asymmetric on purpose: a section opens tighter than it closes, so the plate
     label at the top sits close to what it labels. */
  --sect-top:clamp(54px,7vw,112px);
  --sect-bottom:clamp(78px,10vw,160px);

  /* ---- Corner radii --------------------------------------------------------
     The instrument register is square. There is one radius, for controls, and it
     is small enough to read as a manufacturing chamfer rather than a soft corner.
     Circles are reserved for things that are genuinely circular. */
  --r-control:3px;
  --r-circle:999px;
}

/* Query containers.

   Display type sizes in `cqi` — 1% of the nearest container's inline size — so a
   headline in a narrow column is sized for that column and stays justifiable.
   Every element that establishes a column of text establishes a container.

   .sp-block is the explicit one: wrap any width-justified headline in it. */
.sp-block,.sp-page,.sp-section,.sp-field{container-type:inline-size}

/* The page frame. */
.sp-page{
  max-width:var(--page-max);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

/* Rail + field. Below 860px the rail collapses to a top strip and the field
   takes the full measure — most Spout readers are on a phone. */
.sp-spread{
  display:grid;
  grid-template-columns:var(--rail-w) minmax(0,1fr);
  gap:var(--rail-gap);
  align-items:start;
}
@media (max-width:860px){
  .sp-spread{grid-template-columns:minmax(0,1fr);gap:var(--s-6)}
}

.sp-rail{
  display:flex;flex-direction:column;gap:var(--s-5);
  position:sticky;top:var(--s-7);
}
@media (max-width:860px){
  .sp-rail{position:static;flex-direction:row;align-items:center;gap:var(--s-5)}
}

/* A section. Opens tight under its plate, closes generously. */
.sp-section{padding-block:var(--sect-top) var(--sect-bottom)}

/* Visible column rules, drawing-sheet style. The rule is on the LEFT of the
   field so the rail reads as a margin annotation, not as a column. */
.sp-ruled{border-inline-start:1px solid var(--edge);padding-inline-start:var(--rail-gap)}
@media (max-width:860px){
  .sp-ruled{border-inline-start:0;border-block-start:1px solid var(--edge);
            padding-inline-start:0;padding-block-start:var(--s-5)}
}

/* The oversized section number that lives in the rail. */
.sp-index{
  font-family:var(--face-spine);
  font-size:clamp(30px,4.2vw,60px);
  font-weight:var(--w-heavy);
  font-stretch:calc(var(--wdth-tight) * 1%);
  line-height:.82;
  letter-spacing:-0.04em;
  font-variant-numeric:tabular-nums lining-nums;
  color:var(--fg);
  opacity:.22;
}

/* Full-bleed media escapes the page frame. Square corners, always.

   `100vw` includes the vertical scrollbar, so this overflows by the scrollbar
   width on every desktop page that scrolls — measured at 150px of horizontal
   scroll before the clip below was added. `overflow-x:clip` on the root absorbs
   it WITHOUT creating a scroll container, which `overflow-x:hidden` would, and
   which would break every position:sticky header in the system. */
.sp-bleed{
  width:100vw;
  margin-inline:calc(50% - 50vw);
  border-radius:0;
}
html{overflow-x:clip}


/* ==== tokens/motion.css ==== */
/* Motion — the Hilfiker signature.

   Hans Hilfiker's Swiss station clock sweeps its second hand in 58.5 seconds, then
   HOLDS at twelve, waiting for the master minute impulse. Arrive early, then hold,
   then commit.

   One curve. Three durations, each with a physical claim. No arbitrary values —
   if a transition needs a duration that is not one of these three, the transition
   is wrong, not the scale.

   And the rule that separates this system from every console ever built:
   LIVE STATE IS MOTION, NEVER COLOUR. Running things move. Stopped things are
   still. Nothing is green because it is working. */

:root{
  /* ---- The curve --------------------------------------------------------- */
  /* Most of the distance covered fast, then a long settle. The only easing in
     the system for anything that moves in space. */
  --ease-commit:cubic-bezier(.2,.9,.15,1);
  /* For things leaving, which should not linger. */
  --ease-leave:cubic-bezier(.6,0,.9,.35);

  /* ---- The three durations ----------------------------------------------- */
  --t-ack:120ms;      /* the machine acknowledged you — hover, press, toggle    */
  --t-move:320ms;     /* something moved in space — panel, drawer, tab, row     */
  --t-arrive:900ms;   /* something arrived from off-screen — reveal, hero, page */

  --t-hold:90ms;      /* the wait for the minute pulse. Stagger between siblings. */

  /* ---- Live-state cycles -------------------------------------------------- */
  --t-travel:2400ms;  /* the travelling hairline on a live row                  */
  --t-caret:1060ms;   /* the streaming caret. Not 1000 — an odd period stops it  */
                      /* syncing with anything else on screen and reads alive.  */

  /* ---- Composed transitions ---------------------------------------------- */
  --tr-ack:color var(--t-ack) var(--ease-commit),
           background-color var(--t-ack) var(--ease-commit),
           border-color var(--t-ack) var(--ease-commit),
           font-variation-settings var(--t-ack) var(--ease-commit);
  --tr-move:transform var(--t-move) var(--ease-commit),
            opacity var(--t-move) var(--ease-commit);
  --tr-arrive:transform var(--t-arrive) var(--ease-commit),
              opacity var(--t-arrive) var(--ease-commit);

  --rise:22px;        /* reveals translate up by this. Never scale from zero.   */
}

/* ---- Live state ----------------------------------------------------------- */

/* A hairline that travels the width of its container and holds at the end.
   This is what "running" looks like. It carries no colour information — it
   works in monochrome and for a colour-blind reader. */
@keyframes sp-travel{
  0%   {transform:translateX(-100%)}
  62%  {transform:translateX(0)}      /* arrive early…                          */
  78%  {transform:translateX(0)}      /* …hold…                                 */
  100% {transform:translateX(100%)}   /* …then commit                           */
}

/* The streaming caret. A block that does not blink on/off — it breathes, which
   reads as generation rather than as a text cursor waiting for input. */
@keyframes sp-caret{
  0%,100%{opacity:1}
  50%    {opacity:.28}
}

/* Staged reveal. Opacity plus a rise. Nothing scales up from zero, ever. */
@keyframes sp-rise{
  from{opacity:0;transform:translateY(var(--rise))}
  to  {opacity:1;transform:translateY(0)}
}

/* The mark's own turn, used on the loading indicator. It rotates, arrives early
   and holds — the clock gesture at object scale. */
@keyframes sp-sweep{
  0%   {transform:rotate(0deg)}
  62%  {transform:rotate(330deg)}
  78%  {transform:rotate(330deg)}
  100% {transform:rotate(360deg)}
}

.sp-live-rule{position:relative;overflow:hidden}
.sp-live-rule::after{
  content:"";position:absolute;inset-inline:0;bottom:0;height:1px;
  background:currentColor;opacity:.55;
  animation:sp-travel var(--t-travel) var(--ease-commit) infinite;
}
.sp-caret{
  display:inline-block;width:.52em;height:1.02em;vertical-align:-.16em;
  background:currentColor;margin-inline-start:.14em;
  animation:sp-caret var(--t-caret) var(--ease-commit) infinite;
}
.sp-reveal{animation:sp-rise var(--t-arrive) var(--ease-commit) both}

/* ---- Reduced motion -------------------------------------------------------
   Zeroes the three durations and stops the live cycles. Live state then has to
   be legible without movement, so .sp-live-rule falls back to a solid rule and
   the caret goes solid. This is why live state must never have been colour in
   the first place — the fallback has somewhere to go. */
@media (prefers-reduced-motion:reduce){
  :root{--t-ack:0ms;--t-move:0ms;--t-arrive:0ms;--t-hold:0ms;--rise:0px}
  .sp-live-rule::after{animation:none;transform:none;opacity:.9}
  .sp-caret{animation:none;opacity:1}
  .sp-reveal{animation:none}
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important}
}


/* ==== tokens/state.css ==== */
/* Interaction states — designed, not a lightness step.

   The green is a plate, so the states use plates. Hover does not tint a
   background by 4%: a green plate WIPES IN from the inline start, behind the
   label, on the commit curve. It is a transform, so nothing reflows and nothing
   repaints the text.

   Press seats the control 1px down rather than scaling it — a scale on a square
   instrument reads as a wobble.

   Nothing glows. Ever. */

:root{
  --focus-w:2px;
  --focus-gap:2px;
}

/* ---- Focus ----------------------------------------------------------------
   One ring, on every focusable thing, in the resolved foreground. It therefore
   reads correctly on paper (ink), on slab (paper) and on a green plate (ink,
   measured 8.10). Never a browser outline, never a glow, never green — green
   is structural and a green ring on a green plate would vanish. */
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:var(--focus-w) solid var(--fg);
  outline-offset:var(--focus-gap);
  border-radius:var(--r-control);
}

/* ---- The wipe -------------------------------------------------------------
   Put .sp-wipe on any interactive element that should acknowledge a pointer.
   The plate lives in a pseudo-element behind the content and is revealed by
   scaleX from the inline start. */
.sp-wipe{
  position:relative;
  isolation:isolate;
  transition:var(--tr-ack);
}
.sp-wipe::before{
  content:"";
  position:absolute;
  inset:0;
  z-index:-1;
  background:var(--plate);
  transform:scaleX(0);
  transform-origin:left center;
  transition:transform var(--t-ack) var(--ease-commit),background-color var(--t-ack) var(--ease-commit);
}
.sp-wipe:hover::before,
.sp-wipe:focus-visible::before{transform:scaleX(1)}
/* The wipe's plate carries the plate's own foreground. On paper that is ink
   (8.10 — the only legal combination, since paper on plate is 1.96). Inside a
   green band, where --plate re-points to ink, it follows to paper. */
.sp-wipe:hover,
.sp-wipe:focus-visible{color:var(--plate-fg)}
.sp-wipe:active::before{background:var(--plate-deep)}
.sp-wipe:active{transform:translateY(1px)}

@media (prefers-reduced-motion:reduce){
  .sp-wipe::before{transition:none}
}

/* ---- The plate control ----------------------------------------------------
   A control that IS a plate cannot use the wipe — there is no ground left for
   the plate to arrive over. It deepens instead, and seats on press, so the
   gesture still reads as the same machine. */
.sp-plate-btn,.sp-fail-btn{transition:var(--tr-ack),transform var(--t-ack) var(--ease-commit)}
.sp-plate-btn:hover:not(:disabled),
.sp-plate-btn:focus-visible:not(:disabled){background:var(--plate-deep)}
/* A destructive control deepens within its OWN hue. Sharing the plate hover
   would turn the delete button green on the way to being pressed. */
.sp-fail-btn:hover:not(:disabled),
.sp-fail-btn:focus-visible:not(:disabled){filter:brightness(.88)}
.sp-plate-btn:active:not(:disabled),
.sp-fail-btn:active:not(:disabled){transform:translateY(1px)}

/* ---- Selected -------------------------------------------------------------
   A wash plus a 3px plate rail on the inline start. Never a full green fill —
   a green row would put the whole table into the accent budget. */
.sp-selected{
  background:var(--plate-wash);
  box-shadow:inset 3px 0 0 0 var(--plate);
}

/* ---- Disabled -------------------------------------------------------------
   Not 50% opacity on the whole control — that greys the label and the border
   equally and reads as a rendering fault. A sunk fill, quiet text, and a DASHED
   edge, which ties to the Slot and Empty language: the shape is there, it is
   just not filled yet. */
.sp-disabled,
:where(button,input,select,textarea)[disabled]{
  background:var(--bg-sunk);
  color:var(--fg-quiet);
  border-style:dashed;
  cursor:not-allowed;
}
.sp-disabled .sp-wipe::before,
:where(button,input,select,textarea)[disabled]::before{display:none}

/* ---- Touch ----------------------------------------------------------------
   44px minimum on anything a thumb reaches. Character is not an excuse for a
   32px tap target on a phone.

   Control heights are TOKENS, not literals in the components, and this is why:
   components set their height with an inline style, and an inline style beats
   any stylesheet rule — so a `@media (pointer:coarse)` rule that tries to raise
   a 34px button to 44px silently loses. Measured on the homepage at 375px: five
   buttons at 34px, with the coarse-pointer rule present and doing nothing.

   Referencing a token from the inline style inverts that. The component writes
   `min-height: var(--h-sm)`, and the media query below owns what --h-sm means. */
:root{
  --h-sm:34px;
  --h-md:44px;
  --h-lg:54px;
  --h-chip:32px;
  --h-nav:40px;
  --h-input:48px;
}
@media (pointer:coarse){
  :root{
    --h-sm:44px;
    --h-chip:44px;
    --h-nav:44px;
  }
}
.sp-touch{min-height:var(--h-md);min-width:var(--h-md)}


/* ==== tokens/registers.css ==== */
/* The three registers.

   One spine, three registers. The spine — type system, rail, easing signature,
   mark, numerals, state grammar — never changes. The register re-tunes ground,
   density, measure and motion budget for a class of surface.

   These are documented composition modes, not a runtime theme toggle. A surface
   declares its register once, on <body>, and does not switch.

       <body data-register="studio">      marketing, proposals, case work
       <body data-register="delivery">    console, runs, telemetry
       <body data-register="everyday">    own-product consumer apps            */

/* ---- Studio ---------------------------------------------------------------
   Paper ground with slab media blocks. Generous. Full motion budget — this is
   the only register where a WebGL hero is permitted. */
[data-register="studio"]{
  --row-h:52px;
  --pad-block:var(--s-6);
  --pad-inline:var(--s-7);
  --stack:var(--s-7);
  --measure:62ch;
}

/* ---- Delivery -------------------------------------------------------------
   Slab ground. Dense and tabular — an operator scans this, they do not read it.
   Motion is spent almost entirely on live state; decorative reveals are off.
   Requires tokens/fonts-delivery.css for the machine face. */
/* The dark token map lives in ground.css, which lists this selector alongside
   .sp-slab and .sp-field. Only the density and motion budget belong here. */
[data-register="delivery"]{
  --row-h:34px;                    /* a log line, not a list item              */
  --pad-block:var(--s-3);
  --pad-inline:var(--s-5);
  --stack:var(--s-5);
  --measure:78ch;                  /* wider — this is scanned, not read        */
  --sect-top:var(--s-7);
  --sect-bottom:var(--s-8);
  --t-arrive:var(--t-move);        /* nothing takes 900ms in an operator's way */

  background:var(--bg);
  color:var(--fg);
}

/* ---- Everyday -------------------------------------------------------------
   Paper ground, consumer product. Density comes from GoPay, not from Western
   minimalism: everything visible at once, nothing hidden behind a menu, because
   a hidden action reads to a first-time user as a missing feature.
   Touch targets go up, not down. */
[data-register="everyday"]{
  --row-h:56px;                    /* thumb-sized                              */
  --pad-block:var(--s-5);
  --pad-inline:var(--s-6);
  --stack:var(--s-6);
  --measure:54ch;                  /* narrower — read on a phone in one hand   */
  --t-base:16.5px;                 /* up a full point for daylight legibility  */
  --sect-top:var(--s-8);
  --sect-bottom:var(--s-9);
}

/* The field — the hero's ground, Studio register only.

   The gradient here is not decoration, it is the FALLBACK. lib/field.js paints a
   canvas over it; where WebGL is unavailable, the script is blocked, or the user
   has asked for reduced motion, this is what remains — and it is a finished
   image on its own, dark at the bottom so headline type sits clean on it.

   Never put a field on a Delivery or Everyday surface. An operator does not need
   a moving background behind a run log. */
.sp-field{
  position:relative;
  isolation:isolate;
  background:
    radial-gradient(120% 92% at 22% 8%, #22301A 0%, #101609 46%, var(--slab) 78%),
    var(--slab);
  color:var(--paper);
  overflow:hidden;
}
.sp-field > *:not(canvas){position:relative;z-index:1}
/* The protection gradient. Text over media ALWAYS gets a bottom-up scrim —
   never a flat wash over the whole frame, never a blurred capsule behind a
   headline. */
.sp-field::after{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(to top, rgba(8,10,6,.92) 0%, rgba(8,10,6,.55) 26%, rgba(8,10,6,0) 62%);
}

/* A slab block inside a Studio page — the media band, the console preview, the
   footer. Carries grain, because a large dark field will band without it.
   Its token map comes from ground.css; only the paint is here. */
.sp-slab{
  background:var(--slab);
  color:var(--paper);
}


/* ==== tokens/base.css ==== */
/* Reset and element defaults.

   Deliberately small. The system does not restyle every element — it sets the
   ground, the type, and the few defaults that would otherwise fight the tokens. */

*,*::before,*::after{box-sizing:border-box}

html{
  -webkit-text-size-adjust:100%;
  text-size-adjust:100%;
  scroll-behavior:smooth;
}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;
  background:var(--bg);
  color:var(--fg);
  font-family:var(--face-spine);
  font-size:var(--t-base);
  font-weight:var(--w-book);
  line-height:var(--lead-body);
  font-synthesis-weight:none;      /* never fake a weight — the axis is real   */
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
}

h1,h2,h3,h4,h5,h6,p,figure,blockquote{margin:0}
ul,ol{margin:0;padding:0;list-style:none}

img,svg,video,canvas{display:block;max-width:100%;height:auto}

/* Links carry a 1px underline offset far enough to clear the descenders, and
   thicken rather than change colour on hover — colour is spent on structure. */
a{
  color:inherit;
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.22em;
  text-decoration-color:var(--edge-firm);
  transition:text-decoration-color var(--t-ack) var(--ease-commit),
             text-decoration-thickness var(--t-ack) var(--ease-commit);
}
a:hover{text-decoration-thickness:2px;text-decoration-color:var(--fg)}

button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:0;padding:0;cursor:pointer}

hr{border:0;border-top:1px solid var(--edge);margin:0}

table{border-collapse:collapse;width:100%}
th{text-align:start;font-weight:var(--w-medium)}

::selection{background:var(--plate);color:var(--ink)}

/* Skip link — visible only when focused, and it lands where it says it does. */
.sp-skip{
  position:absolute;inset-inline-start:-9999px;top:var(--s-4);
  background:var(--plate);color:var(--ink);
  padding:var(--s-4) var(--s-6);border-radius:var(--r-control);
  z-index:100;font-weight:var(--w-medium);
}
.sp-skip:focus{inset-inline-start:var(--s-4)}

/* Screen-reader-only, for the text that gives a live state its name when the
   motion has been turned off. */
.sp-sr{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

/* Grain. One overlay, on slab grounds only, at low strength. It is the only
   texture in the system and it exists to stop large dark fields banding. */
.sp-grain{position:relative;isolation:isolate}
.sp-grain::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  opacity:.055;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}


/* ==== tokens/kit.css ==== */
/* The kit register — additive layer for the spout.co.zm site surfaces.

   Everything here is derived from the Figma read in research/kit-notes.md:
   rounded panels on a light canvas, the green word-highlight, lifted cards,
   the giant off-canvas mark. Spout colours only; the kit's palette died at
   the border.

   Link order on a site surface:
     styles.css  →  tokens/fonts-inter.css  →  tokens/kit.css                 */

:root{
  --r-panel:24px;    /* section containers — measured off the kit             */
  --r-card:12px;     /* cards inside panels                                   */
  --r-media:12px;    /* photos                                                */

  /* The kit's one shadow ("Category Hover", 0/12/32 @ 10% ink), as a ramp.   */
  --lift-1:0 4px 14px rgba(20,23,15,.08);
  --lift-2:0 12px 32px rgba(20,23,15,.10);
  --lift-3:0 22px 54px rgba(20,23,15,.16);
}

/* ---- The word-highlight -------------------------------------------------- */
/* The kit puts an accent blob behind one word per heading. Here it is a green
   plate that WIPES IN (scaleX, commit curve) when the heading enters the
   viewport — the system's hover gesture promoted to a scroll moment.
   Ink text on green: measured 8.10. The plate sits behind the word, slightly
   oversized, rotated a breath so it reads placed rather than programmatic. */
.sp-hl{position:relative;display:inline-block;white-space:nowrap}
.sp-hl::before{
  content:"";position:absolute;inset:-0.06em -0.18em -0.02em -0.14em;z-index:-1;
  background:var(--plate);border-radius:8px;transform:scaleX(0) rotate(-1.2deg);
  transform-origin:left center;
  transition:transform 480ms var(--ease-commit);
}
.sp-hl.is-in::before{transform:scaleX(1) rotate(-1.2deg)}
/* No-JS and reduced-motion: the plate is simply there. */
@media (prefers-reduced-motion:reduce){
  .sp-hl::before{transition:none;transform:scaleX(1) rotate(-1.2deg)}
}
.no-js .sp-hl::before{transform:scaleX(1) rotate(-1.2deg)}

/* ---- Panels -------------------------------------------------------------- */
/* The kit's big rounded ink containers on the light canvas. Token map flips
   via the slab scope in ground.css — .sp-panel-ink is listed there. */
.sp-panel{border-radius:var(--r-panel);overflow:hidden;position:relative}
.sp-panel-ink{background:var(--slab);color:var(--paper)}

/* ---- Cards that lift ----------------------------------------------------- */
.sp-lift{transition:transform var(--t-ack) var(--ease-commit),box-shadow var(--t-ack) var(--ease-commit)}
.sp-lift:hover{transform:translateY(-4px);box-shadow:var(--lift-2)}

/* ---- Photos -------------------------------------------------------------- */
.sp-photo{display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit}
.sp-photo-frame{border-radius:var(--r-media);overflow:hidden;position:relative;
                background:var(--bg-sunk)}
/* Text over a photo always sits on this scrim — contrast by construction. */
.sp-photo-scrim::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top, rgba(8,10,6,.88) 0%, rgba(8,10,6,.42) 44%, rgba(8,10,6,.06) 78%);
}
/* Hover zoom inside a fixed frame — the classic classy move, one only. */
.sp-photo-frame:hover .sp-photo{transform:scale(1.03)}
.sp-photo{transition:transform 600ms var(--ease-commit)}

/* ---- The giant off-canvas mark ------------------------------------------- */
/* The kit's hero circle, as Spout's ring+disc. Positioned by the surface;
   the halo is a soft green bloom that must never read as a glow effect on
   text — it sits behind the mark only. */
.sp-mark-giant{position:absolute;pointer-events:none;z-index:0}
.sp-mark-giant .halo{
  position:absolute;inset:-12%;border-radius:999px;
  background:radial-gradient(closest-side, rgba(140,188,67,.34), rgba(140,188,67,0) 70%);
}

/* ---- Reveal-on-scroll ---------------------------------------------------- */
/* Marker class + IntersectionObserver (lib/kit.js). Until observed, hidden
   and risen; .is-in plays the entry. Stagger via --d per element. */
.sp-in{opacity:0;transform:translateY(26px);
       transition:opacity 700ms var(--ease-commit),transform 700ms var(--ease-commit);
       transition-delay:var(--d,0ms)}
.sp-in.is-in{opacity:1;transform:none}
.no-js .sp-in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .sp-in{opacity:1;transform:none;transition:none}
}

/* ---- Scroll-linked, exactly two ------------------------------------------ */
/* CSS scroll-driven animations; inert where unsupported. 1: hero mark drift. */
@supports (animation-timeline: scroll()) {
  @media not (prefers-reduced-motion:reduce){
    .sp-drift{animation:sp-drift-y linear both;animation-timeline:scroll();
              animation-range:0vh 120vh}
    @keyframes sp-drift-y{from{transform:translateY(0)}to{transform:translateY(9%)}}
    /* 2: the footer line tracks in from the side as its band scrolls into view */
    .sp-track-in{animation:sp-track-x linear both;animation-timeline:view();
                 animation-range:entry 0% entry 90%}
    @keyframes sp-track-x{from{transform:translateX(6%);opacity:.2}to{transform:none;opacity:1}}
  }
}

/* ---- Sticky nav state ---------------------------------------------------- */
/* Transparent over the hero; paper + hairline once scrolled (lib/kit.js flips
   data-scrolled on <body>). */
.sp-nav{position:sticky;top:0;z-index:40;background:transparent;
        border-bottom:1px solid transparent;
        transition:background-color var(--t-move) var(--ease-commit),
                   border-color var(--t-move) var(--ease-commit)}
[data-scrolled] .sp-nav{background:var(--paper);border-bottom-color:var(--rule)}

/* ---- Count-up ------------------------------------------------------------ */
/* Numbers set in the markup are the FINAL values (no-JS truth); lib/kit.js
   animates from zero when observed. Tabular so nothing shifts. */
.sp-count{font-variant-numeric:tabular-nums lining-nums}

/* ---- View transitions between pages (WP) --------------------------------- */
@view-transition{navigation:auto}
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none !important}
}


/* ==== tokens/fonts-inter.css ==== */
/* Site faces — Google Sans Flex for display, Inter for body (TK's call,
   2026-08-17, replacing the earlier all-Inter direction). Both self-hosted in
   the WP bundle; this hosted import exists for comp preview parity only. */


:root{
  --face-display:"Google Sans Flex","Google Sans",-apple-system,"Helvetica Neue",Arial,sans-serif;
  --face-spine:"Inter",-apple-system,"Helvetica Neue",Helvetica,Arial,sans-serif;
  --face-said:"Inter",-apple-system,"Helvetica Neue",Helvetica,Arial,sans-serif;

  --track-display:-0.022em;
  --track-h1:-0.018em;
  --track-h2:-0.014em;
}

/* Quotes: Inter Medium italic. The "words a person actually said" rule
   survives as an italic voice. */
.sp-said,.sp-said-sm{font-style:italic;font-weight:500;letter-spacing:-0.008em}


/* ==== surfaces/home/sections.css ==== */
/* spout.co.zm — sections.css
   The comp's composition layer, written to port VERBATIM into the WP theme
   (building-wp-sites doctrine: all fidelity in theme.json tokens + this file).
   Every section maps to a kit-notes.md row; comments carry the mapping.
   Tokens come from styles.css + fonts-inter.css + kit.css. Spout colours only. */

/* ---- headings follow their ground (WP override guard) -------------------- */
/* WordPress global styles pin every h1–h6 to --wp--preset--color--ink with an
   element-level rule. That overrides the light colour our dark grounds set on
   themselves — inheritance loses to a direct element rule — and every heading on
   a slab panel rendered near-black on near-black (measured 1.16:1 on the live
   site). A heading must take the colour of the ground it sits on, so restore the
   inheritance with class-level specificity that wins regardless of source order.
   No-op in the comp (no WP sheet); the fix only bites under WordPress. */
.launch.ink :is(h1,h2,h3,h4,h5,h6),
.plan.ink :is(h1,h2,h3,h4,h5,h6),
.sp-panel-ink :is(h1,h2,h3,h4,h5,h6),
.cta :is(h1,h2,h3,h4,h5,h6),
.ask :is(h1,h2,h3,h4,h5,h6),
.foot :is(h1,h2,h3,h4,h5,h6){color:inherit}

/* ---- page scaffold ------------------------------------------------------- */
.wrap{max-width:1140px;margin-inline:auto;padding-inline:clamp(18px,4vw,32px)}
.section{padding-block:clamp(56px,8vw,104px)}
.eyebrow{display:block;font:600 13px/1.3 var(--face-spine);letter-spacing:.02em;
         color:var(--plate-ink);margin-bottom:14px}
.h-sec{font:700 clamp(30px,4.6vw,52px)/1.08 var(--face-display);
       letter-spacing:var(--track-h1);color:var(--fg);text-wrap:balance}
.sub{font:400 var(--t-base)/var(--lead-body) var(--face-spine);color:var(--fg-mid);
     max-width:56ch;margin-top:16px}

/* ---- nav (kit row 0) ----------------------------------------------------- */
.nav-in{display:flex;align-items:center;gap:26px;min-height:72px}
.nav-links{display:flex;gap:26px;margin-inline-start:auto;align-items:center}
.nav-links a{text-decoration:none;font:500 15px/1 var(--face-spine);color:var(--fg)}
.nav-links a:hover{color:var(--plate-ink)}
.burger{display:none}
@media (max-width:860px){
  .nav-links .opt{display:none}
  /* 44px burger to the site-library floor; the flyout below is the go-live-gated
     mobile menu, ported verbatim into the WP theme. */
  .burger{display:grid;place-items:center;gap:0;width:44px;height:44px;
          background:none;border:0;cursor:pointer;padding:0}
  .burger span{display:block;width:22px;height:2px;background:var(--fg);
               margin-block:2.5px;transition:transform var(--t-move) var(--ease-commit),
               opacity var(--t-ack) var(--ease-commit)}
  [data-menu-open] .burger span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  [data-menu-open] .burger span:nth-child(2){opacity:0}
  [data-menu-open] .burger span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
}
.flyout{position:fixed;inset:0;z-index:60;background:var(--slab);color:var(--paper)}
.flyout-in{display:flex;flex-direction:column;gap:8px;padding:96px 28px 28px}
.flyout-in > a{font:700 clamp(26px,7vw,34px)/1.3 var(--face-spine);color:var(--paper);
  text-decoration:none;padding-block:10px;
  opacity:0;transform:translateY(18px);
  transition:opacity 500ms var(--ease-commit),transform 500ms var(--ease-commit);
  transition-delay:var(--d,0ms)}
.flyout-in > a.btn{font-size:17px;color:var(--ink);margin-top:18px;align-self:flex-start}
[data-menu-open] .flyout-in > a{opacity:1;transform:none}
[data-menu-open]{overflow:hidden}
@media (prefers-reduced-motion:reduce){
  .flyout-in > a{opacity:1;transform:none;transition:none}
  .burger span{transition:none}
}

/* ---- hero (kit row 1: 3-line stack + giant off-canvas circle) ------------ */
.hero{position:relative;overflow:clip;padding-block:clamp(64px,10vw,128px) 0}
.hero-mark{top:-16%;inset-inline-end:-13%;width:min(56vw,780px);aspect-ratio:1}
.hero-mark svg{position:relative;width:100%;height:100%;display:block}
.hero-copy{position:relative;z-index:1;max-width:880px}
/* Sized to the measure, verified: the longest line, "custom-built, from", must
   hold as ONE line at the cap inside the 880px column. 92px wrapped every span
   and pushed the sub 300px below the fold — measured, not guessed. */
.hero-h1{font:700 clamp(38px,5.9vw,78px)/1.05 var(--face-display);
         letter-spacing:var(--track-display);color:var(--fg)}
.hero-sub{font:400 clamp(16px,1.6vw,19px)/1.55 var(--face-spine);
          color:var(--fg-mid);max-width:46ch;margin-top:22px}
.hero-cta{display:flex;gap:14px;flex-wrap:wrap;margin-top:34px}
/* trust text-marks along the hero's bottom — the kit's logo strip, honest */
.trust{display:flex;flex-wrap:wrap;gap:14px 40px;align-items:baseline;
       margin-top:clamp(44px,7vw,84px);padding-block:22px;
       border-top:1px solid var(--rule)}
.trust b{font:700 15px/1.3 var(--face-spine);color:var(--fg);white-space:nowrap}
.trust span{font:400 13px/1.3 var(--face-spine);color:var(--fg-quiet)}

/* ---- buttons -------------------------------------------------------------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
     min-height:var(--h-md);padding-inline:22px;border-radius:10px;
     font:600 15.5px/1 var(--face-spine);text-decoration:none;cursor:pointer;
     border:1px solid transparent;transition:var(--tr-ack),transform var(--t-ack) var(--ease-commit)}
.btn-plate{background:var(--plate);color:var(--ink)}
.btn-plate:hover{background:var(--plate-deep);transform:translateY(-1px)}
.btn-ink{background:var(--fg);color:var(--bg)}
.btn-ink:hover{transform:translateY(-1px)}
.btn-line{background:transparent;color:var(--fg);border-color:var(--edge-firm)}
.btn-line:hover{border-color:var(--fg)}
.btn-lg{min-height:54px;padding-inline:28px;font-size:17px}
.btn .arr{transition:transform var(--t-ack) var(--ease-commit)}
.btn:hover .arr{transform:translateX(3px)}

/* ---- services (kit row 2: dot list + media) ------------------------------- */
.services{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
          gap:clamp(28px,5vw,72px);align-items:center}
@media (max-width:860px){.services{grid-template-columns:minmax(0,1fr)}}
.svc-list{display:flex;flex-direction:column;margin-top:10px}
.svc{display:flex;align-items:center;gap:18px;padding-block:17px;
     border-bottom:1px solid var(--rule);text-decoration:none;color:var(--fg)}
.svc:last-child{border-bottom:0}
.svc .dot{width:14px;height:14px;border-radius:999px;background:var(--plate);flex:none;
          transition:transform var(--t-ack) var(--ease-commit)}
.svc:hover .dot{transform:scale(1.5)}
.svc b{font:600 clamp(19px,2.2vw,24px)/1.2 var(--face-spine)}
.svc span{font:400 14px/1.4 var(--face-spine);color:var(--fg-quiet);margin-inline-start:auto;
          text-align:end;max-width:22ch}
.services .sp-photo-frame{aspect-ratio:4/3;box-shadow:var(--lift-2)}

/* ---- launches (kit row 3: word-highlight + 2×2 alternating cards) --------- */
.launch-head{display:flex;align-items:flex-end;justify-content:space-between;
             gap:26px;flex-wrap:wrap;margin-bottom:38px}
.launch-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(430px,100%),1fr));gap:22px}
.launch{border-radius:var(--r-panel);padding:clamp(24px,3vw,38px);min-height:300px;
        display:flex;flex-direction:column;gap:12px;position:relative;overflow:hidden}
.launch.ink{background:var(--slab);color:var(--paper)}
.launch.paper{background:var(--paper-raised);border:1px solid var(--rule)}
.launch .k{font:600 12.5px/1.3 var(--face-spine);opacity:.62}
.launch .t{font:600 clamp(22px,2.6vw,30px)/1.15 var(--face-display);letter-spacing:-.02em}
.launch p{font:400 14.5px/1.55 var(--face-spine);opacity:.78;max-width:44ch}
.launch .foot{margin-top:auto;display:flex;align-items:center;gap:10px;
              font:600 14px/1 var(--face-spine)}
.launch .rings{position:absolute;inset-inline-end:-60px;bottom:-60px;opacity:.16;pointer-events:none}

/* ---- stats (kit row 4: shapes + counters) --------------------------------- */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
       gap:clamp(24px,4vw,54px)}
.stat .shape{height:66px;display:flex;align-items:flex-end;margin-bottom:16px;color:var(--plate)}
.stat .n{font:700 clamp(38px,4.6vw,58px)/1 var(--face-display);letter-spacing:-.03em;
         color:var(--fg);font-variant-numeric:tabular-nums lining-nums}
.stat .l{font:500 15px/1.4 var(--face-spine);color:var(--fg-mid);margin-top:8px}

/* ---- audiences (kit row 5: three big ink panels + chips) ------------------ */
.aud-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(340px,100%),1fr));gap:22px;
          margin-top:38px}
.aud{display:flex;flex-direction:column;min-height:430px}
.aud .media{position:relative;flex:1;min-height:210px}
.aud .media img{position:absolute;inset:0}
.aud .body{padding:clamp(22px,2.6vw,30px);display:flex;flex-direction:column;gap:11px}
.aud .t{font:600 clamp(20px,2.2vw,25px)/1.15 var(--face-display)}
.aud p{font:400 14.5px/1.55 var(--face-spine);opacity:.8}
.aud .chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:6px}
.chip{display:inline-flex;align-items:center;min-height:30px;padding:4px 12px;
      border-radius:999px;border:1px solid var(--edge);
      font:500 12.5px/1 var(--face-spine);color:inherit}

/* ---- pricing (kit row 7, the real three + Mission Control) ---------------- */
.price-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));
            gap:22px;margin-top:38px;align-items:stretch}
.plan{border-radius:var(--r-panel);padding:clamp(24px,2.8vw,34px);
      display:flex;flex-direction:column;gap:14px;position:relative}
.plan.paper{background:var(--paper-raised);border:1px solid var(--rule)}
.plan.ink{background:var(--slab);color:var(--paper);box-shadow:var(--lift-3)}
.plan .name{display:flex;align-items:center;gap:10px;
            font:600 20px/1.2 var(--face-display)}
.plan .flag{font:600 11.5px/1 var(--face-spine);background:var(--plate);color:var(--ink);
            padding:6px 10px;border-radius:999px}
.plan .for{font:400 14px/1.45 var(--face-spine);opacity:.72}
.plan .price{font:700 clamp(40px,4.6vw,54px)/1 var(--face-display);letter-spacing:-.03em;
             font-variant-numeric:tabular-nums lining-nums}
.plan .terms{font:500 13px/1.4 var(--face-spine);opacity:.66}
.plan ul{display:flex;flex-direction:column;gap:9px;margin-block:8px;padding:0;list-style:none}
.plan li{display:flex;gap:10px;font:400 14px/1.5 var(--face-spine);opacity:.85}
.plan li svg{flex:none;margin-top:3px}
.plan .btn{margin-top:auto}
.plan .save{font:500 12.5px/1.4 var(--face-spine);opacity:.6}
.mission{display:flex;align-items:center;justify-content:space-between;gap:22px;
         flex-wrap:wrap;margin-top:22px;padding:clamp(22px,2.6vw,30px);
         border:1px dashed var(--edge-firm);border-radius:var(--r-panel)}
.mission b{font:700 18px/1.3 var(--face-spine)}
.mission p{font:400 14px/1.5 var(--face-spine);color:var(--fg-mid);max-width:52ch}

/* ---- founding client (copy §1.9 — replaces fake testimonials) ------------- */
.founding{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);
          gap:clamp(28px,5vw,72px);align-items:center}
@media (max-width:860px){.founding{grid-template-columns:minmax(0,1fr)}}
.founding blockquote{font:500 italic clamp(22px,2.8vw,32px)/1.35 var(--face-spine);
                     letter-spacing:-.01em;color:var(--fg);margin:0}
.founding .sp-photo-frame{aspect-ratio:4/5;max-height:440px;box-shadow:var(--lift-2)}

/* ---- FAQ (kit row 8: accordion + mini form) -------------------------------- */
.faq-grid{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);
          gap:clamp(28px,5vw,64px);align-items:start;margin-top:38px}
@media (max-width:900px){.faq-grid{grid-template-columns:minmax(0,1fr)}}
.faq details{border-bottom:1px solid var(--rule)}
.faq summary{display:flex;align-items:center;justify-content:space-between;gap:18px;
             padding-block:19px;cursor:pointer;list-style:none;
             font:600 16.5px/1.4 var(--face-spine);color:var(--fg)}
.faq summary::-webkit-details-marker{display:none}
.faq summary .x{flex:none;width:22px;height:22px;position:relative;color:var(--plate-ink)}
.faq summary .x::before,.faq summary .x::after{content:"";position:absolute;background:currentColor;
  inset-inline:3px;top:10px;height:2px;transition:transform var(--t-move) var(--ease-commit)}
.faq summary .x::after{transform:rotate(90deg)}
.faq details[open] summary .x::after{transform:rotate(0deg)}
.faq .a{padding-block:0 20px;font:400 15px/1.6 var(--face-spine);color:var(--fg-mid);max-width:60ch}
.ask{background:var(--slab);color:var(--paper);border-radius:var(--r-panel);
     padding:clamp(24px,2.8vw,32px);display:flex;flex-direction:column;gap:14px}
.ask b{font:700 20px/1.25 var(--face-spine)}
.ask p{font:400 14px/1.5 var(--face-spine);opacity:.72}
.ask input,.ask textarea{width:100%;min-height:48px;padding:12px 14px;border-radius:10px;
  border:1px solid var(--rule-firm-on-slab);background:var(--slab-raised);color:var(--paper);
  font:400 15px/1.4 var(--face-spine)}
.ask textarea{min-height:96px;resize:vertical}
.ask input:focus-visible,.ask textarea:focus-visible{outline:2px solid var(--paper);outline-offset:2px}

/* ---- CTA band (kit row 9 head: photo + overlay) ---------------------------- */
.cta{position:relative;border-radius:var(--r-panel);overflow:hidden;min-height:380px;
     display:flex;align-items:flex-end}
.cta > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.cta .in{position:relative;z-index:1;padding:clamp(28px,4vw,54px);color:var(--paper);
         display:flex;flex-wrap:wrap;align-items:flex-end;gap:26px;width:100%}
.cta-copy{flex:1 1 360px;display:flex;flex-direction:column;gap:14px}
.cta h2{font:700 clamp(34px,5vw,58px)/1.05 var(--face-display);letter-spacing:-.028em;max-width:16ch}
.cta-copy p{font:400 clamp(15px,1.6vw,17px)/1.5 var(--face-spine);max-width:48ch;opacity:.92}
.cta .btn{flex:none}

/* ---- footer (kit row 9 lower + copy §1.11 NAP) ----------------------------- */
.foot{background:var(--slab);color:var(--paper);margin-top:clamp(56px,8vw,104px)}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(230px,100%),1fr));
           gap:38px;padding-block:clamp(44px,6vw,72px)}
.foot .col b{display:block;font:700 15px/1.3 var(--face-spine);margin-bottom:14px}
.foot .col a,.foot .col span{display:block;font:400 14px/1.9 var(--face-spine);
  color:var(--quiet-on-slab);text-decoration:none}
.foot .col a:hover{color:var(--paper)}
.foot-bar{display:flex;align-items:center;justify-content:space-between;gap:22px;flex-wrap:wrap;
          padding-block:22px;border-top:1px solid var(--rule-on-slab);
          font:400 13px/1.5 var(--face-spine);color:var(--quiet-on-slab)}
/* The kit's oversized footer wordmark, done with the SUPPLIED lockup geometry
   rather than re-set text — it scales to the measure exactly, so it cannot
   clip, and the logo doctrine (never re-set the wordmark) holds. */
.foot-word{overflow:hidden;padding-block:10px}
.foot-word .line{display:block;width:100%;height:auto;color:var(--paper);opacity:.12}

/* ---- WhatsApp float --------------------------------------------------------- */
.wa{position:fixed;inset-inline-end:18px;bottom:18px;z-index:50;width:56px;height:56px;
    border-radius:999px;background:var(--plate);color:var(--ink);display:grid;place-items:center;
    box-shadow:var(--lift-2);transition:transform var(--t-ack) var(--ease-commit)}
.wa:hover{transform:translateY(-3px)}

/* ---- a11y helpers ----------------------------------------------------------- */
.sp-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
       clip:rect(0 0 0 0);white-space:nowrap;border:0}
.sp-skip{position:absolute;top:-60px;left:12px;z-index:100;background:var(--fg);color:var(--bg);
         padding:12px 18px;border-radius:10px;font:600 14px/1 var(--face-spine);
         text-decoration:none}
.sp-skip:focus-visible{top:12px}

/* ---- kses-safe utilities (inline styles are stripped in Forge html nodes) -- */
.pt0{padding-top:0}
.mt0{margin-top:0}
.mt16{margin-top:16px}
.mt24{margin-top:24px}
.blk{display:block}
.brand-link{display:flex}
.band-sunk{background:var(--paper-sunk);border-block:1px solid var(--rule)}
[data-tone="plate-ink"]{color:var(--plate-ink)}
[data-tone="plate"]{color:var(--plate)}
.logo-img{display:block;height:30px;width:auto}
.foot .logo-img{height:26px;filter:none}
.foot-blurb{display:block;margin-top:14px;max-width:30ch}
.aud .media{border-radius:0}
.foot-word .line{width:100%;height:auto}

/* ---- icon masks — currentColor spans, kses-proof ---------------------------
   The mask URL is rewritten to the uploads path in the WP bundle build. */
.ic{display:inline-block;width:18px;height:18px;background:currentColor;flex:none;
    -webkit-mask:var(--ic) no-repeat center / contain;mask:var(--ic) no-repeat center / contain}
.ic-arrow{--ic:url("../img/arrow.svg")}
.ic-out{--ic:url("../img/arrow-out.svg");width:16px;height:16px}
.ic-check{--ic:url("../img/check.svg");width:15px;height:15px;margin-top:3px}
.ic-wa{--ic:url("../img/whatsapp.svg");width:26px;height:26px}
.plan li .ic-check{flex:none}

/* Forge front-end root fix (landmine #55) — harmless outside WP.
   Measured here: the rendered root carries `gf-el gf-container` (no gf-root),
   so the log's original selector missed and the wrapping-column-flex phantom
   space inflated every page ~2,200px. Target the _class we set on the doc. */
.spout-page.gf-container{display:block !important;max-width:none !important;
  width:100% !important;padding:0 !important}
.spout-page > .gf-html{display:block}

/* ---- form result notes (body carries gc-ok / gc-err after redirect) ------- */
.form-note{display:none;padding:12px 14px;border-radius:10px;
           font:500 14px/1.45 var(--face-spine)}
.form-ok{background:var(--plate);color:var(--ink)}
.form-err{background:var(--flag-wash);color:var(--flag)}
body.gc-ok .form-ok{display:block}
body.gc-err .form-err{display:block}

/* Inner-page hero offset (pricing/stores/locations/contact) */
.pt-hero{padding-top:clamp(96px,12vw,150px)}
