/* =====================================================================
   stica.css — the one stylesheet for stica.co.uk
   Built 2026-08-06 from phase-zero/GRID-CONSOLIDATED.md.

   This file replaces nine drifted copies of the same component set:
   the old stica.css, grid-motion.css, mobile.css, scenes.css,
   craft/assets/style.css and the page-local <style> blocks in
   index, start, craft, craft/pricing, apply, member, coaching,
   pricing and science.

   A page links exactly two files and nothing else:
     <link rel="stylesheet" href="/assets/stica.css">
     <script src="/assets/stica.js" defer></script>

   NO page-local component CSS. If a component is missing, it is missing
   on purpose or by mistake, and the answer is to ask, not to invent.

   Fonts are four external .woff2 files under /assets/fonts/. They are NOT
   embedded; nothing renders offline. (The old header claimed otherwise.)
   ===================================================================== */


/* =====================================================================
   1. FONTS
   Geist and Geist Mono are variable fonts (fvar table verified), so one
   @font-face each with a weight range, not five and two.
   Preload the two that render above the fold, from the page <head>:
     <link rel="preload" href="/assets/fonts/ff49f83effe.woff2" as="font"
           type="font/woff2" crossorigin>
     <link rel="preload" href="/assets/fonts/f8c3cbed783.woff2" as="font"
           type="font/woff2" crossorigin>
   Kalam is below the fold everywhere; swap is fine, do not preload it.
   ===================================================================== */

@font-face{font-family:'Geist';font-style:normal;font-weight:400 800;font-display:swap;
  src:url(/assets/fonts/ff49f83effe.woff2) format("woff2");}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:400 500;font-display:swap;
  src:url(/assets/fonts/f8c3cbed783.woff2) format("woff2");}
@font-face{font-family:'Kalam';font-style:normal;font-weight:400;font-display:swap;
  src:url(/assets/fonts/fa0b26053cd.woff2) format("woff2");}
@font-face{font-family:'Kalam';font-style:normal;font-weight:700;font-display:swap;
  src:url(/assets/fonts/fbefa33190a.woff2) format("woff2");}


/* =====================================================================
   2. TOKENS

   Palette values are declared ONCE, as --l-* (light) and --d-* (dark).
   The live tokens (--bg, --ink, …) are a mapping onto those.

   Dark is LIVE (switched on 2026-08-06). Every page carries
   data-theme="auto" on <html>, so a visitor whose device is set to dark
   gets the dark site and everyone else gets light. "dark" forces it and
   "light" pins it; two pages pin light on purpose, because they are
   artefacts rather than pages a visitor browses: assets/og-src.html
   renders the social card, and downloads/stica-example-plan.html is a
   printable A4 document. Every new page must set data-theme, or the site
   changes appearance as someone walks through it.

   Working-state colours (green / amber / cobalt / oxide) are FACTS, not
   branding. They never change with the line accent and are never
   repurposed as decoration.
   ===================================================================== */

:root{
  /* ---- families ---- */
  --sans:'Geist',system-ui,-apple-system,sans-serif;
  --mono:'Geist Mono','SFMono-Regular',Consolas,monospace;
  --hand:'Kalam',cursive;

  /* ---- light palette (values live here, once) ---- */
  --l-bg:#FAFAF6;      --l-surface:#FFFFFF;  --l-grid:#ECECE6;
  --l-line:#E2E2DC;    --l-line2:#CFCFC8;    --l-line-ui:#8D8D82;
  --l-ink:#111110;     --l-ink2:#33332B;     --l-mut:#6B6B61;
  --l-green:#0F7A3D;   --l-green-bg:#E4F6EA;
  --l-amber:#8A5806;   --l-amber-bg:#FBEFD6;
  --l-cobalt:#27349C;  --l-cobalt-bg:#E6EAFE;
  --l-oxide:#7A2520;   --l-oxide-bg:#F3E4E2;
  --l-shadow:0 1px 2px rgba(20,20,19,.05), 0 6px 18px rgba(20,20,19,.05);
  --l-hl-mix:20%;
  --l-scrim:rgba(10,10,9,.5);

  /* ---- dark palette (values live here, once) ---- */
  --d-bg:#17181A;      --d-surface:#1F2123;  --d-grid:#26282B;
  --d-line:#303234;    --d-line2:#3C3E41;    --d-line-ui:#6E7175;
  --d-ink:#ECECE7;     --d-ink2:#C4C5BE;     --d-mut:#8F908A;
  --d-green:#4FD68A;   --d-green-bg:#16302233;
  --d-amber:#FBB13C;   --d-amber-bg:#3A2C0F40;
  --d-cobalt:#8FA0FF;  --d-cobalt-bg:#1B215080;
  --d-oxide:#E0625A;   --d-oxide-bg:#3A1E1B66;
  --d-shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 22px rgba(0,0,0,.35);
  --d-hl-mix:32%;
  --d-scrim:rgba(0,0,0,.62);

  /* ---- live tokens: light by default ---- */
  --bg:var(--l-bg);            --surface:var(--l-surface);   --grid:var(--l-grid);
  --line:var(--l-line);        --line2:var(--l-line2);       --line-ui:var(--l-line-ui);
  --ink:var(--l-ink);          --ink2:var(--l-ink2);         --mut:var(--l-mut);
  --green:var(--l-green);      --green-bg:var(--l-green-bg);
  --amber:var(--l-amber);      --amber-bg:var(--l-amber-bg);
  --cobalt:var(--l-cobalt);    --cobalt-bg:var(--l-cobalt-bg);
  --oxide:var(--l-oxide);      --oxide-bg:var(--l-oxide-bg);
  --shadow:var(--l-shadow);    --hl-mix:var(--l-hl-mix);     --scrim:var(--l-scrim);

  /* ---- spacing: 4px-rooted, twelve steps, nothing between ---- */
  --s1:4px;  --s2:8px;  --s3:12px; --s4:16px; --s5:24px;
  --s6:32px; --s7:44px; --s8:64px; --s9:80px; --s10:96px;
  /* Round 3: two steps added for a page that opts into a roomier rhythm
     (.st-section--roomy, .st-hero-split--roomy) than the 80px default.
     Nothing existing reads these; no page regresses by their addition. */
  --s11:128px; --s12:176px;

  /* ---- page ---- */
  --w-page:1080px;   /* everything */
  --w-wide:1220px;   /* one case only: a full-bleed media row. Never text, never a footer. */
  --gutter:44px;     /* set by the container, never per section */
  --sec-y:var(--s9); /* section vertical padding */
  --sec-y-roomy:var(--s11); /* the opt-in wider rhythm; overridden at both breakpoints below like --sec-y is */

  /* ---- radii: five values, nothing else ---- */
  --r-sm:4px;    /* inline marks, wordmark highlight, small inner blocks */
  --r-md:6px;    /* form inputs, chips, small controls */
  --r-lg:8px;    /* tiles, cards, buttons, panels — the default */
  --r-xl:12px;   /* the one hero-scale panel per page */
  --r-full:999px;/* header nav pills ONLY */
  --r-dot:50%;   /* brand dot, chip dots, step rings, pins */

  /* ---- shadows: objecthood, never hierarchy ---- */
  --shadow-paper:drop-shadow(0 1px 2px rgba(20,20,19,.32))
                 drop-shadow(0 12px 18px rgba(20,20,19,.20))
                 drop-shadow(0 26px 36px rgba(20,20,19,.14));
  --shadow-stuck:0 1px 1px rgba(28,24,14,.05), 1px 2px 4px rgba(28,24,14,.06),
                 3px 6px 11px rgba(28,24,14,.07), 6px 13px 22px rgba(28,24,14,.08),
                 9px 22px 38px rgba(28,24,14,.06);

  /* ---- motion ---- */
  --ease:cubic-bezier(0.22,1,0.36,1);
  --dur-1:150ms;  /* colour and border on hover */
  --dur-2:220ms;  /* transform, small state change */
  --dur-3:320ms;  /* stuck-note lift, panel open */
  --dur-4:380ms;  /* scroll reveal */
  --dur-5:520ms;  /* highlighter sweep */
  --dur-6:1600ms; /* a drawn line */

  /* ---- type steps: every size on the site is one of these ---- */
  --fs-display:clamp(36px,6.4vw,64px);
  --fs-h1:clamp(30px,5vw,44px);
  --fs-h2:clamp(26px,4vw,38px);
  /* One addition, round 2: the price card's own number needed to outrank a
     section heading and never tie an h1. Sits between --fs-h1 and
     --fs-display on purpose. Still a token, still clamped, still used
     nowhere by hand. */
  --fs-price:clamp(40px,4.6vw,52px);
  --fs-h3:20px;
  --fs-h3-lg:24px;
  --fs-h4:16px;
  --fs-lede:clamp(16.5px,2vw,19px);
  --fs-body:16px;
  --fs-body-sm:14px;
  --fs-eyebrow:11px;
  --fs-label:10.5px;
  --fs-chip:10px;
  --fs-small:11px;
  --fs-note:30px;
  --fs-note-sm:21px;
  --fs-note-xs:17px;
}

/* ---- dark mapping. Two blocks, one set of values. ---- */
:root[data-theme="dark"]{
  --bg:var(--d-bg);            --surface:var(--d-surface);   --grid:var(--d-grid);
  --line:var(--d-line);        --line2:var(--d-line2);       --line-ui:var(--d-line-ui);
  --ink:var(--d-ink);          --ink2:var(--d-ink2);         --mut:var(--d-mut);
  --green:var(--d-green);      --green-bg:var(--d-green-bg);
  --amber:var(--d-amber);      --amber-bg:var(--d-amber-bg);
  --cobalt:var(--d-cobalt);    --cobalt-bg:var(--d-cobalt-bg);
  --oxide:var(--d-oxide);      --oxide-bg:var(--d-oxide-bg);
  --shadow:var(--d-shadow);    --hl-mix:var(--d-hl-mix);     --scrim:var(--d-scrim);
  color-scheme:dark;
}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"]{
    --bg:var(--d-bg);            --surface:var(--d-surface);   --grid:var(--d-grid);
    --line:var(--d-line);        --line2:var(--d-line2);       --line-ui:var(--d-line-ui);
    --ink:var(--d-ink);          --ink2:var(--d-ink2);         --mut:var(--d-mut);
    --green:var(--d-green);      --green-bg:var(--d-green-bg);
    --amber:var(--d-amber);      --amber-bg:var(--d-amber-bg);
    --cobalt:var(--d-cobalt);    --cobalt-bg:var(--d-cobalt-bg);
    --oxide:var(--d-oxide);      --oxide-bg:var(--d-oxide-bg);
    --shadow:var(--d-shadow);    --hl-mix:var(--d-hl-mix);     --scrim:var(--d-scrim);
    color-scheme:dark;
  }
}
:root[data-theme="light"]{ color-scheme:light; }


/* =====================================================================
   3. THE ACCENT: one brand constant, one accent per product line

   Teal is the brand constant. It carries the shell on EVERY page without
   exception: wordmark, nav, footer, focus ring, skip link. It is also the
   page accent for everything that is not a product line — the homepage,
   pricing, consulting, the method, the legal pages, status, 404.

   A product line overrides ONLY the four accent tokens, by putting its
   class on <html>. Pages within a line share their line's accent. There
   is no such thing as a per-page accent.

     (none)        Stica teal      brand constant + all shared pages
     .line-stica   cobalt          Stica: /learn, /teams, /start
     .line-coach   violet          Stica Coach: /coaches

   Each accent ships FOUR tokens with three different jobs, because one
   value doing focus ring, button fill and accent text is exactly the
   fault that produced six live accessibility failures:

     --acc        decorative ONLY. The pin, the wordmark wash, a low-alpha
                  fill, a spine stroke where a number already carries the
                  meaning. NEVER text. NEVER a sole boundary.
     --acc-line   any accent line that must be perceivable: focus ring,
                  step ring, active-tab underline, left-accent border.
                  >= 3:1 on both grounds.
     --acc-fill   solid accent blocks carrying white text. White on it
                  is >= 4.5:1.
     --acc-text   accent-coloured text at any size. >= 4.5:1 on both
                  grounds.

   Measured ratios are in FOUNDATION-REPORT.md. Do not substitute a value.
   ===================================================================== */

:root{
  /* Every accent value on the property is declared here, once. Nothing
     below this block, and no page anywhere, may write one of these hexes
     by hand. Measured ratios are in FOUNDATION-REPORT.md. */
  --a-brand:#12A6B5;        --a-brand-line:#0F909D;   --a-brand-fill:#0D7B87;
  --a-brand-line-d:#12A6B5; --a-brand-text-d:#12A6B5;

  --a-stica:#3D5AFF;        --a-stica-line:#5F77FF;   --a-stica-fill:#405CFF;
  --a-stica-line-d:#4C67FF; --a-stica-text-d:#7086FF;

  --a-coach:#7C5CFF;        --a-coach-line:#876AFF;   --a-coach-fill:#704DFF;
  --a-coach-line-d:#7959FF; --a-coach-text-d:#957BFF;

  /* live accent tokens: brand teal by default, so a page that forgets its
     line class renders the brand, never something wrong */
  --acc:var(--a-brand);
  --acc-line:var(--a-brand-line);
  --acc-fill:var(--a-brand-fill);
  --acc-text:var(--a-brand-fill);
}
/* .line-stica / .line-coach normally sit on <html> (one line owns the whole
   page). pricing/index.html is the one page where two lines share a page,
   so the same classes are ALSO plain (non-:root) rules here: put one on a
   <section> and only that section's descendants get the line's tokens.
   Same tokens, same bars, just a narrower scope. */
:root.line-stica, .line-stica{
  --acc:var(--a-stica); --acc-line:var(--a-stica-line);
  --acc-fill:var(--a-stica-fill); --acc-text:var(--a-stica-fill);
}
:root.line-coach, .line-coach{
  --acc:var(--a-coach); --acc-line:var(--a-coach-line);
  --acc-fill:var(--a-coach-fill); --acc-text:var(--a-coach-fill);
}

/* On the dark board the bright value becomes the safe one. Fill is
   mode-invariant: white on it passes in both modes. */
:root[data-theme="dark"]{             --acc-line:var(--a-brand-line-d); --acc-text:var(--a-brand-text-d); }
:root[data-theme="dark"].line-stica, [data-theme="dark"] .line-stica{  --acc-line:var(--a-stica-line-d); --acc-text:var(--a-stica-text-d); }
:root[data-theme="dark"].line-coach, [data-theme="dark"] .line-coach{  --acc-line:var(--a-coach-line-d); --acc-text:var(--a-coach-text-d); }
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"]{            --acc-line:var(--a-brand-line-d); --acc-text:var(--a-brand-text-d); }
  :root[data-theme="auto"].line-stica, [data-theme="auto"] .line-stica{ --acc-line:var(--a-stica-line-d); --acc-text:var(--a-stica-text-d); }
  :root[data-theme="auto"].line-coach, [data-theme="auto"] .line-coach{ --acc-line:var(--a-coach-line-d); --acc-text:var(--a-coach-text-d); }
}

/* Teal carries the shell on EVERY page without exception, even inside a
   product line: wordmark, nav, footer, focus ring. A line class overrides
   --acc* at :root for the content it wraps, but that inheritance must not
   leak into the header or footer, or the brand mark changes colour from
   page to page. Re-pinning the four tokens on the shell subtree wins the
   cascade for everything inside it, machine content untouched. */
.st-head,.st-footer,.st-skip{
  --acc:var(--a-brand); --acc-line:var(--a-brand-line);
  --acc-fill:var(--a-brand-fill); --acc-text:var(--a-brand-fill);
}
:root[data-theme="dark"] .st-head,:root[data-theme="dark"] .st-footer,:root[data-theme="dark"] .st-skip{
  --acc-line:var(--a-brand-line-d); --acc-text:var(--a-brand-text-d);
}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-head,:root[data-theme="auto"] .st-footer,:root[data-theme="auto"] .st-skip{
    --acc-line:var(--a-brand-line-d); --acc-text:var(--a-brand-text-d);
  }
}


/* =====================================================================
   4. BASE
   ===================================================================== */

*,*::before,*::after{box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;}
body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--sans);
  font-size:var(--fs-body);
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img,svg,video{max-width:100%;height:auto;}
a{color:var(--acc-text);}
hr{border:0;border-top:1px solid var(--line);margin:var(--s6) 0;}

/* One focus treatment, everywhere. 3px at 2px offset in --acc-line,
   which clears the 3:1 non-text bar in every line and both modes. */
:where(a,button,input,textarea,select,summary,[tabindex]):focus-visible{
  outline:3px solid var(--acc-line);
  outline-offset:2px;
  border-radius:var(--r-sm);
}
:where(a,button,input,textarea,select,summary,[tabindex]):focus:not(:focus-visible){outline:none;}


/* =====================================================================
   5. PAGE, CONTAINER, GRAPH GROUND, SECTION
   ===================================================================== */

/* The graph ground goes on a wrapper around the whole page, on EVERY page. */
.st-grid{
  background-image:
    linear-gradient(var(--grid) 1px,transparent 1px),
    linear-gradient(90deg,var(--grid) 1px,transparent 1px);
  background-size:48px 48px;
  background-position:-1px -1px;
}

/* One width. Gutters are applied by the container, never per section. */
.st-wrap{
  max-width:var(--w-page);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
.st-wrap--wide{max-width:var(--w-wide);}  /* full-bleed media rows only */

/* One idea per section. Sections divide with a hairline, never a colour block. */
.st-section{
  padding-block:var(--sec-y);
  border-top:1px solid var(--line);
  /* So an in-page anchor jump (a #id target inside a section) lands below
     the sticky header instead of under it. Matches the header's own
     rendered height with a little air. */
  scroll-margin-top:84px;
}
.st-section--first{border-top:0;}
/* Round 3: opt-in roomier rhythm (128px desktop, stepping down at both
   breakpoints below same as --sec-y does) for a page that wants more air
   than the 80px default. Nothing existing carries this class. */
.st-section--roomy{padding-block:var(--sec-y-roomy);}
/* Round 3 (teams): the opposite opt-in — a denser seam where a page wants
   two adjacent sections to read as one cluster rather than two equal
   80px-apart blocks. --s8 rather than a bespoke value, so the step still
   sits on the twelve-step scale. */
.st-section--tight{padding-block:var(--s8);}
/* A banded section separates one major block. At most twice per page. */
.st-section--band{background:var(--surface);}
/* A tinted section separates the page's one most important block with a real
   ground, in the line's own accent, at a low mix so text contrast is
   untouched. At most once per page.
   Round 3: radius added so the tint reads as a designed ground change
   confined to .st-wrap, not a stray square div. Purely additive (a rounded
   corner where there was a sharp one); nothing that depends on the sharp
   corner exists. */
.st-section--tint{background:color-mix(in srgb,var(--acc) 5%,var(--bg));border-radius:var(--r-xl);}
/* Round 3 (index): an inverted dark band, for the one moment a page wants a
   surface contrast a light page cannot otherwise reach (a closing CTA).
   Same technique as the white-paper-on-dark-board rule in §16, run in
   reverse: re-pin the live tokens to the already-fully-defined dark
   palette on this subtree only, so every descendant (.st-btn, .st-lede,
   a focus ring) just works without a single hardcoded hex. At most once
   per page. Full width by default — wrap the max-width content inside in
   .st-wrap, same as .st-rail and the final .st-cta already do. */
.st-section--dark{
  background:var(--d-bg); color:var(--d-ink);
  --bg:var(--d-bg);         --surface:var(--d-surface);   --grid:var(--d-grid);
  --line:var(--d-line);     --line2:var(--d-line2);       --line-ui:var(--d-line-ui);
  --ink:var(--d-ink);       --ink2:var(--d-ink2);         --mut:var(--d-mut);
  --green:var(--d-green);   --green-bg:var(--d-green-bg);
  --amber:var(--d-amber);   --amber-bg:var(--d-amber-bg);
  --cobalt:var(--d-cobalt); --cobalt-bg:var(--d-cobalt-bg);
  --oxide:var(--d-oxide);   --oxide-bg:var(--d-oxide-bg);
  --shadow:var(--d-shadow);
  --acc-line:var(--a-brand-line-d); --acc-text:var(--a-brand-text-d);
}
.st-section--dark.line-stica, .line-stica .st-section--dark{ --acc-line:var(--a-stica-line-d); --acc-text:var(--a-stica-text-d); }
.st-section--dark.line-coach, .line-coach .st-section--dark{ --acc-line:var(--a-coach-line-d); --acc-text:var(--a-coach-text-d); }
/* …and on a page that is ALREADY dark, the rule above painted #17181A onto
   #17181A: measured 1.00:1 against the page, so the homepage's closing band
   disappeared completely the moment dark was switched on. Inversion has to
   run the other way here. The band becomes a RAISED slab: every live token
   steps one rung up the dark ramp it already owns, so the block reads as its
   own ground (1.20:1 against the page, better separation than the light
   theme's own .st-section--band gets at 1.05:1) with no new colour invented
   and no hex written by hand. Deliberately stops at --d-grid rather than
   going lighter: one rung further and the accent text inside drops under
   4.5:1. Whether the closing band should be LOUDER than this in dark is a
   design call, not a repair — a light band on a dark page is the literal
   inversion, and it is also a glare bomb at the foot of a night-time page. */
:root[data-theme="dark"] .st-section--dark{
  background:var(--d-grid); color:var(--d-ink);
  --bg:var(--d-grid);       --surface:var(--d-line);      --grid:var(--d-line);
  --line:var(--d-line2);    --line2:var(--d-line-ui);
}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-section--dark{
    background:var(--d-grid); color:var(--d-ink);
    --bg:var(--d-grid);       --surface:var(--d-line);      --grid:var(--d-line);
    --line:var(--d-line2);    --line2:var(--d-line-ui);
  }
}

/* Column grids. These four, and nothing else. */
.st-cols-2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4);}
/* Round 3: opt-in top alignment. Default stretch is right when both
   columns are the same kind of card; it is wrong the moment one side is
   short text and the other is a tall card, because the short side then
   stretches into a bordered box with nothing in the lower half of it —
   a visibly empty container. Opt in per instance rather than flipping the
   shared default, since existing two-column grids elsewhere rely on stretch. */
.st-cols-2--top{align-items:start;}
.st-cols-3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s4);}
.st-cols-4{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s4);}
.st-split{display:grid;grid-template-columns:1.15fr .85fr;gap:var(--s8);align-items:center;}

/* Two-voice reading spread: 7/5 machine column plus a dashed pen rail.
   This is the whole of what survived scenes.css. */
.st-spread{display:grid;grid-template-columns:7fr 5fr;gap:0 var(--s7);align-items:start;}
.st-spread__body{max-width:68ch;min-width:0;}
.st-spread__rail{
  position:relative;min-height:60px;padding-top:var(--s1);
  border-left:1px dashed var(--line2);padding-left:var(--s5);
  font-size:var(--fs-body-sm);color:var(--mut);
}
.st-spread--flip{grid-template-columns:5fr 7fr;}
.st-spread--flip .st-spread__body{order:2;}
.st-spread--flip .st-spread__rail{
  order:1;border-left:0;border-right:1px dashed var(--line2);
  padding-left:0;padding-right:var(--s5);text-align:right;
}
/* Decorative grid flourish in the rail. Hairline only, never carries meaning. */
.st-spread__mark{display:block;}
.st-spread__mark svg{overflow:visible;}
.st-spread__mark path,.st-spread__mark line,.st-spread__mark circle{
  fill:none;stroke:var(--line2);stroke-width:1;vector-effect:non-scaling-stroke;
}


/* =====================================================================
   6. TYPE
   One h1 per page. No skipped heading levels. Visual size comes from the
   class, semantics from the tag: if an h3 must look big, use
   <h3 class="st-h3-lg">, never <h2>.
   Measure caps are enforced, not advisory.
   Minimum size anywhere is 10px, and 10px is reserved for chip text.
   ===================================================================== */

/* Round 3: text-wrap:balance on the three headline-scale classes. No
   `text-wrap` existed anywhere in the sheet, so a display headline at 64px
   in a narrow hero column ragged badly (a short first line, an orphaned
   word closing line two). Supported browsers balance the rag; browsers
   without it just keep the old wrap, so this is a pure improvement with
   no fallback needed. Never added to .st-h3/.st-h4 — balance below display
   scale fights short, deliberately-terse card headings more than it helps. */
.st-display,.st-h1,.st-h2{text-wrap:balance;}
.st-display{font-size:var(--fs-display);font-weight:800;line-height:.94;letter-spacing:-.05em;color:var(--ink);margin:0 0 var(--s4);}
/* Round 3: an opt-in larger step for the one page that wants to lead on
   scale, without moving --fs-display itself under six live pages that
   have not been re-checked against it. Floor sits clear of --fs-h2's own
   38px ceiling at every width; ceiling clears the 84px bar a flagship
   hero needs on a 1440 canvas. */
.st-display--xl{font-size:clamp(44px,6.4vw,88px);}
.st-h1     {font-size:var(--fs-h1);font-weight:800;line-height:1;letter-spacing:-.045em;color:var(--ink);margin:0 0 var(--s4);}
.st-h2     {font-size:var(--fs-h2);font-weight:700;line-height:1.1;letter-spacing:-.042em;color:var(--ink);margin:0 0 var(--s3);}
.st-h3     {font-size:var(--fs-h3);font-weight:700;line-height:1.25;letter-spacing:-.5px;color:var(--ink);margin:0 0 var(--s2);}
.st-h3-lg  {font-size:var(--fs-h3-lg);font-weight:700;line-height:1.2;letter-spacing:-.7px;color:var(--ink);margin:0 0 var(--s2);}
.st-h4     {font-size:var(--fs-h4);font-weight:600;line-height:1.35;letter-spacing:-.3px;color:var(--ink);margin:0 0 var(--s2);}

.st-lede   {font-size:var(--fs-lede);font-weight:400;line-height:1.6;color:var(--ink2);max-width:52ch;margin:0 0 var(--s5);}
.st-body   {font-size:var(--fs-body);font-weight:400;line-height:1.6;color:var(--ink2);max-width:64ch;margin:0 0 var(--s4);}
.st-body-sm{font-size:var(--fs-body-sm);font-weight:400;line-height:1.55;color:var(--ink2);max-width:48ch;margin:0 0 var(--s3);}

/* The mono metadata rule: anything ABOUT the content is mono, small,
   uppercase, tracked out. Content itself is never mono. */
.st-eyebrow{font-family:var(--mono);font-size:var(--fs-eyebrow);font-weight:500;line-height:1.4;letter-spacing:1.5px;text-transform:uppercase;color:var(--mut);margin:0 0 var(--s3);}
.st-eyebrow b{color:var(--acc-text);font-weight:500;}  /* ONE accent word, not a whole accent line */
.st-label  {font-family:var(--mono);font-size:var(--fs-label);font-weight:400;line-height:1.4;letter-spacing:1px;text-transform:uppercase;color:var(--mut);margin:0 0 var(--s2);}
.st-small  {font-family:var(--mono);font-size:var(--fs-small);font-weight:400;line-height:1.6;color:var(--mut);margin:0;}

/* The hand rule: Kalam is margins and captions. Never body copy, never a
   button, never an interface label, never a heading. Always tilted.
   Only ever a positive or clarifying note, never a warning. */
.st-note   {font-family:var(--hand);font-weight:700;font-size:var(--fs-note);line-height:1.05;color:var(--ink);display:inline-block;margin:0 0 var(--s4);}
.st-note-sm{font-family:var(--hand);font-weight:700;font-size:var(--fs-note-sm);line-height:1.15;color:var(--ink);display:inline-block;margin:0 0 var(--s3);}
.st-note-xs{font-family:var(--hand);font-weight:700;font-size:var(--fs-note-xs);line-height:1.2;color:var(--ink);display:inline-block;margin:0 0 var(--s2);}
.st-note--acc,.st-note-sm--acc,.st-note-xs--acc{color:var(--acc-text);}
[data-tilt="-3"]{transform:rotate(-3deg);}
[data-tilt="-2"]{transform:rotate(-2deg);}
[data-tilt="-1"]{transform:rotate(-1deg);}
[data-tilt="1"] {transform:rotate(1deg);}
[data-tilt="2"] {transform:rotate(2deg);}
[data-tilt="3"] {transform:rotate(3deg);}

.st-mono{font-family:var(--mono);}
.st-rule{display:block;height:4px;width:96px;background:var(--acc-line);margin:var(--s3) 0 var(--s5);border-radius:2px;}

/* Section heading block: eyebrow + h2 + lede. Sizes from the scale.
   No inline overrides, on any page, ever. */
.st-sechead{margin:0 0 var(--s6);}
.st-sechead--centre{text-align:center;}
.st-sechead--centre .st-lede{margin-inline:auto;}


/* =====================================================================
   7. SHELL: skip link, header, nav, mobile nav, brand, footer
   ===================================================================== */

/* 7.1 Skip link — required on every page, first element in <body>. */
.st-skip{
  position:absolute;left:var(--s2);top:-100px;z-index:100;
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line-ui);border-radius:var(--r-md);
  padding:var(--s3) var(--s4);font-size:var(--fs-body-sm);font-weight:600;
  text-decoration:none;transition:top var(--dur-2) var(--ease);
}
.st-skip:focus{top:var(--s2);}

/* 7.2 Header. One definition. One nav item list. */
.st-head{
  position:sticky;top:0;z-index:40;
  border-bottom:1px solid var(--line);
  background:color-mix(in srgb,var(--bg) 88%,transparent);
  backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
}
.st-head__inner{
  max-width:var(--w-page);margin-inline:auto;padding:var(--s3) var(--gutter);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s5);
}
.st-nav{display:flex;align-items:center;gap:var(--s5);}
.st-nav a{
  font-size:var(--fs-body-sm);color:var(--ink2);text-decoration:none;
  transition:color var(--dur-1) var(--ease);
}
.st-nav a:hover{color:var(--ink);}
/* Current page. Every page must mark its own nav item with aria-current="page". */
.st-nav a[aria-current="page"]{
  color:var(--ink);font-weight:600;
  box-shadow:inset 0 -2px 0 0 var(--acc-line);
}

/* 7.6 Wordmark. The one thing already built as a single component. */
.st-brand{
  font-weight:700;font-size:30px;letter-spacing:-1px;color:var(--ink);
  text-decoration:none;line-height:1;white-space:nowrap;flex:none;
  --logo-hl:color-mix(in srgb,var(--acc) var(--hl-mix),transparent);
  background-image:linear-gradient(var(--logo-hl),var(--logo-hl));
  /* Position and size are in em, tied to the glyph box itself, never to the
     element's own box height — so it cannot detach when the header around
     it grows (a wrapped nav, a taller row). Fixed 2026-08-06, round 2. */
  background-repeat:no-repeat;background-position:left .78em;background-size:100% .52em;
  border-radius:var(--r-sm);padding:0 var(--s1) 1px;
}
.st-brand .dot{
  display:inline-block;width:7px;height:7px;border-radius:var(--r-dot);
  background:var(--acc);margin-left:2px;vertical-align:baseline;
  overflow:hidden;font-size:0;
}

/* 7.3 Mobile nav. Hidden above 640px; the toggle is the only nav below it. */
.st-navtoggle{
  display:none;
  align-items:center;justify-content:center;gap:var(--s2);
  min-width:44px;min-height:44px;padding:0 var(--s3);
  font-family:var(--mono);font-size:var(--fs-eyebrow);letter-spacing:1px;text-transform:uppercase;
  color:var(--ink);background:transparent;
  border:1px solid var(--line2);border-radius:var(--r-md);cursor:pointer;
}
.st-navtoggle__bars{display:block;width:16px;height:10px;position:relative;}
.st-navtoggle__bars::before,.st-navtoggle__bars::after{
  content:"";position:absolute;left:0;width:100%;height:1.5px;background:currentColor;
  transition:transform var(--dur-2) var(--ease),opacity var(--dur-2) var(--ease);
}
.st-navtoggle__bars::before{top:0;}
.st-navtoggle__bars::after{bottom:0;}
.st-navtoggle[aria-expanded="true"] .st-navtoggle__bars::before{transform:translateY(4px) rotate(45deg);}
.st-navtoggle[aria-expanded="true"] .st-navtoggle__bars::after{transform:translateY(-4px) rotate(-45deg);}

.st-navpanel{
  display:none;
  border-top:1px solid var(--line);
  background:var(--surface);
}
.st-navpanel[data-open="true"]{display:block;}
.st-navpanel ul{list-style:none;margin:0;padding:var(--s2) var(--gutter) var(--s4);}
.st-navpanel li + li{border-top:1px solid var(--line);}
.st-navpanel a{
  display:flex;align-items:center;min-height:44px;
  font-size:var(--fs-body);color:var(--ink2);text-decoration:none;
}
.st-navpanel a[aria-current="page"]{color:var(--ink);font-weight:600;}
.st-navpanel .st-btn,.st-navpanel .st-btn--sec{width:100%;justify-content:center;margin-top:var(--s3);}

/* 7.4 Footer. One definition, one link list, same width as the content. */
.st-footer{border-top:1px solid var(--line);background:var(--surface);}
.st-footer__inner{
  max-width:var(--w-page);margin-inline:auto;padding:var(--s6) var(--gutter);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s5);flex-wrap:wrap;
}
.st-footer__nav{display:flex;gap:var(--s5);flex-wrap:wrap;}
.st-footer__nav a{font-size:13px;color:var(--ink2);text-decoration:none;}
.st-footer__nav a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px;}
.st-footer__tag{font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);margin:0;}


/* =====================================================================
   8. BUTTONS
   Four, and four states each. Minimum target 44x44 everywhere.
   Buttons carry NO shadow at rest: shadows signal objecthood, not
   hierarchy. If you are reaching for a shadow to make a button look
   important, use the accent or the hierarchy instead.
   ===================================================================== */

.st-btn,.st-btn--sec,.st-btn--ghost,.st-btn--pill{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
  font-family:var(--sans);text-decoration:none;cursor:pointer;
  border:1px solid transparent;
  /* Round 2: a button never mirrors its container. Cards and tiles use
     --r-lg; a button sitting inside one now reads as its own object
     rather than a nested panel. */
  border-radius:var(--r-md);
  min-height:44px;
  transition:background-color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),
             color var(--dur-1) var(--ease);
}

/* Primary — the ONE action in view */
.st-btn{
  background:var(--acc-fill);color:#fff;
  font-size:15px;font-weight:600;padding:13px 22px;
}
.st-btn:hover{background:color-mix(in srgb,var(--acc-fill) 88%,#000);}
.st-btn:active{background:color-mix(in srgb,var(--acc-fill) 78%,#000);}

/* Secondary — beside a primary. A filled tint, not a hairline box, so it
   never disappears against a --surface card that has its own hairline
   border in the same neutral family. */
.st-btn--sec{
  background:color-mix(in srgb,var(--ink) 5%,transparent);
  color:var(--ink);border-color:var(--line-ui);
  font-size:15px;font-weight:600;padding:13px 20px;
}
.st-btn--sec:hover{border-color:var(--acc-line);background:color-mix(in srgb,var(--ink) 9%,transparent);}
.st-btn--sec.is-picked,.st-btn--sec[aria-pressed="true"]{
  border-color:var(--acc-line);color:var(--acc-text);
  background:color-mix(in srgb,var(--acc) 8%,transparent);
}

/* Outline — an accent-bordered action that must read as live, not muted.
   Round 3: for a free/starter tier sitting beside a paid one, where --sec's
   neutral tint reads as switched off. Accent border and text, transparent
   fill, so it never competes with the page's one primary (.st-btn). */
.st-btn--outline{
  background:transparent;color:var(--acc-text);border-color:var(--acc-line);
  font-size:15px;font-weight:600;padding:13px 20px;
}
.st-btn--outline:hover{background:color-mix(in srgb,var(--acc) 8%,transparent);}

/* Tertiary — an in-flow link that acts as an action */
.st-btn--ghost{
  background:transparent;color:var(--ink);padding:var(--s3) 0;min-height:44px;
  font-size:15px;font-weight:600;
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;
}
.st-btn--ghost:hover{color:var(--acc-text);}

/* Pill — the header nav's Sign in / Start here pair. NOWHERE ELSE. */
.st-btn--pill{
  border-radius:var(--r-full);font-size:13px;font-weight:600;
  padding:9px 16px;min-height:40px;
  background:var(--acc-fill);color:#fff;
}
.st-btn--pill.st-btn--pill-outline{
  background:transparent;color:var(--ink);border-color:var(--line-ui);padding:8px 15px;
}
.st-btn--pill:hover{background:color-mix(in srgb,var(--acc-fill) 88%,#000);}
.st-btn--pill.st-btn--pill-outline:hover{background:var(--surface);border-color:var(--line-ui);}

/* The shell repaints its own buttons, so say the label colour again here.
   `.st-nav a` and `.st-navpanel a` (§7) are class-plus-type, so they outrank
   the plain `.st-btn` / `.st-btn--pill` class rules above and were painting a
   FILLED button's white label in --ink2. Measured on the header's own "Start
   free": 2.55:1 in light and 2.88:1 in dark, i.e. the site's most-clicked
   button failed in both modes, and it only surfaced when dark was switched on.
   Re-stated at the specificity the shell uses, for the filled variants only.
   The outline pill keeps --ink, which is what a transparent fill wants. */
.st-nav a.st-btn,.st-nav a.st-btn--pill,
.st-navpanel a.st-btn,.st-navpanel a.st-btn--pill{color:#fff;}
.st-nav a.st-btn--pill-outline,.st-navpanel a.st-btn--pill-outline,
.st-navpanel a.st-btn--sec,.st-navpanel a.st-btn--ghost{color:var(--ink);}
.st-navpanel a.st-btn--outline{color:var(--acc-text);}

/* Disabled. Always set aria-disabled too; a disabled <a> is not a thing. */
.st-btn[disabled],.st-btn--sec[disabled],.st-btn--ghost[disabled],.st-btn--pill[disabled],
.st-btn[aria-disabled="true"],.st-btn--sec[aria-disabled="true"],
.st-btn--ghost[aria-disabled="true"],.st-btn--pill[aria-disabled="true"]{
  opacity:.55;cursor:not-allowed;pointer-events:none;
}

/* Pending. The label swaps, the width does NOT, so the layout cannot jump.
   Markup: <button aria-busy="true" style="min-width:<measured>px"> or wrap
   the label in .st-btn__label and let the ellipsis replace it. */
[aria-busy="true"] .st-btn__label{visibility:hidden;}
[aria-busy="true"]{position:relative;}
[aria-busy="true"]::after{
  content:"…";position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;
  font-size:20px;line-height:1;
}

.st-btnrow{display:flex;gap:var(--s3);flex-wrap:wrap;align-items:center;}


/* =====================================================================
   9. TILE / CARD  — one component, four variants
   Replaces seventeen classes describing one thing.
   No shadow at rest. Ever.
   ===================================================================== */

.st-tile{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  padding:var(--s5);
  display:flex;flex-direction:column;gap:var(--s3);
}
.st-tile > :last-child{margin-bottom:0;}

/* Whole card is an anchor */
.st-tile--link{
  text-decoration:none;color:inherit;
  transition:border-color var(--dur-1) var(--ease),transform var(--dur-2) var(--ease);
}
.st-tile--link:hover{border-color:var(--acc-line);transform:translateY(-2px);}
.st-tile:focus-within{border-color:var(--acc-line);}

.st-tile--source{border-left:3px solid var(--green);}   /* verified against source */
.st-tile--accent{border-left:3px solid var(--acc-line);}
.st-tile--flat{background:var(--bg);}                   /* on a banded section */
/* Provisional or optional block. Two per page maximum. */
.st-tile--draft{border-style:dashed;border-color:var(--line2);}
/* Round 3 (teams): a persistent door mark. A hover-only lift is invisible
   on a touch screen and, at rest, a link tile and a static tile read as
   the same object. The arrow is always on; only its position animates. */
.st-tile--arrow{position:relative;padding-right:calc(var(--s5) + 22px);}
.st-tile--arrow::after{
  content:"\2192";position:absolute;right:var(--s5);bottom:var(--s5);
  font-size:16px;color:var(--acc-text);
  transition:transform var(--dur-2) var(--ease);
}
.st-tile--arrow:hover::after{transform:translateX(3px);}

/* 9b. SHOT — a real screen framed as a small object: a delivered artefact,
   not decorative photography (see §16's placeholder-imagery rule, which
   this does not fall under; the image behind it is genuine product output).
   Whole-image link; the border and shadow read as "this is a page", the
   same shadow language as .st-panel uses for the one hero-scale surface. */
.st-shot{
  display:block;border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;box-shadow:var(--shadow);background:var(--bg);
  transition:border-color var(--dur-1) var(--ease);
}
.st-shot:hover{border-color:var(--acc-line);}
.st-shot img{display:block;width:100%;height:auto;}

/* 7.23 Audience door: a tile with a mono who-line, a body line, one action.
   On the homepage the door's left border is the ONLY place a line accent
   appears outside its own pages. */
.st-door{}
.st-door--stica{border-left:3px solid var(--a-stica-line);}
.st-door--coach{border-left:3px solid var(--a-coach-line);}
.st-door--teal {border-left:3px solid var(--acc-line);}
/* Round 2: each door hovers in its own line accent, not whatever accent
   the current page happens to carry — the generic .st-tile--link:hover
   rule above was turning the cobalt and violet cards teal on hover. */
.st-door--stica:hover{border-color:var(--a-stica-line);}
.st-door--coach:hover{border-color:var(--a-coach-line);}
.st-door--teal:hover {border-color:var(--acc-line);}
/* Round 3: sans, not mono. The page already carries a section eyebrow in
   mono; a second tracked-mono voice on the same screen (this line, three
   times over) read as the same grey noise repeated, not two roles. This
   kicker keeps the small-caps, letter-spaced treatment but in the sans
   face, so it reads as a distinct "card kicker" register. */
.st-door__who{font-family:var(--sans);font-size:13px;font-weight:600;letter-spacing:.3px;text-transform:uppercase;color:var(--mut);margin:0;}
/* Round 2: the price is the answer to the page's one required question.
   Never .st-small for it — that is a caption size reserved for metadata,
   not the decision a cold visitor came to make.
   Round 3: pinned to the bottom of the card. Door cards carry a variable
   amount of copy above the price, and the row they sit in is equal-height
   by CSS Grid's default stretch — so without this, the price line landed
   at a different y-position on every card in the same row. */
.st-door__price{font-size:15px;font-weight:500;color:var(--ink2);margin:0;margin-top:auto;}
.st-door__price b{font-size:20px;font-weight:700;color:var(--ink);letter-spacing:-.02em;}

/* 7.22 Metric strip: mono label then one claim, in a bordered surface row. */
.st-strip{
  display:flex;align-items:baseline;gap:var(--s4);flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s4) var(--s5);
}
.st-strip__claim{font-size:17.5px;font-weight:600;letter-spacing:-.3px;line-height:1.35;color:var(--ink);margin:0;}

/* 7.22b Glance list. Round 3: a compact set of anchor rows for a real
   price/fact summary in a hero panel, built for pricing/index.html — never
   a chip pretending to be navigation. One line per fact, the value pinned
   right, each row a full 44px tap target. */
.st-glance{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;}
.st-glance li + li{border-top:1px solid var(--line);}
.st-glance a{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  min-height:44px;padding:var(--s2) 0;
  text-decoration:none;color:var(--ink);
  font-size:var(--fs-body-sm);font-weight:600;
  transition:color var(--dur-1) var(--ease);
}
.st-glance a:hover,.st-glance a:hover .st-glance__val{color:var(--acc-text);}
.st-glance__val{font-family:var(--mono);font-size:var(--fs-small);font-weight:500;color:var(--mut);transition:color var(--dur-1) var(--ease);}


/* =====================================================================
   10. CHIP
   10px mono uppercase with a 6px leading dot. A chip NEVER carries meaning
   by colour alone — the word is always there.
   ===================================================================== */

.st-chip{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--mono);font-size:var(--fs-chip);font-weight:500;
  line-height:1.2;letter-spacing:.5px;text-transform:uppercase;
  padding:4px 9px;border-radius:var(--r-md);
  background:var(--surface);color:var(--mut);border:1px solid var(--line);
}
.st-chip::before{content:"";width:6px;height:6px;border-radius:var(--r-dot);background:currentColor;flex:none;}
/* A chip used as a real, clickable jump link (not a status tag) needs the
   44px tap target every interactive thing on the site gets. Text stays
   chip-size; the hit area grows around it. */
.st-chip--link{
  min-height:44px;padding:0 var(--s3);text-decoration:none;
  transition:border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
}
.st-chip--link:hover{border-color:var(--acc-line);color:var(--acc-text);}
.st-chip--verified{background:var(--green-bg);color:var(--green);border-color:transparent;}
.st-chip--review  {background:var(--amber-bg);color:var(--amber);border-color:transparent;}
.st-chip--source  {background:var(--cobalt-bg);color:var(--cobalt);border-color:transparent;}
.st-chip--rejected{background:var(--oxide-bg);color:var(--oxide);border-color:transparent;}
.st-chip--mute    {background:var(--bg);color:var(--mut);}
/* Large chip: for a grading system carrying the primary meaning of a page
   (e.g. an evidence grade), not a secondary tag. Steps up to the next
   type step on the scale (--fs-small) rather than inventing a new size. */
.st-chip--lg{font-size:var(--fs-small);padding:5px 11px;gap:7px;}
.st-chip--lg::before{width:7px;height:7px;}


/* =====================================================================
   11. NUMBERED STEP, and the timeline that is the same shape with state
   Replaces seven implementations of one motif.
   ===================================================================== */

.st-steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s5);}
.st-step{display:grid;grid-template-columns:auto 1fr;gap:var(--s4);align-items:start;}
.st-step__num{
  width:36px;height:36px;flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-dot);background:var(--bg);
  border:2px solid var(--acc-line);
  /* Round 3 (teams): 14px mono in --acc-text read as an empty ring at
     mobile widths — the numeral is the one piece of information the shape
     exists to carry, so it gets the readable weight; the ring alone still
     carries the accent identity. */
  font-family:var(--mono);font-weight:700;font-size:16px;color:var(--ink);
}
.st-step__body > :last-child{margin-bottom:0;}

/* 7.19 Timeline: the step list with three states. */
.st-timeline .st-step__num{border-color:var(--line2);color:var(--mut);}
.st-timeline .is-done    .st-step__num{border-color:var(--green);color:var(--green);}
.st-timeline .is-current .st-step__num{border-color:var(--acc-line);color:var(--acc-text);}
.st-timeline .is-upcoming .st-step__body{opacity:.7;}

/* Round 3: opt-in connector. A hairline down the numeral column, so a
   step list reads as one continuous line rather than loose, disconnected
   circles — the difference between a numbered list and an actual
   timeline. Off by default because most .st-steps uses (three cards in a
   row elsewhere) are not meant to read as a single connected line. */
.st-steps--connected .st-step{position:relative;}
.st-steps--connected .st-step:not(:last-child) .st-step__num::after{
  content:"";position:absolute;top:100%;left:50%;width:1px;
  height:calc(var(--s5) + 2px);background:var(--line2);transform:translateX(-50%);
}
.st-steps--connected .st-step__num{position:relative;}


/* =====================================================================
   12. PRICE BLOCK
   One component. The featured variant carries the word "Recommended" as
   text, never colour alone.
   ===================================================================== */

/* Round 2: fixed, consistent card width instead of 1fr-stretch, so a
   two-tier row and a three-tier row read as the same module rather than
   two different widths one scroll apart. Centred so a short row (one or
   two cards) doesn't sprawl edge to edge. auto-fit + a capped max keeps
   the 640px and 920px breakpoint overrides below in full control. */
.st-prices{
  display:grid;gap:var(--s4);
  grid-template-columns:repeat(auto-fit,minmax(240px,290px));
  justify-content:center;
  align-items:stretch;
}
.st-price{
  position:relative;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  /* Top padding reserves the badge slot on EVERY card, badged or not, so
     __name and __amount sit on one baseline across a row regardless of
     which cards carry a badge. */
  padding:40px var(--s5) var(--s5);
  display:flex;flex-direction:column;gap:var(--s3);
  transition:border-color var(--dur-1) var(--ease),transform var(--dur-2) var(--ease);
}
.st-price:hover{border-color:var(--acc-line);transform:translateY(-2px);}
/* Round 3: a shared row-track grid, opted into only where subgrid is
   supported (every evergreen browser; the file already relies on :has()
   at the same bar). Every card in a row now shares six row tracks — name,
   amount, what, list, note, action — so badge/name/price/what-line/list-
   start land on one baseline across the row regardless of how much text
   any one card carries, and the action lands on one shared bottom line
   without needing margin-top:auto to fake it. Flex remains the fallback
   for anything older. */
/* Final pass, 2026-08-06: the six-row block below is now scoped to
   .st-prices > .st-price. It used to match EVERY .st-price, which broke a
   card standing on its own outside a price row: on /teams the hero price
   card sits in the .st-hero-split grid, inherited `grid-row:span 6` from
   that grid instead, and opened a dead half-screen gap between the name
   and the £295. Shared row tracks are a property of a ROW of cards, so
   they belong to the row, and a lone card now falls back to the flex
   layout above, which is what it wants. */
@supports (grid-template-rows:subgrid){
  .st-prices{grid-template-rows:repeat(6,auto);}
  .st-prices > .st-price{display:grid;grid-template-rows:subgrid;grid-row:span 6;gap:var(--s3);}
  .st-prices > .st-price > .st-price__name{grid-row:1;align-self:start;}
  .st-prices > .st-price > .st-price__amount{grid-row:2;align-self:start;}
  .st-prices > .st-price > .st-price__what{grid-row:3;align-self:start;}
  .st-prices > .st-price > .st-price__list{grid-row:4;align-self:start;}
  .st-prices > .st-price > .st-price__note{grid-row:5;align-self:start;}
  .st-prices > .st-price > .st-btn,.st-prices > .st-price > .st-btn--sec,
  .st-prices > .st-price > .st-btn--ghost,.st-prices > .st-price > .st-btn--outline{grid-row:6;align-self:end;}
}
/* Recommended: real weight, never a shadow (rule: no shadow at rest). A
   tinted surface and the accent border carry it.
   Round 3: was border-width:2px plus +6px padding on every side to
   compensate — that shrank the featured card's content box relative to
   its neighbours by 2x the extra border, which is exactly what pushed
   "/ month" onto its own line on the one card carrying the badge. An
   inset box-shadow reads as the same thicker ring without touching the
   box model, so all three cards keep an identical content width. */
.st-price--featured{
  border-color:var(--acc-line);
  box-shadow:inset 0 0 0 1px var(--acc-line);
  background:color-mix(in srgb,var(--acc) 5%,var(--surface));
}
/* The badge sits astride the card's top edge, out of flow, so it never
   pushes __name/__amount down — the fix for the staircase. */
.st-price__badge{
  position:absolute;top:0;left:24px;transform:translateY(-50%);
  font-family:var(--mono);font-size:var(--fs-chip);letter-spacing:.5px;text-transform:uppercase;
  border-radius:var(--r-md);padding:4px 9px;white-space:nowrap;
  background:var(--acc-fill);color:#fff;
}
/* Recommendation gets the fill (above). Scarcity/availability gets an
   outline in --mut — two different facts never wear the same pill. */
.st-price__badge--outline{background:var(--surface);color:var(--mut);border:1px solid var(--mut);padding:3px 8px;}
.st-price__name{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:1px;text-transform:uppercase;color:var(--mut);margin:0;}
.st-price__amount{
  font-size:var(--fs-price);font-weight:800;letter-spacing:-.03em;line-height:1;color:var(--ink);margin:0;
  font-variant-numeric:tabular-nums;
}
.st-price__unit{
  font-family:var(--sans);font-size:var(--fs-body-sm);font-weight:500;color:var(--mut);
  margin-left:4px;vertical-align:6px;
  /* Round 3: the single highest-priority figure on the page must never
     wrap onto its own orphan line under a narrowed content box. */
  white-space:nowrap;
}
.st-price__what{font-size:var(--fs-body-sm);line-height:1.55;color:var(--ink2);margin:0;}
.st-price__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2);}
.st-price__list li{font-size:var(--fs-body-sm);line-height:1.5;color:var(--ink2);padding-left:var(--s5);position:relative;}
.st-price__list li::before{content:"";position:absolute;left:var(--s2);top:.62em;width:6px;height:6px;border-radius:var(--r-dot);background:var(--acc-line);}
/* The annual note is a conversion lever, not a version stamp: body face,
   the figure in --ink, straight under __unit — never floating above the
   button as its own orphan line. */
.st-price__note{font-family:var(--sans);font-size:var(--fs-body-sm);color:var(--mut);margin:0;}
.st-price__note strong{color:var(--ink);font-weight:700;}
/* The action is always the LAST child of a price card, so every button in a
   row lines up on one baseline no matter how uneven the cards above are.
   Put __note before the button, never after it. Removing flex:1 from
   __list (above) means any leftover height collects here, above the
   button, as one clean gap — not stretched mid-list. This is the flex
   fallback; the @supports block above overrides it with a shared grid row
   wherever subgrid is available. */
.st-price > .st-btn,.st-price > .st-btn--sec,.st-price > .st-btn--ghost,.st-price > .st-btn--outline{margin-top:auto;}
@media (max-width:640px){
  .st-price{padding:36px var(--s4) var(--s4);}
  .st-price--featured{padding:36px var(--s4) var(--s4);}
  .st-price__badge{left:16px;}
}

/* Round 3: a row that should own its measure edge-to-edge and left-align
   under a left-aligned heading, rather than centre a short row in the
   middle of the page — built for pricing/index.html, where two, three and
   one-card rows all sat at different widths and different alignments.
   Default .st-prices (centred, capped at 290px) is untouched, so every
   existing page keeps its current layout. Wider min/max so three cards
   land near 315-320px in the 992px content column, and a lone card still
   reads as a real object rather than a shrunk-down list item. */
.st-prices--wide{
  grid-template-columns:repeat(auto-fit,minmax(280px,360px));
  justify-content:start;
}

/* Round 3: monthly/annual billing toggle. Any input[name="billing"] (see
   stica.js §6) flips this attribute on <body>; the CSS below only ever
   swaps which [data-period] span shows, so it is a content change, not a
   motion effect, and needs no entry in the motion section. Two or more
   segmented controls sharing name="billing" (e.g. one per subscription
   section) stay in sync for free, because a radio group is scoped by name
   across the whole document regardless of where each input sits in the DOM. */
[data-period="annual"]{display:none;}
body[data-billing="annual"] [data-period="monthly"]{display:none;}
body[data-billing="annual"] [data-period="annual"]{display:inline;}


/* =====================================================================
   13. HERO
   Two shapes. Every hero on the site is one of these two.
   ===================================================================== */

.st-hero{padding-block:var(--s10) var(--s9);}
.st-hero__cta{margin-top:var(--s5);}
.st-hero-split{
  padding-block:var(--s10) var(--s9);
  display:grid;grid-template-columns:1.15fr .85fr;gap:var(--s8);align-items:center;
}
/* Round 3: opt-in roomier hero (176/128 instead of 96/80). The existing
   ≤920px rule below (padding-block:var(--s8) var(--s7)) also targets
   .st-hero-split and wins there on source order at equal specificity, so
   this collapses to the same mobile padding every other hero uses —
   nothing extra to maintain per breakpoint. */
.st-hero-split--roomy{padding-block:var(--s12) var(--sec-y-roomy);}
/* Round 3: opt-in top alignment for a hero whose panel is a real content
   object (a timeline, a multi-line block) rather than a short note — the
   default centring floats a short panel in the middle of a taller column
   and reads as an orphaned widget. */
.st-hero-split--top{align-items:start;}
/* The one hero-scale panel per page. It, and the sticky header, are the
   only things on a page that carry --shadow. */
.st-panel{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:var(--s6);box-shadow:var(--shadow);
}
/* Round 3 (index): a light window-chrome bar for a panel that is standing
   in for a real product screen. Three dots, nothing else — no fabricated
   photograph, no invented metric, just the frame that tells a viewer
   "this is app content" rather than "this is a card of text". */
.st-panel__chrome{display:flex;gap:6px;margin-bottom:var(--s5);}
.st-panel__chrome span{width:10px;height:10px;border-radius:var(--r-dot);background:var(--line2);}


/* =====================================================================
   14. FORM
   Native controls only: a real <a>, a real <button>, a real <details>.
   No <div> with a click handler, anywhere, ever.
   Input boundaries are --line-ui (>=3:1), never --line2 (1.50:1).
   ===================================================================== */

.st-field{display:flex;flex-direction:column;gap:var(--s2);margin-bottom:var(--s5);}
.st-field__label{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:1px;text-transform:uppercase;
  color:var(--mut);font-weight:500;
}
/* Required: the mark carries a text equivalent, not a bare asterisk. */
.st-field__req{color:var(--oxide);font-weight:700;}
.st-field__req::after{content:" required";font-size:var(--fs-label);letter-spacing:1px;}

.st-input,.st-select,.st-textarea{
  font-family:var(--sans);font-size:var(--fs-body);line-height:1.5;color:var(--ink);
  background:var(--surface);
  border:1px solid var(--line-ui);
  border-radius:var(--r-md);
  padding:12px 14px;min-height:44px;width:100%;
  transition:border-color var(--dur-1) var(--ease);
}
.st-textarea{min-height:120px;resize:vertical;}
.st-input:hover,.st-select:hover,.st-textarea:hover{border-color:var(--ink2);}
.st-input[disabled],.st-select[disabled],.st-textarea[disabled]{opacity:.55;cursor:not-allowed;background:var(--bg);}
.st-input::placeholder,.st-textarea::placeholder{color:var(--mut);}

/* Error. Per-field, wired with aria-invalid and aria-describedby.
   One server error line at the bottom of a form is not feedback. */
.st-input[aria-invalid="true"],.st-select[aria-invalid="true"],.st-textarea[aria-invalid="true"]{
  border-color:var(--oxide);border-width:2px;
}
.st-help {font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);margin:0;}
.st-error{font-family:var(--mono);font-size:var(--fs-small);color:var(--oxide);margin:0;display:flex;gap:6px;align-items:baseline;}
.st-error::before{content:"×";font-size:14px;line-height:1;}
.st-error:empty{display:none;}

.st-check{display:flex;align-items:flex-start;gap:var(--s3);cursor:pointer;}
.st-check input{width:20px;height:20px;margin:2px 0 0;accent-color:var(--acc-fill);flex:none;}
.st-check span{font-size:var(--fs-body-sm);line-height:1.5;color:var(--ink2);}

/* Disclosure — native <details>. */
.st-disclosure{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);}
.st-disclosure > summary{
  cursor:pointer;list-style:none;padding:var(--s3) var(--s4);min-height:44px;
  display:flex;align-items:center;gap:var(--s2);
  font-size:var(--fs-body-sm);font-weight:600;color:var(--ink);
}
.st-disclosure > summary::-webkit-details-marker{display:none;}
.st-disclosure > summary::after{content:"+";margin-left:auto;font-family:var(--mono);color:var(--mut);}
.st-disclosure[open] > summary::after{content:"–";}
.st-disclosure__body{padding:0 var(--s4) var(--s4);}

/* Segmented control — a real radio group. role="tablist" is the only
   alternative. Two buttons with an .on class and no ARIA is not one. */
.st-segment{display:inline-flex;border:1px solid var(--line-ui);border-radius:var(--r-md);overflow:hidden;}
.st-segment label{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:0 var(--s4);cursor:pointer;
  font-size:var(--fs-body-sm);font-weight:500;color:var(--ink2);background:var(--surface);
  transition:background-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
}
.st-segment label + label{border-left:1px solid var(--line-ui);}
.st-segment input{position:absolute;opacity:0;width:1px;height:1px;}
.st-segment input:checked + span,.st-segment label:has(input:checked){background:var(--acc-fill);color:#fff;}
.st-segment label:has(input:focus-visible){outline:3px solid var(--acc-line);outline-offset:-3px;}

/* File drop zone and list */
.st-drop{
  border:1px dashed var(--line-ui);border-radius:var(--r-lg);background:var(--bg);
  padding:var(--s6);text-align:center;
  transition:border-color var(--dur-1) var(--ease),background-color var(--dur-1) var(--ease);
}
.st-drop.is-over{border-color:var(--acc-line);background:color-mix(in srgb,var(--acc) 6%,transparent);}
.st-filelist{list-style:none;margin:var(--s3) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--s2);}
.st-filelist li{
  display:flex;align-items:center;gap:var(--s3);
  border:1px solid var(--line);border-radius:var(--r-md);padding:var(--s2) var(--s3);
  font-family:var(--mono);font-size:var(--fs-small);color:var(--ink2);background:var(--surface);
}
.st-filelist button{margin-left:auto;min-width:44px;min-height:44px;background:transparent;border:0;color:var(--mut);cursor:pointer;font-size:16px;}
.st-filelist button:hover{color:var(--oxide);}


/* =====================================================================
   15. CALLOUT, EVIDENCE, RAIL, CTA, TABLE
   ===================================================================== */

/* 7.16 + 7.21 Honesty callout, banner and alert are one component.
   The --honest variant is a signature of the positioning: it says plainly
   what is not proven. */
.st-callout{
  border:1px solid var(--line);border-left:3px solid var(--acc-line);
  border-radius:var(--r-lg);background:var(--surface);
  padding:var(--s4) var(--s5);
}
.st-callout > :last-child{margin-bottom:0;}
.st-callout--info   {border-left-color:var(--cobalt);}
.st-callout--caution{border-left-color:var(--amber);}
.st-callout--honest {border-left-color:var(--line-ui);background:var(--bg);}
.st-callout__label{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:1px;text-transform:uppercase;color:var(--mut);margin:0 0 var(--s2);}
/* Round 3: a callout sitting directly above a price row needs real
   clearance, or a badge astride the top of the first card touches the
   callout's own edge. Scoped to the adjacent-sibling case only. */
.st-callout + .st-prices{margin-top:var(--s7);}

/* 7.15 Evidence card: a claim, its grade, its source. Available on every
   page, not just the method page. Left border is green by default (the
   common case, a pooled meta-analysis) with two modifiers so the rail
   always agrees with the grade chip inside it, never contradicts it. */
.st-evidence{background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--green);border-radius:var(--r-lg);padding:var(--s5);}
.st-evidence--review{border-left-color:var(--amber);}
.st-evidence--rejected{border-left-color:var(--oxide);}
.st-evidence__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin-bottom:var(--s3);flex-wrap:wrap;}
/* One measure for the whole card, so the claim and the copy under it don't
   step in and out at different widths. Round 3: bumped 16->17px, the
   title (.st-h3-lg in markup) to 24px and the claim to 500/17px, so the
   card has a real two-step hierarchy instead of a 4px gap at one weight. */
.st-evidence__claim{font-size:17px;font-weight:500;line-height:1.4;letter-spacing:-.3px;color:var(--ink);max-width:62ch;margin:0 0 var(--s3);}
.st-evidence .st-body-sm{max-width:62ch;}
/* Round 3: citations were 11px mono, a legibility failure for running
   text (contrast measured fine; size and face did not). Sans, 12.5px. */
.st-evidence__prov{font-family:var(--sans);font-size:12.5px;color:var(--mut);line-height:1.7;margin:0;}
/* Round 3: the "In Stica, this means" line was a third identical
   .st-body-sm paragraph, distinguished only by a bold lead-in. Give it a
   hairline and a tinted inset so it reads as the card's own conclusion,
   not another caveat. One card (habit timelines) was missing it; every
   evidence card on the page now carries one. */
.st-evidence__stica{
  margin-top:var(--s3);padding-top:var(--s3);border-top:1px solid var(--line);
}
.st-evidence__stica-label{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:1px;
  text-transform:uppercase;color:var(--acc-text);margin:0 0 4px;
}
.st-evidence__stica p{font-size:var(--fs-body-sm);line-height:1.55;color:var(--ink2);max-width:62ch;margin:0;}

/* 7.15b Effect-size bar: the numeric encoding an evidence card needs
   beside its claim. A fixed track, a filled portion sized by inline style
   (the one sanctioned case of a one-off data-driven position — see
   BUILD-BRIEF §1), and an optional tick marking a reference point. The
   fill colour matches the card's grade, the same three-state logic as the
   chip and the left border, so all three signals on a card always agree.
   Only ever used for measures on one shared 0-1 scale (g, d) — a card
   whose number is a different kind of statistic (r, R², a day count)
   does not get this mark; see .st-range below for a range instead. */
.st-effect{display:flex;align-items:center;gap:var(--s3);margin:var(--s3) 0 var(--s4);}
.st-effect__track{
  position:relative;display:block;flex:1;max-width:200px;height:8px;
  background:var(--line);border-radius:var(--r-full);
}
.st-effect__fill{position:absolute;top:0;left:0;bottom:0;border-radius:var(--r-full);background:var(--green);}
.st-effect--review .st-effect__fill{background:var(--amber);}
.st-effect--rejected .st-effect__fill{background:var(--oxide);}
.st-effect__tick{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--line-ui);}
.st-effect__val{font-family:var(--mono);font-size:var(--fs-small);color:var(--ink2);white-space:nowrap;flex:none;}

/* 7.15c Range mark: two labelled endpoints and an average tick, for a
   measure that is a spread (e.g. 18-254 days) rather than a 0-1 effect
   size. Visually distinct from .st-effect on purpose — two dots, not a
   fill-from-zero bar — so it can never be misread as the same scale. */
.st-range{display:flex;align-items:center;gap:var(--s3);margin:var(--s3) 0 var(--s4);}
.st-range__track{
  position:relative;display:block;flex:1;max-width:200px;height:8px;
  background:var(--line);border-radius:var(--r-full);
}
.st-range__bar{position:absolute;top:0;bottom:0;border-radius:var(--r-full);background:var(--amber);opacity:.45;}
.st-range__dot{
  position:absolute;top:50%;width:9px;height:9px;border-radius:var(--r-dot);
  background:var(--amber);transform:translate(-50%,-50%);
}
.st-range__tick{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--line-ui);}
.st-range__val{font-family:var(--mono);font-size:var(--fs-small);color:var(--ink2);white-space:nowrap;flex:none;}

/* 7.25 Cross-sell rail: the "part of the line" strip. */
.st-rail{border-top:1px solid var(--line);background:var(--surface);}
.st-rail__inner{
  max-width:var(--w-page);margin-inline:auto;padding:var(--s8) var(--gutter);
  display:flex;align-items:center;gap:var(--s5);flex-wrap:wrap;
}
.st-rail__links{display:flex;gap:var(--s3);flex-wrap:wrap;}
.st-rail__links a{
  font-size:13.5px;font-weight:600;text-decoration:none;color:var(--ink);
  border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--s3) var(--s4);
  background:var(--bg);min-height:44px;display:inline-flex;align-items:center;
  transition:border-color var(--dur-1) var(--ease);
}
.st-rail__links a:hover{border-color:var(--acc-line);}

/* 7.26 Final CTA block. */
.st-cta{text-align:center;padding-block:var(--s10);}
/* Round 3 (teams): the one page-closing CTA that is meant to close a sale
   gets the roomy step instead of the shared 96px default. The ≤920px and
   ≤640px overrides below (both plain .st-cta, equal specificity, later in
   the cascade) still win at those widths, so this only ever adds air on
   a desktop canvas that has it to spare. */
.st-cta--roomy{padding-block:var(--sec-y-roomy);}
/* Round 3: four live pages (index, method, coaches, and now learn) were
   each setting this inline per instance to centre the lede's own box
   under a centred heading. Same fix as .st-sechead--centre already gets;
   promoted so no page has to say it by hand. */
.st-cta .st-lede{margin-inline:auto;}
.st-cta__flourish{width:260px;height:auto;margin:0 auto var(--s2);display:block;}
/* Visible by default (rule 15: with JS off, everything is visible). The
   path carries pathLength="1" so dasharray/dashoffset are both in the
   0..1 unit space regardless of the SVG's own drawn length. Only under
   .st-motion-ready does it start hidden and draw in on reveal. */
.st-cta__flourish path{
  fill:none;stroke:var(--acc-line);stroke-width:2;stroke-linecap:round;
  stroke-dasharray:1;stroke-dashoffset:0;
}
.st-motion-ready .st-cta__flourish path{stroke-dashoffset:1;}
.st-motion-ready .st-cta__flourish.st-drawn path{
  animation:st-draw var(--dur-6) var(--ease) forwards;
}
@keyframes st-draw{to{stroke-dashoffset:0;}}

/* 7.20 Table. A wide table scrolls inside itself and never scrolls the page.
   A row may open with <th scope="row"> instead of <td> for accessible row
   headers; it reads as body copy, not as a column-header label, so it stays
   legible at the sizes people actually read. */
.st-tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch;}
/* min-width is what makes overflow-x:auto actually engage on a plain table
   (no th[scope=row]) at narrow widths, rather than squeezing every column
   until it wraps onto three lines. width:100% still governs the wide case. */
.st-table{width:100%;min-width:640px;border-collapse:collapse;font-size:var(--fs-body-sm);}
.st-table th{
  font-family:var(--mono);font-size:var(--fs-label);font-weight:500;
  letter-spacing:1px;text-transform:uppercase;color:var(--mut);
  text-align:left;padding:var(--s3) var(--s4) var(--s3) 0;border-bottom:1px solid var(--line);
}
.st-table tbody th{
  font-family:var(--sans);font-size:var(--fs-body-sm);font-weight:500;
  letter-spacing:normal;text-transform:none;color:var(--ink2);
}
.st-table td{padding:var(--s3) var(--s4) var(--s3) 0;border-bottom:1px solid var(--line);color:var(--ink2);vertical-align:top;}
.st-table tr:last-child td{border-bottom:0;}
/* Round 3: the row-header cell (<th scope="row">) carries its own
   border-bottom from the base .st-table th rule above and was never
   cleared on the last row, so the closing hairline stopped dead at column
   two instead of running the full row width. */
.st-table tr:last-child th{border-bottom:0;}
/* Below 640px a table that uses accessible row headers restacks as paired
   rows: each cell shows its own column label, so nothing is read by
   position alone. Opt in with <th scope="row"> on the first cell of every
   <tr> and data-label="<column header>" on every <td> after it. Scoped to
   :has(th[scope="row"]) on purpose, so a plain data table with no row
   headers (e.g. the evidence table on /method) keeps its own header row
   and simple horizontal scroll instead of losing its column context. */
@media (max-width:640px){
  /* Round 3: the base .st-table rule sets min-width:640px so overflow-x
     scrolling engages on an ordinary table. This variant restacks instead
     of scrolling, but that min-width was never reset, so the table stayed
     640px wide inside a 390px viewport, every value's flex-based
     space-between threw it off to the right edge at x≈640, and the whole
     section rendered as row headings with nothing beside them. */
  .st-table:has(th[scope="row"]){min-width:0;}
  .st-tablewrap:has(.st-table:has(th[scope="row"])){overflow-x:visible;}
  .st-table:has(th[scope="row"]) thead{
    position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
    clip:rect(0 0 0 0);white-space:nowrap;border:0;
  }
  .st-table:has(th[scope="row"]),
  .st-table:has(th[scope="row"]) tbody,
  .st-table:has(th[scope="row"]) tr,
  .st-table:has(th[scope="row"]) th,
  .st-table:has(th[scope="row"]) td{display:block;width:100%;}
  .st-table:has(th[scope="row"]) tr{padding:var(--s3) 0;border-bottom:1px solid var(--line);}
  .st-table:has(th[scope="row"]) tr:last-child{border-bottom:0;}
  .st-table:has(th[scope="row"]) tbody th{
    font-weight:700;color:var(--ink);padding:0 0 var(--s2);border-bottom:0;
  }
  .st-table:has(th[scope="row"]) td{
    border-bottom:0;padding:2px 0;
    display:flex;align-items:baseline;justify-content:space-between;gap:var(--s3);
  }
  .st-table:has(th[scope="row"]) td::before{
    content:attr(data-label);flex:none;
    font-family:var(--mono);font-size:var(--fs-label);letter-spacing:1px;
    text-transform:uppercase;color:var(--mut);
  }
}

/* Breadcrumb — for any page two levels deep. */
.st-crumb{font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);margin:0 0 var(--s4);}
.st-crumb a{color:var(--mut);text-decoration:none;}
.st-crumb a:hover{color:var(--ink2);text-decoration:underline;text-underline-offset:3px;}
.st-crumb span{margin:0 var(--s2);}


/* =====================================================================
   16. PAPER OBJECTS
   Shadows signal objecthood. Paper gets --shadow-stuck or --shadow-paper;
   tiles, cards, chips and buttons get nothing at rest.
   ONE .st-stuck per page, maximum, on a standalone note or artefact,
   never on a grid tile or a repeated list.
   The white paper rule: in dark mode the board darkens, pinned paper does
   not. .st-stuck and .st-paper carry their own light palette.
   ===================================================================== */

.st-stuck{
  /* Round 3: sizing promoted from a per-instance inline style (the only
     page that used the component was setting max-width and padding by
     hand) to a sensible default. A page with a real reason to differ can
     still override width via a wrapping element with a max-width utility;
     this removes the need to do it inline in the common case. */
  position:relative;background:var(--surface);color:var(--ink);
  max-width:340px;padding:var(--s5);
  border:1px solid color-mix(in srgb,var(--ink) 6%,transparent);
  border-radius:var(--r-sm);
  transform:rotate(var(--tilt,-1.4deg));
  background-image:
    linear-gradient(180deg,rgba(255,255,255,.6),rgba(255,255,255,0) 9px),
    linear-gradient(0deg,rgba(40,34,20,.035),rgba(40,34,20,0) 14px);
  box-shadow:var(--shadow-stuck);
  transition:transform var(--dur-3) var(--ease),box-shadow var(--dur-3) var(--ease);
}
/* Same convention as .st-tile and .st-step__body: the last line inside
   never carries its own trailing margin, so the padding box closes clean
   without an inline margin:0 per instance. */
.st-stuck > :last-child{margin-bottom:0;}
.st-stuck:hover{
  transform:rotate(calc(var(--tilt,-1.4deg) * .35)) translateY(-3px) scale(1.014);
  box-shadow:0 2px 3px rgba(28,24,14,.06),2px 5px 9px rgba(28,24,14,.08),
             5px 12px 22px rgba(28,24,14,.10),9px 24px 40px rgba(28,24,14,.11),
             14px 36px 60px rgba(28,24,14,.07);
}
.st-stuck::before{
  content:"";position:absolute;top:-9px;left:calc(50% + var(--tapex,0px));
  transform:translateX(-50%) rotate(var(--tape,-1.5deg));
  width:98px;height:28px;z-index:2;
  background:linear-gradient(180deg,rgba(255,255,255,.34),rgba(255,255,255,.05) 48%,rgba(70,58,28,.06)),
             repeating-linear-gradient(90deg,rgba(70,58,28,.05) 0 1px,transparent 1px 4px),
             rgba(214,201,158,.5);
  box-shadow:0 2px 5px rgba(28,24,14,.14);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 11%,#000 89%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 11%,#000 89%,transparent);
}
.st-stuck--pin::before{
  width:18px;height:18px;border-radius:var(--r-dot);top:-7px;z-index:3;
  background:radial-gradient(circle at 36% 30%,#ffb3b3 0 10%,#ff7a7a 32%,var(--acc) 62%,
             color-mix(in srgb,var(--acc) 62%,#000) 100%);
  box-shadow:0 3px 6px rgba(28,24,14,.4),inset -2px -2px 4px rgba(0,0,0,.35),
             inset 2px 2px 3px rgba(255,255,255,.6);
  -webkit-mask-image:none;mask-image:none;
}
/* The white paper rule, both dark entry points. */
:root[data-theme="dark"] .st-stuck{
  --surface:#FCFBF7; --ink:#111110; --ink2:#33332B; --mut:#6B6B61; --line:#E2E2DC;
  background:#FCFBF7;border-color:rgba(0,0,0,.14);
  box-shadow:0 1px 2px rgba(0,0,0,.5),4px 10px 22px rgba(0,0,0,.55),10px 26px 50px rgba(0,0,0,.42);
}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-stuck{
    --surface:#FCFBF7; --ink:#111110; --ink2:#33332B; --mut:#6B6B61; --line:#E2E2DC;
    background:#FCFBF7;border-color:rgba(0,0,0,.14);
    box-shadow:0 1px 2px rgba(0,0,0,.5),4px 10px 22px rgba(0,0,0,.55),10px 26px 50px rgba(0,0,0,.42);
  }
}

/* Torn-paper machinery. Kept, but do NOT build a page that depends on it
   until real photography exists. Placeholder imagery must not ship. */
.st-photo{position:relative;display:inline-block;}
.st-paper{background:#fff;padding:10px 10px 4px;filter:var(--shadow-paper);}
.st-photo .win{display:block;line-height:0;overflow:hidden;}
.st-photo .win img{width:100%;height:100%;object-fit:cover;display:block;}
.st-photo .cap{font-family:var(--hand);font-weight:700;color:#111110;padding:9px 4px 2px;line-height:1.05;background:#fff;}
.st-photo .cap small{font-family:var(--mono);font-weight:400;font-size:var(--fs-chip);letter-spacing:1px;color:#6B6B61;display:block;margin-top:4px;text-transform:uppercase;}
.st-paper.tear-left{clip-path:polygon(6% 0%,100% 0%,100% 100%,4% 100%,3.59% 100%,0.12% 98.95%,1.03% 95.72%,0.25% 92.63%,3.79% 89.87%,3.95% 85.9%,0.14% 83.26%,0.16% 80.25%,3.52% 76.05%,0.21% 71.75%,3.63% 66.64%,2.64% 61.49%,0.4% 58.78%,2.36% 54.38%,0.09% 49.67%,1.18% 46.84%,1.09% 43.45%,2.58% 40.6%,0.46% 35.94%,0.48% 31.7%,3.58% 27.77%,2.61% 24.69%,1.19% 20.57%,0.33% 16.65%,0.79% 12.83%,0.77% 7.83%,1.01% 3.78%,0.56% 0.0%);padding-left:16px;}
.st-paper.tear-right{clip-path:polygon(0% 0%,96.07% 0.0%,99.71% 3.46%,99.69% 6.83%,97.48% 9.62%,97.39% 12.45%,99.33% 16.57%,96.15% 19.8%,97.32% 23.96%,97.48% 27.53%,99.56% 31.31%,99.84% 36.4%,98.93% 40.26%,99.21% 44.35%,97.74% 49.2%,99.98% 52.28%,99.16% 55.28%,99.97% 60.24%,99.42% 64.97%,99.62% 68.22%,99.91% 71.31%,99.12% 75.83%,99.12% 80.88%,99.05% 83.99%,99.58% 89.06%,98.92% 93.95%,99.5% 98.12%,98.96% 100%,0% 100%);padding-right:16px;}
.st-paper.tear-top{clip-path:polygon(0.0% 3.42%,4.22% 3.83%,7.82% 0.79%,11.93% 1.07%,16.12% 2.98%,18.93% 0.97%,23.19% 1.06%,28.37% 2.45%,33.26% 0.6%,37.75% 1.02%,41.36% 2.5%,45.87% 2.59%,49.98% 0.5%,53.73% 3.71%,58.51% 1.18%,61.33% 3.77%,65.88% 0.05%,68.55% 3.62%,72.72% 3.02%,76.57% 0.03%,79.77% 2.28%,84.18% 0.76%,88.07% 0.76%,92.27% 0.97%,97.27% 3.06%,100% 3.05%,100% 100%,0% 100%);padding-top:16px;}
.st-paper.tear-bottom{clip-path:polygon(0% 0%,100% 0%,100% 99.4%,99.01% 96.84%,95.21% 98.84%,90.15% 99.75%,85.48% 98.99%,80.6% 99.63%,76.52% 98.99%,72.94% 99.23%,67.97% 96.49%,63.74% 97.4%,59.97% 98.87%,57.03% 98.99%,52.55% 99.38%,47.52% 97.53%,44.7% 99.79%,39.74% 99.92%,35.08% 96.34%,31.46% 99.34%,26.89% 99.9%,22.7% 98.97%,18.74% 99.62%,14.33% 99.36%,10.83% 98.82%,7.52% 96.18%,4.46% 99.02%,0.0% 99.75%);padding-bottom:14px;}
.st-tape{position:absolute;width:90px;height:23px;background:rgba(231,219,176,.74);border-left:1px dashed rgba(0,0,0,.07);border-right:1px dashed rgba(0,0,0,.07);}
.st-pin{position:absolute;width:15px;height:15px;border-radius:var(--r-dot);background:var(--acc);box-shadow:0 2px 4px rgba(0,0,0,.35),inset -2px -2px 3px rgba(0,0,0,.25);}
.st-pin::after{content:"";position:absolute;top:3px;left:3px;width:5px;height:5px;border-radius:var(--r-dot);background:rgba(255,255,255,.6);}


/* =====================================================================
   17. MOTION
   Opacity and transform. That is the whole list for anything that moves,
   plus background-size on the sweep and stroke-dashoffset on a drawn line.
   Never a layout property. Nothing loops. No parallax, no scroll-jacking,
   no pinned sections, no carousel, no autoplaying video.

   The reveal gate: everything hidden for motion is hidden ONLY under
   .st-motion-ready, which stica.js adds on load. With JS off, everything
   is visible. Never write a hide-state that is not gated this way.

   RETIRED 2026-08-06 and not to be rebuilt: the cursor-following dot, the
   hover ring and the click ink ripple. A permanent mousemove handler and a
   loop that never stops, invisible on the platform that matters most.
   ===================================================================== */

.st-motion-ready .st-reveal{
  opacity:0;transform:translateY(14px);
  transition:opacity var(--dur-4) var(--ease),transform var(--dur-4) var(--ease);
  will-change:opacity,transform;
}
.st-motion-ready .st-reveal.st-visible{opacity:1;transform:none;}
.st-motion-ready .st-reveal[data-reveal-order="2"]{transition-delay:60ms;}
.st-motion-ready .st-reveal[data-reveal-order="3"]{transition-delay:120ms;}
.st-motion-ready .st-reveal[data-reveal-order="4"]{transition-delay:180ms;}
.st-motion-ready .st-reveal[data-reveal-order="5"]{transition-delay:240ms;}

/* Highlighter sweep. Round 3: background-size animates per LINE BOX, so a
   phrase that wraps onto two lines paints two independent square-ended
   bars that grow at once, reading as an accidental text selection rather
   than one pen stroke — worst on mobile, where the wrap is near-certain.
   white-space:nowrap keeps every swept phrase on one line; only ever wrap
   a short phrase in this span. */
.st-sweep{
  /* nowrap stays: without it a wrapped phrase paints two independent
     square-ended bars (see the block comment above), which reads as an
     accidental text selection rather than a highlighter stroke. Every
     current usage is a short (three-word) fragment for exactly this
     reason; keep it that way rather than removing nowrap. */
  position:relative;display:inline;white-space:nowrap;
  /* Round 3 (method): 18% read as not-there-at-all against cream/white —
     the one live usage besides this page (coaches) gets more legible too. */
  --sweep:color-mix(in srgb,var(--acc) 30%,transparent);
  background-image:linear-gradient(var(--sweep),var(--sweep));
  background-repeat:no-repeat;background-position:left center;background-size:100% 55%;
  border-radius:var(--r-sm);padding-bottom:1px;
}
.st-motion-ready .st-sweep{
  background-size:0% 55%;
  transition:background-size var(--dur-5) var(--ease);
  will-change:background-size;
}
.st-motion-ready .st-sweep.st-drawn{background-size:100% 55%;}

/* A drawn line: the spine, a ring, the CTA flourish */
.st-ring-wrap{position:relative;display:inline-block;}
.st-ring-wrap svg.st-ring{
  position:absolute;pointer-events:none;overflow:visible;
  top:50%;left:50%;transform:translate(-50%,-50%);
  width:calc(100% + 28px);height:calc(100% + 24px);
}
.st-ring path{
  fill:none;stroke:var(--acc-line);stroke-width:2;stroke-linecap:round;
  vector-effect:non-scaling-stroke;stroke-dasharray:320;stroke-dashoffset:0;
}
.st-motion-ready .st-ring path{
  stroke-dashoffset:320;
  transition:stroke-dashoffset var(--dur-6) var(--ease);
  will-change:stroke-dashoffset;
}
.st-motion-ready .st-ring.st-drawn path{stroke-dashoffset:0;}

/* Hand annotation fade-in */
.st-motion-ready .st-note,
.st-motion-ready .st-note-sm,
.st-motion-ready .st-note-xs{opacity:0;transition:opacity var(--dur-2) ease 200ms;}
.st-motion-ready .st-note.st-drawn,
.st-motion-ready .st-note-sm.st-drawn,
.st-motion-ready .st-note-xs.st-drawn{opacity:1;}


/* =====================================================================
   18. BREAKPOINTS
   Exactly two. Every page uses these and only these. Anything else is a bug.
   Mobile-first is non-negotiable: check 375px before calling a page done,
   and it must reflow cleanly at 320px.
   ===================================================================== */

@media (max-width:920px){
  :root{--gutter:24px;--sec-y:var(--s8);--sec-y-roomy:var(--s9);}
  .st-split,.st-hero-split,.st-cols-2{grid-template-columns:1fr;gap:var(--s6);}
  .st-cols-3{grid-template-columns:1fr;}
  .st-cols-4{grid-template-columns:1fr 1fr;}
  .st-spread,.st-spread--flip{grid-template-columns:1fr;gap:var(--s5);}
  .st-spread--flip .st-spread__body{order:1;}
  .st-spread--flip .st-spread__rail{order:2;}
  .st-spread__rail,.st-spread--flip .st-spread__rail{
    border-left:0;border-right:0;border-top:1px dashed var(--line2);
    padding:var(--s4) 0 0;text-align:left;
  }
  .st-hero,.st-hero-split{padding-block:var(--s8) var(--s7);}
  /* Decorative side art hides. */
  .st-decor{display:none;}
  /* Round 2: nav collapses HERE, not at 640. Five links plus two pills
     need ~780px to sit on one line without squeezing; below 920 the row
     used to shrink each pill until its own text wrapped onto two lines,
     which also dragged the wordmark highlight out of place two
     breakpoints up (fixed independently in .st-brand, but the crowd was
     the trigger). The primary action stays on screen so a visitor never
     has to open the menu to find the one thing the header is for. */
  .st-nav{display:flex;gap:var(--s3);}
  .st-nav a:not(.st-btn--pill){display:none;}
  .st-nav .st-btn--pill-outline{display:none;}
  /* Round 3 (teams): .st-head__inner has three flex children here (brand,
     nav, toggle); space-between turns the middle one (now just the pill)
     into a dead-centre floater, anchored to nothing. Auto margins are
     resolved before justify-content distributes remaining space, so this
     pulls nav flush against the toggle and both flush against the right
     edge, leaving space-between nothing left to distribute — brand stays
     pinned left, exactly as before. No markup change, so every page using
     this header is fixed at once. */
  .st-nav{margin-left:auto;}
  .st-navtoggle{display:inline-flex;}
  /* Final CTA keeps step with the section rhythm here instead of holding
     its full 96px at every width. */
  .st-cta{padding-block:var(--s8);}
}

@media (max-width:640px){
  :root{--gutter:20px;--sec-y:var(--s7);--sec-y-roomy:var(--s8);}
  .st-cols-4{grid-template-columns:1fr;}
  .st-prices{grid-template-columns:1fr;}
  .st-cta{padding-block:var(--s7);}
  /* Hand notes stop being absolutely positioned and flow inline. */
  .st-note,.st-note-sm,.st-note-xs{position:static !important;transform:none;display:block;}
  .st-brand{font-size:26px;}
  .st-footer__inner{flex-direction:column;align-items:flex-start;gap:var(--s4);}
  .st-footer__nav{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3) var(--s5);width:100%;}
}

/* Round 3: a hero's own CTA row goes full-width and stacks below 480px.
   flex-wrap already stopped the two buttons overlapping at any width, but
   two uneven-width buttons left-aligned and wrapped read as an accident,
   not a decision. Scoped to .st-hero__cta so ordinary button rows
   (the mobile nav panel, a form's actions) are unaffected. */
@media (max-width:480px){
  .st-hero__cta .st-btnrow{flex-direction:column;align-items:stretch;}
  .st-hero__cta .st-btnrow > a{width:100%;text-align:center;}
}

/* Hero-split price-first: opt-in modifier for a hero that pairs the pitch
   with a price/action card rather than an illustration or note. On a
   narrow screen the card comes first, so the cost is visible before a
   long scroll through the pitch. The ordinary hero-split pages (text
   beside a note or a panel) do not carry this class and are unaffected. */
@media (max-width:920px){
  .st-hero-split--price-first > :first-child{order:2;}
  .st-hero-split--price-first > :last-child{order:1;}
}


/* =====================================================================
   19. REDUCED MOTION
   Nothing on this site conveys information through motion, so reducing it
   loses nothing. Every page must be checked with the setting on.
   ===================================================================== */

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
  /* Final frames, explicitly. */
  .st-motion-ready .st-reveal{opacity:1 !important;transform:none !important;}
  .st-motion-ready .st-sweep{background-size:100% 55% !important;}
  .st-motion-ready .st-ring path{stroke-dashoffset:0 !important;}
  .st-motion-ready .st-note,
  .st-motion-ready .st-note-sm,
  .st-motion-ready .st-note-xs{opacity:1 !important;}
  .st-cta__flourish path{stroke-dashoffset:0 !important;animation:none !important;}
  .st-stuck{transition:none;}
  .st-stuck:hover{transform:rotate(var(--tilt,-1.4deg));}
}


/* =====================================================================
   20. UTILITIES — the only ones. Do not add to this list per page.
   ===================================================================== */

.st-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.st-centre{text-align:center;}
.st-stack{display:flex;flex-direction:column;}
.st-stack-2{gap:var(--s2);} .st-stack-3{gap:var(--s3);} .st-stack-4{gap:var(--s4);}
.st-stack-5{gap:var(--s5);} .st-stack-6{gap:var(--s6);}
.st-flow > * + *{margin-top:var(--s4);}

/* ---------------------------------------------------------------------------
   THEME TOGGLE (added 2026-08-07)
   The site had a complete dark palette and no way for a visitor to ask for it:
   data-theme="auto" followed the device and nothing else. This is the control.
   The icon shows what the NEXT click gives you, which is what its label says
   too: a moon while you are in light, a sun while you are in dark.
   Wired by assets/theme.js, which also carries the choice to app.stica.co.uk.
   --------------------------------------------------------------------------- */
.st-themetoggle{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:34px;height:34px;padding:0;
  border:1px solid var(--line);border-radius:999px;
  background:transparent;color:var(--ink2);cursor:pointer;
  transition:color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease);
}
.st-themetoggle:hover{color:var(--ink);border-color:var(--line-ui);}
.st-themetoggle:focus-visible{outline:2px solid var(--acc-line);outline-offset:2px;}
.st-themetoggle svg{display:block;}
.st-themetoggle__sun{display:none;}
:root[data-theme="dark"] .st-themetoggle__sun{display:block;}
:root[data-theme="dark"] .st-themetoggle__moon{display:none;}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-themetoggle__sun{display:block;}
  :root[data-theme="auto"] .st-themetoggle__moon{display:none;}
}
/* In the mobile panel it sits in the button row and earns a label, because a
   bare icon in a full-width stack reads as a mistake rather than a control. */
.st-navpanel .st-themetoggle{width:auto;height:auto;gap:8px;padding:10px 16px;border-radius:var(--r-lg);}
.st-navpanel .st-themetoggle::after{content:"Dark mode";font-size:var(--fs-body-sm);}
:root[data-theme="dark"] .st-navpanel .st-themetoggle::after{content:"Light mode";}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-navpanel .st-themetoggle::after{content:"Light mode";}
}
