/* bradatiguru.hr design system. Hand-written, served as-is, no build step.
   Budget 24576 B raw (raised from 18432 on 2026-08-21 -- fix round 1; see
   BUDGETS in lib/publish.js). Order is contractual: 1 fonts, 2 reset,
   3 tokens, 4 base, 5 primitives, 6 motion, 7 components. Later tasks
   append to 7. */

/* 1. FONTS. latin-ext is declared after latin so it wins the codepoints the
   two subsets share; Croatian needs it. Variable ranges, one file each. */
@font-face{font-family:Archivo;font-weight:400 900;font-display:swap;src:url(/fonts/archivo-latin.woff2)format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:Archivo;font-weight:400 900;font-display:swap;src:url(/fonts/archivo-latin-ext.woff2)format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'JetBrains Mono';font-weight:400 500;font-display:swap;src:url(/fonts/jetbrains-mono-latin.woff2)format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'JetBrains Mono';font-weight:400 500;font-display:swap;src:url(/fonts/jetbrains-mono-latin-ext.woff2)format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

/* 2. RESET */
*,::before,::after{box-sizing:border-box}
body,h1,h2,h3,p,ul,ol,dl,dd,figure,blockquote,fieldset,pre{margin:0;padding:0}
ul,ol{list-style:none}
fieldset{border:0}
img{max-width:100%;height:auto}
body{background:var(--paper);color:var(--ink);font:400 16px/1.55 var(--font-sans);-webkit-font-smoothing:antialiased}

/* 3. TOKENS. Every colour, border, shadow, motion and layer value from the
   inventory, plus the type scale. Single-use type values live in the rule
   that owns them instead of here -- see the task report. */
:root{
--paper:#f2f1ee;--ink:#0d0d0d;--accent:#1a3d8f;
--text-body:#44443f;--text-list:#2e2e2a;--text-muted:#55554f;--text-lede:#33332f;
--hairline-soft:#d9d8d3;--ghost:#d3d2cc;--field-bg:#fbfaf8;--pre-text:#e8e7e2;
--on-ink-86:rgba(242,241,238,.86);--on-ink-72:rgba(242,241,238,.72);
--on-ink-65:rgba(242,241,238,.65);--on-ink-60:rgba(242,241,238,.6);
--on-ink-55:rgba(242,241,238,.55);
--font-sans:Archivo,Helvetica,sans-serif;--font-mono:'JetBrains Mono',monospace;
--t-h1-home:800 clamp(34px,8vw,124px)/.98 var(--font-sans);
--t-h2-ctaband:900 clamp(32px,4.4vw,68px)/1.02 var(--font-sans);
--t-h2-darksplit:800 clamp(28px,3vw,44px)/1 var(--font-sans);
--t-h3-card:800 clamp(26px,2.5vw,38px)/1.02 var(--font-sans);
--t-h3-split:800 clamp(26px,2.4vw,36px)/1.05 var(--font-sans);
--t-h3-process:700 21px/1.15 var(--font-sans);
--t-h3-item:700 17px/1.2 var(--font-sans);
--t-faq-q:700 clamp(18px,1.8vw,25px)/1.25 var(--font-sans);
--t-stat-home:800 40px/1 var(--font-sans);
--t-stat-service:900 clamp(40px,4.4vw,62px)/1 var(--font-sans);
--t-ghost-num:900 56px/1 var(--font-sans);
--t-rating:900 68px/1 var(--font-sans);
--t-card-body:400 16.5px/1.55 var(--font-sans);
--t-faq-a:400 16.5px/1.6 var(--font-sans);
--t-panel-body:400 16px/1.6 var(--font-sans);
--t-item-body:400 15.5px/1.55 var(--font-sans);
--t-footer-body:400 14.5px/1.6 var(--font-sans);
--t-chat-msg:400 14px/1.55 var(--font-sans);
--t-quote:500 19px/1.4 var(--font-sans);
--t-figcaption:500 13px/1.5 var(--font-sans);
--t-label-stat:500 12.5px/1.3 var(--font-sans);
--t-nav:500 13.5px/1 var(--font-sans);
--ls-h1:-.035em;--ls-h2:-.03em;--ls-h3:-.02em;--ls-h3-tight:-.015em;
--ls-stat:-.04em;--ls-ghost:-.05em;--ls-li:.01em;--ls-ui:.02em;--ls-rating:.03em;
--ls-field:.04em;--ls-label:.05em;--ls-chip:.06em;--ls-mono:.08em;
--ls-mono-wide:.1em;--ls-panel:.16em;--ls-index:.18em;
--container-max:1320px;--gutter:32px;--sec-pad:104px 32px 0;
--sec-pad-hero:76px 32px 64px;--sec-gap:104px;--band-pad:0 32px;
--border-ink:1px solid var(--ink);--border-soft:1px solid var(--hairline-soft);
--border-on-ink:1px solid rgba(242,241,238,.2);
--shadow-container:12px 12px 0 var(--ink);--shadow-dialog:10px 10px 0 var(--ink);
--shadow-launcher:6px 6px 0 var(--accent);--shadow-launcher-hover:6px 6px 0 var(--ink);
--shadow-cta:5px 5px 0 var(--ink);
--shadow-sm-ink:4px 4px 0 var(--ink);--shadow-sm-accent:4px 4px 0 var(--accent);
--shadow-pressed:2px 2px 0 var(--ink);--shadow-field-focus:inset 0 0 0 1px var(--accent);
--transition-press:transform .08s,box-shadow .08s;
--press-3:translate(3px,3px);--press-2:translate(2px,2px);
--z-header:90;--z-chat:120;--z-skiplink:200;
--chat-width:min(360px,calc(100vw - 32px));--chat-max-h:68vh;--chat-offset:24px;
--logo-size:26px;--eyebrow-dot:8px;--panel-dot:7px
}

/* 4. BASE. The three dominant type scales are element defaults, so a section
   pays bytes only for the sizes that differ from them. */
a{color:var(--accent);text-decoration:none}
a:hover{color:var(--ink)}
::selection{background:var(--accent);color:var(--paper)}
:focus-visible{outline:3px solid var(--accent);outline-offset:3px}
p{text-wrap:pretty}
h1,h2,h3{overflow-wrap:break-word}
h1{font:800 clamp(30px,6.4vw,100px)/1 var(--font-sans);letter-spacing:var(--ls-h1)}
h2{font:800 clamp(26px,4.6vw,68px)/1 var(--font-sans);letter-spacing:var(--ls-h2)}
h3{font:700 20px/1.2 var(--font-sans);letter-spacing:var(--ls-h3)}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
.skip-link{position:absolute;top:0;left:-9999px;z-index:var(--z-skiplink);padding:12px 16px;background:var(--ink);color:var(--paper);font:500 12px/1 var(--font-mono)}
.skip-link:focus{left:0}

/* 5. PRIMITIVES */
main>section{max-width:var(--container-max);margin:0 auto;padding:var(--sec-pad)}
/* Full-bleed band: the wrapper's two ink borders replace the grid's own. */
.band{max-width:none;padding:0;margin-top:var(--sec-gap);border-top:var(--border-ink);border-bottom:var(--border-ink)}
.wrap{max-width:var(--container-max);margin:0 auto;padding:var(--band-pad)}
/* Auto-fit columns. --n is the track floor, wrapped in min() so every grid
   collapses to one column at 320px. Set --n, --gap and --cols-gap on the
   element that reads them: custom properties inherit, so a value left on an
   ancestor is picked up by any descendant that uses the same name.
   .grid is the HAIRLINE grid (inventory 2.1) and ONLY that: no child draws a
   divider, an ink background shows through 1px gaps, and every child repaints
   paper -- which is why wrapped rows never double up. 17 of the design's 21
   auto-fit grids are this.
   The other 4 are NOT hairline grids and must use .cols instead: the footer
   column grid (--n:220px, gap 32px), the home hero split (--n:340px, gap
   56px) and the two briefDrafter field pairs (--n:180px, gap 18px). Putting
   those on .grid gives the footer 1px black gutters. */
.grid,.cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(var(--n,320px),100%),1fr))}
.grid{gap:1px;background:var(--ink)}
.grid>*{background:var(--paper)}
.cols{gap:var(--cols-gap,32px)}
.boxed{border:var(--border-ink)}
.raised{box-shadow:var(--shadow-container)}
.stack{display:flex;flex-direction:column;gap:var(--gap,18px)}
.row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--gap,14px)}
.between{justify-content:space-between}
.push{margin-left:auto}
.pad{padding:44px 38px}
.on-ink{background:var(--ink);color:var(--paper)}
/* Type helpers. Second hairline system: soft rule, only inside a paper card. */
.soft{border-top:var(--border-soft)}
.accent{color:var(--accent)}
.mono{font-family:var(--font-mono)}
.dot{width:var(--eyebrow-dot);height:var(--eyebrow-dot);flex:none;background:var(--accent)}
.eyebrow{display:flex;align-items:center;gap:14px;font:500 13px/1.4 var(--font-sans);letter-spacing:var(--ls-label);color:var(--text-muted)}
.label{font:500 12.5px/1 var(--font-sans);letter-spacing:var(--ls-label);color:var(--text-muted)}
.label-accent{font:600 12.5px/1 var(--font-sans);letter-spacing:var(--ls-label);color:var(--accent)}
.index{font:500 12px/1 var(--font-mono);letter-spacing:var(--ls-index);color:var(--accent)}
.lede{max-width:62ch;font:400 clamp(18px,1.5vw,22px)/1.5 var(--font-sans);color:var(--text-lede)}
.intro,.prose{font:400 18px/1.55 var(--font-sans);color:var(--text-body)}
.intro{max-width:60ch;margin:0 0 48px}
.body{font:var(--t-item-body);color:var(--text-body)}
.list{display:flex;flex-direction:column;gap:8px;margin:6px 0 0;font:500 14.5px/1.45 var(--font-sans);letter-spacing:var(--ls-li);color:var(--text-list)}
/* Buttons. Press-down: the shadow shrinks by exactly the translate distance,
   so its bottom-right edge stays put (inventory 2.2). Chrome buttons (header,
   chat launcher) trade shadow colour only -- they never move. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;padding:20px 26px;border:var(--border-ink);background:var(--paper);color:var(--ink);font:600 14.5px/1 var(--font-sans);letter-spacing:var(--ls-ui);text-align:center;cursor:pointer;box-shadow:var(--shadow-cta);transition:var(--transition-press)}
.btn:hover{background:var(--ink);color:var(--paper);transform:var(--press-3);box-shadow:var(--shadow-pressed)}
.btn-accent{background:var(--accent);color:var(--paper)}
/* .btn-sm and .btn-chrome are MODIFIERS: they need the base .btn class too
   (class="btn btn-sm"). Each restates its own hover box-shadow rather than
   inheriting it from .btn:hover, so the press stays correct if a later task
   ever uses one on its own or inserts a rule between them. */
.btn-sm{padding:15px 20px;border:0;background:var(--ink);color:var(--paper);font-size:14px;box-shadow:var(--shadow-sm-accent)}
.btn-sm:hover{background:var(--accent);transform:var(--press-2);box-shadow:var(--shadow-pressed)}
.btn-chrome{padding:12px 18px;border:0;background:var(--ink);color:var(--paper);font-size:13.5px;white-space:nowrap;box-shadow:var(--shadow-sm-accent)}
.btn-chrome:hover{background:var(--accent);color:var(--paper);transform:none;box-shadow:var(--shadow-sm-ink)}
.chip{padding:11px 14px;border:var(--border-ink);background:var(--paper);color:var(--ink);font:500 12px/1 var(--font-mono);letter-spacing:var(--ls-chip);transition:var(--transition-press)}
.chip[aria-pressed=true]{background:var(--ink);color:var(--paper)}
/* Fields. The prototype removed the focus ring from all five controls and gave
   two of them nothing back (D19). The global :focus-visible above is left
   alone; the border and inset ring are added on top of it, never instead. */
.field{display:flex;flex-direction:column;gap:6px}
.field>span,legend{font:500 12.5px/1 var(--font-sans);letter-spacing:var(--ls-field);color:var(--text-muted)}
input,select,textarea{width:100%;padding:13px 14px;border:var(--border-ink);background:var(--field-bg);color:var(--ink);font:400 15px/1 var(--font-sans)}
textarea{line-height:1.5;resize:vertical}
input:focus-visible,select:focus-visible,textarea:focus-visible{border-color:var(--accent);box-shadow:var(--shadow-field-focus)}
/* Hero, ctaBand and prose are the three section types P1 already built. */
.hero{padding:var(--sec-pad-hero)}
.hero .eyebrow{margin-bottom:36px}
.hero .lede{margin:44px 0 0}
.hero .actions{display:flex;flex-wrap:wrap;gap:14px;margin-top:40px}
.hero-home .eyebrow{margin-bottom:44px}
.hero-home h1{font:var(--t-h1-home);text-wrap:balance}
.hero-home .lede{max-width:56ch}
/* Fix round 1: home's lede/CTA split. One of the 4 non-hairline auto-fit
   grids (--n:340px) -- .cols, not .grid, or the 1px gap would paint an ink
   hairline through the hero. */
.hero-split{--n:340px;--cols-gap:56px;margin-top:56px;align-items:end}
.hero-home .actions{margin-top:0}
/* width:calc keeps the 32px gutter at every viewport, so the 12px shadow is
   never clipped and the band reflows to 320px without a media query. */
.cta-band{display:flex;flex-wrap:wrap;align-items:end;justify-content:space-between;gap:36px;width:calc(100% - 64px);max-width:calc(var(--container-max) - 64px);margin:var(--sec-gap) auto 0;padding:clamp(44px,6vw,84px) clamp(32px,5vw,68px);background:var(--accent);color:var(--paper);border:var(--border-ink);box-shadow:var(--shadow-container)}
.cta-band h2{max-width:22ch;font:var(--t-h2-ctaband);color:var(--paper)}
.cta-band .btn{padding:22px 28px;white-space:nowrap}
/* prose is the only long-form measure. Its links carry an underline: accent on
   ink body text is ~1.9:1, so colour alone fails 1.4.1 (inventory OQ6). */
.prose>*{max-width:74ch}
.prose>*+*{margin-top:20px}
.prose h2,.prose h3{margin-top:40px}
.prose a{text-decoration:underline}
.prose ul,.prose ol{padding-left:1.2em}
.prose ul{list-style:disc}
.prose ol{list-style:decimal}

/* 6. MOTION */
@media (prefers-reduced-motion: reduce){*,*::before,*::after{animation:none !important;transition:none !important;scroll-behavior:auto !important}}

/* 7. COMPONENTS -- section types append below this line. */

/* Header (D1: 20px gutters here, not the usual 32px -- prototype wins). */
.site-header{position:sticky;top:0;z-index:var(--z-header);background:var(--paper);border-bottom:var(--border-ink)}
.header-inner{max-width:var(--container-max);margin:0 auto;padding:12px 20px;min-height:70px;display:flex;flex-wrap:wrap;align-items:center;gap:14px 28px}
.logo{display:flex;align-items:center;gap:10px;color:var(--ink);font:900 13px/1 var(--font-sans)}
.logo-mark{display:flex;align-items:center;justify-content:center;width:var(--logo-size);height:var(--logo-size);flex:none;background:var(--ink);color:var(--paper)}
.site-nav{display:flex;flex-wrap:wrap;gap:12px 22px}
.site-nav a{color:var(--ink);font:var(--t-nav)}
.site-nav a:hover,.site-nav a[aria-current=page]{color:var(--accent)}
.lang-toggle{display:flex;border:var(--border-ink)}
.lang-toggle span,.lang-toggle a{padding:7px 11px;font:500 11px/1 var(--font-mono);letter-spacing:var(--ls-mono)}
.lang-toggle span{background:var(--ink);color:var(--paper)}
.lang-toggle a{color:var(--ink)}
.lang-toggle a:hover{background:var(--accent);color:var(--paper)}
.lang-toggle>:last-child{border-left:var(--border-ink)}

/* Footer: full-bleed ink band. Every direct child shares one centring rule
   below, so the always-present .colophon (site.footer may be absent) needs
   no wrapper of its own. */
.site-footer{margin-top:var(--sec-gap);border-top:var(--border-ink);background:var(--ink);color:var(--paper)}
.site-footer>*{max-width:var(--container-max);margin:0 auto;padding-left:32px;padding-right:32px}
.site-footer a,.site-footer .list{color:var(--on-ink-86)}
.site-footer a:hover{color:var(--paper)}
.site-footer .label{color:var(--on-ink-60)}
.footer-legal{padding-top:72px;font:var(--t-footer-body);font-style:normal;color:var(--on-ink-72)}
.footer-cols{--n:220px;border-top:var(--border-on-ink);margin-top:36px;padding-top:36px}
.colophon{margin-top:44px;padding-top:20px;padding-bottom:40px;border-top:var(--border-on-ink);font:500 11px/1.4 var(--font-mono);color:var(--on-ink-72)}

/* Service-hero backlink ("<- All services"). Colour/hover come free from the
   global a{} / a:hover{} rules (2.2: accent, hover ink) -- only type is new. */
.backlink{display:block;margin-bottom:40px;font:var(--t-nav);letter-spacing:var(--ls-ui);white-space:nowrap}

/* Shared section-header row: statsBand has none of its own, but cardGrid,
   processCards and every later h2-owning type in this catalogue open with
   this exact row. */
.section-head{display:flex;align-items:baseline;gap:20px;margin-bottom:40px}

/* statsBand (4.2): two variants, one .stats grid. It cannot carry the .grid
   class itself -- the test pins <dl class="stats"> as the whole attribute --
   so it repeats .grid's three declarations under its own name instead of
   combining classes; --n and the ink/paper split still come from the
   variant's own wrapper the same way every other grid reads them. .band
   already carries margin-top:104px for its next tenant (marquee/footer); both
   statsBand wrappers sit flush under a hero instead, so that margin is
   cancelled here, not in the shared primitive. */
.stats-home,.stats-service{margin-top:0}
.stats-home{background:var(--ink);--n:220px}
.stats-service{--n:240px}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(var(--n,240px),100%),1fr));gap:1px;background:var(--ink)}
.stats>div{background:var(--paper)}
.stats-home .stats>div{padding:34px 26px}
.stats-service .stats>div{padding:44px 30px}
.stats-home dt{font:var(--t-label-stat);color:var(--text-muted);letter-spacing:var(--ls-label)}
.stats-home dd{margin:12px 0 0;font:var(--t-stat-home);letter-spacing:var(--ls-h2)}
.stats-service .value{font:var(--t-stat-service);letter-spacing:var(--ls-stat)}
.stats-service p{margin:14px 0 0;font:var(--t-item-body)}

/* cardGrid (4.3): hairline grid variant A at the primitive's own default
   --n (320px), so the wrapper needs only .grid .boxed, no override class. */
.card{padding:44px 38px 38px}
.card h3{font:var(--t-h3-card);letter-spacing:var(--ls-h2)}
.card p,.split p{font:var(--t-card-body);color:var(--text-body)}
.card .btn-sm{margin-top:10px;align-self:flex-start}
.list li{border-top:var(--border-soft);padding-top:8px}

/* processCards (4.4): hairline grid variant A, --n:240px. */
.process{--n:240px}
.process-step{--gap:14px;padding:40px 34px}
.process-step h3{font:var(--t-h3-process)}
.ghost{font:var(--t-ghost-num);color:var(--ghost);letter-spacing:var(--ls-ghost)}

/* caseStudyTiles (4.5): 104px lands as margin here, not the usual section
   padding -- own wrapper, not main>section's default. Hairline grid variant
   A, --n:260px, plus the S1 shadow (.raised). Tile h3 matches the base h3
   default (--t-h3-row), so it costs nothing extra. */
.case-study{margin:var(--sec-gap) auto 0;padding:0 32px}
.case-grid{--n:260px}
.case-tile{--gap:14px;padding:38px 32px}

/* twoUpSplit (4.8): hairline grid variant A at the primitive's default --n
   (320px) -- same wrapper cardGrid uses. */
.split{--gap:16px;padding:44px 38px}
.split h3{font:var(--t-h3-split);letter-spacing:var(--ls-h2)}

/* numberedList (4.9): F1 fix (3.3, R3) -- the number moves inline with the
   title (.row-head) instead of a fixed 40px track, leaving two
   auto-fit(200px) tracks that collapse to one column at 320px like every
   other grid, no media query. row-head needs min-width:0 to actually
   shrink; row-body's font matches body's default, only colour differs. */
.rows{border-top:var(--border-ink)}
.list-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:28px;align-items:start;padding:26px 0;border-bottom:var(--border-ink)}
.row-head{display:flex;align-items:baseline;gap:14px;min-width:0}
.rownum{font:500 12px/1.6 var(--font-mono);letter-spacing:var(--ls-mono-wide);color:var(--accent)}
.row-body{color:var(--text-body)}

/* darkSplit (4.10): hairline grid variant A, default --n (320px). Owns its
   own h2 inside the ink panel -- no separate .section-head. */
.split-dark{padding:48px 40px}
.split-dark .label{color:var(--on-ink-65)}
.split-dark p{font:var(--t-panel-body);color:var(--on-ink-72)}
.split-light{--gap:22px;padding:48px 40px}
.split-item{padding-top:18px}
.split-item h3{font:var(--t-h3-item);margin:0 0 8px}

/* reviewWall (4.7): hairline grid variant A, --n:240px. Ruling B: no
   Review/AggregateRating JSON-LD (see sections.js) -- the accent tile is a
   real link to the Google profile instead. --on-ink-72 stands in for the
   prototype's --on-ink-78 on the caption too (both pass AA on accent). */
.review-grid{--n:240px}
.quote-tile{--gap:20px;padding:36px 32px}
.quote-tile blockquote{margin:0;font:var(--t-quote);text-wrap:pretty}
.quote-tile figcaption{margin-top:auto;font:var(--t-figcaption);color:var(--text-muted);border-top:var(--border-soft);padding-top:14px}
.rating-tile{display:flex;flex-direction:column;justify-content:space-between;gap:24px;padding:36px 32px;background:var(--accent);color:var(--paper)}
.rating-tile:hover{color:var(--paper)}
.rating-tile .label{color:var(--on-ink-72)}
.rating-value{font:var(--t-rating)}
.rating-caption{margin-top:10px;font:var(--t-figcaption);letter-spacing:var(--ls-rating);color:var(--on-ink-72)}

/* techMarquee (4.6): enabled:false by default (Ruling A/R4) -- when a
   document opts in, sections.js also emits the pause <button> (2.2.2). The
   blanket reduced-motion rule already force-disables the animation below
   (it carries no !important of its own, so that rule always wins). */
.marquee{display:flex;align-items:center;gap:24px;margin-top:var(--sec-gap);padding:18px var(--gutter);border-top:var(--border-ink);border-bottom:var(--border-ink);background:var(--ink)}
.marquee-track{flex:1;min-width:0;overflow:hidden}
.marquee-track>div{display:flex;width:max-content;animation:bgmarq 42s linear infinite}
.marquee-track span{padding-right:26px;white-space:nowrap;color:var(--paper);font:500 13px/1 var(--font-mono)}
.marquee-toggle{flex:none;width:32px;height:32px;line-height:32px;text-align:center;border:var(--border-on-ink);color:var(--paper)}
.marquee-toggle[aria-pressed=true]{background:var(--paper);color:var(--ink)}
.marquee-track.paused>div{animation-play-state:paused}
@keyframes bgmarq{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* faq (4.11): <details>/<summary> = free keyboard, focus, open/close; ui.js
   only closes siblings on open ([data-faq]). Wrapper reuses .rows' border-top. */
.faq-item{border-bottom:var(--border-ink)}
.faq-q{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:26px 0;list-style:none;cursor:pointer}
.faq-q::-webkit-details-marker{display:none}
.faq-q::after{content:'+';flex:none;font:500 20px/1 var(--font-mono);color:var(--accent)}
details[open]>.faq-q::after{content:'−'}
.faq-q h3{margin:0;font:var(--t-faq-q);letter-spacing:var(--ls-h3)}
.faq-a{max-width:74ch;padding:0 0 28px;font:var(--t-faq-a);color:var(--text-body)}

/* briefDrafter (4.13): hairline grid, --n:360px (form | output). The two
   field-pair rows are the primitives' other non-hairline exception --
   .cols, --n:180px, never .grid. .field/.chip/input/select/textarea focus
   already exist (5. PRIMITIVES), built for this section and unused until
   now -- as are the --panel-dot/--ls-panel tokens. */
.brief-grid{--n:360px}
.brief-pair{--n:180px;--cols-gap:18px}
.brief-form{padding:40px 36px;--gap:22px}
.brief-form fieldset{display:flex;flex-direction:column;gap:10px}
.chip-row{display:flex;flex-wrap:wrap;gap:8px}
.brief-output{padding:40px 36px;--gap:20px}
.panel-dot{width:var(--panel-dot);height:var(--panel-dot)}
.panel-eyebrow{font:500 11px/1 var(--font-mono);letter-spacing:var(--ls-panel);color:var(--on-ink-60)}
.brief-pre{flex:1;margin:0;font:400 13px/1.6 var(--font-mono);color:var(--pre-text);white-space:pre-wrap;word-break:break-word}
.panel-note{font:500 11px/1.5 var(--font-mono);color:var(--on-ink-55);border-top:var(--border-on-ink);padding-top:14px}

/* Chat launcher + dialog (4.0/7.5, R3/R10), page.js shell. Launcher reuses
   .btn.btn-chrome (chrome flips colour, never presses -- S3/S7, OQ4);
   dialog reuses --shadow-dialog/--chat-width/--chat-max-h/--t-chat-msg. */
.chat-wrap{position:fixed;right:var(--chat-offset);bottom:var(--chat-offset);z-index:var(--z-chat)}
.chat-dialog{position:absolute;right:0;bottom:calc(100% + 12px);width:var(--chat-width);max-height:var(--chat-max-h);display:flex;flex-direction:column;background:var(--paper);box-shadow:var(--shadow-dialog)}
.chat-dialog[hidden]{display:none}
.chat-head{display:flex;align-items:center;gap:10px;padding:16px 18px}
.chat-title{font:600 13px/1 var(--font-sans)}
.chat-close{width:28px;height:28px;line-height:28px;text-align:center;font-size:18px}
.chat-log{flex:1;overflow-y:auto;padding:18px;display:flex;flex-direction:column;gap:12px}
.chat-msg{max-width:88%;padding:12px 14px;font:var(--t-chat-msg)}
.chat-msg-bot{align-self:flex-start;background:var(--field-bg);border:var(--border-ink)}
.chat-msg-user{align-self:flex-end;background:var(--accent);color:var(--paper)}
.chat-notice,.chat-chips,.chat-form{padding:14px 18px;border-top:var(--border-ink)}
.chat-notice{display:flex;flex-direction:column;gap:10px;font:400 13px/1.5 var(--font-sans);color:var(--text-body)}
.chat-chips{display:flex;flex-wrap:wrap;gap:8px}
.chat-form{display:flex;gap:8px}
.chat-form input{flex:1;width:auto;min-width:0}
/* Copy-address confirmation. Fixed to the viewport bottom so it never moves
   the page under the pointer, and dismissible by button or Escape rather
   than only by timeout -- a message that vanishes on its own schedule is
   unusable to anyone reading slowly. */
.copy-toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:40;max-width:min(92vw,420px);padding:16px 20px;background:var(--ink);color:var(--paper);box-shadow:var(--shadow-dialog);display:flex;flex-direction:column;gap:8px}
.copy-toast[hidden]{display:none}
.copy-toast-msg{margin:0;font:inherit;line-height:1.45}
.copy-toast-actions{margin:0;display:flex;gap:16px;align-items:baseline;font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-mono)}
.copy-toast-actions a,.copy-toast-close{color:var(--paper);text-decoration:underline;background:none;border:0;padding:0;font:inherit;cursor:pointer}
.copy-toast-close{margin-left:auto;text-underline-offset:3px}
@media (prefers-reduced-motion:no-preference){.copy-toast{animation:toast-in .28s cubic-bezier(.2,.7,.3,1) both}@keyframes toast-in{from{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%,0)}}}
/* Hero build figure -- three recognisable pages drawing themselves in turn:
   a landing page, a shop, a data explorer. 21s loop, 7s each. Within a scene
   every element arrives as a 1px OUTLINE and only then fills with ink, which
   is what reads as "being built" rather than as a loading skeleton.
   Pure CSS, aria-hidden, and prefers-reduced-motion shows the finished shop
   frame instead of a frozen first frame. */
.hero-head{display:grid;grid-template-columns:1fr;gap:32px;align-items:center}
@media (min-width:960px){.hero-head{grid-template-columns:minmax(0,1fr) 300px;gap:64px}}
@media (max-width:959px){.build{display:none}}
.build{position:relative;aspect-ratio:1/1;border:var(--border-ink);background:var(--paper);overflow:hidden}
.build-chrome{position:absolute;left:0;right:0;top:0;height:7%;border-bottom:var(--border-soft)}
.sc{position:absolute;left:0;right:0;top:7%;bottom:0}
.sc i{position:absolute;display:block;box-sizing:border-box;box-shadow:inset 0 0 0 1px var(--ink)}
.sc .ac{box-shadow:inset 0 0 0 1px var(--accent)}
/* scene 1 -- landing */
.sc1 .hd{left:8%;right:8%;top:6%;height:7%}
.sc1 .nv{right:8%;top:7.5%;width:26%;height:4%}
.sc1 .h1{left:8%;right:30%;top:21%;height:14%}
.sc1 .ld{left:8%;right:44%;top:40%;height:5%}
.sc1 .ac{left:8%;top:51%;width:24%;height:7%}
.sc1 .cd{top:68%;height:22%;width:38%}
.sc1 .cd:nth-of-type(6){left:8%}
.sc1 .cd:nth-of-type(7){right:8%}
/* scene 2 -- shop */
.sc2 .hd{left:8%;right:8%;top:6%;height:7%}
.sc2 .fl{left:8%;top:18%;width:44%;height:4%}
.sc2 .tl{width:26%;height:26%}
.sc2 .tl:nth-of-type(3),.sc2 .tl:nth-of-type(6){left:8%}
.sc2 .tl:nth-of-type(4),.sc2 .tl:nth-of-type(7){left:37%}
.sc2 .tl:nth-of-type(5),.sc2 .tl:nth-of-type(8){left:66%}
.sc2 .tl:nth-of-type(3),.sc2 .tl:nth-of-type(4),.sc2 .tl:nth-of-type(5){top:28%}
.sc2 .tl:nth-of-type(6),.sc2 .tl:nth-of-type(7),.sc2 .tl:nth-of-type(8){top:60%}
/* scene 3 -- explorer */
.sc3 .sb{left:8%;top:6%;width:18%;bottom:8%}
.sc3 .st{left:30%;right:8%;top:6%;height:9%}
.sc3 .br{width:11%;bottom:8%}
.sc3 .br:nth-of-type(3){left:30%;height:26%}
.sc3 .br:nth-of-type(4){left:44%;height:44%}
.sc3 .br:nth-of-type(5){left:58%;height:33%}
.sc3 .br:nth-of-type(6){left:72%;height:56%}
.sc3 .br:nth-of-type(7){left:86%;height:40%}
@media (prefers-reduced-motion:reduce){
.sc1,.sc3{display:none}
.sc2 i{background:var(--ink)}
.sc2 .ac{background:var(--accent)}
}
@media (prefers-reduced-motion:no-preference){
.sc i{opacity:0;animation:21s linear infinite both}
.sc1 i{animation-name:sc1}
.sc2 i{animation-name:sc2}
.sc3 i{animation-name:sc3}
.sc i:nth-of-type(2){animation-delay:.35s}
.sc i:nth-of-type(3){animation-delay:.7s}
.sc i:nth-of-type(4){animation-delay:1.05s}
.sc i:nth-of-type(5){animation-delay:1.4s}
.sc i:nth-of-type(6){animation-delay:1.75s}
.sc i:nth-of-type(7){animation-delay:2.1s}
.sc i:nth-of-type(8){animation-delay:2.45s}
@keyframes sc1{0%{opacity:0;background:transparent}2%{opacity:1;background:transparent}
6%{opacity:1;background:var(--ink)}28%{opacity:1;background:var(--ink)}32%{opacity:0;background:var(--ink)}100%{opacity:0;background:var(--ink)}}
@keyframes sc2{0%{opacity:0}33%{opacity:0;background:transparent}35%{opacity:1;background:transparent}
39%{opacity:1;background:var(--ink)}61%{opacity:1;background:var(--ink)}65%{opacity:0;background:var(--ink)}100%{opacity:0}}
@keyframes sc3{0%{opacity:0}66%{opacity:0;background:transparent}68%{opacity:1;background:transparent}
72%{opacity:1;background:var(--ink)}94%{opacity:1;background:var(--ink)}98%{opacity:0;background:var(--ink)}100%{opacity:0}}
.sc1 .ac{animation-name:sc1a}
.sc2 .ac{animation-name:sc2a}
.sc3 .ac{animation-name:sc3a}
@keyframes sc1a{0%{opacity:0;background:transparent}2%{opacity:1;background:transparent}
6%{opacity:1;background:var(--accent)}28%{opacity:1;background:var(--accent)}32%{opacity:0}100%{opacity:0}}
@keyframes sc2a{0%{opacity:0}33%{opacity:0;background:transparent}35%{opacity:1;background:transparent}
39%{opacity:1;background:var(--accent)}61%{opacity:1;background:var(--accent)}65%{opacity:0}100%{opacity:0}}
@keyframes sc3a{0%{opacity:0}66%{opacity:0;background:transparent}68%{opacity:1;background:transparent}
72%{opacity:1;background:var(--accent)}94%{opacity:1;background:var(--accent)}98%{opacity:0}100%{opacity:0}}
}
/* 8. BLOG. The index is a grid of cards; a post is a reading column with a
   rail beside it. Measure stays capped so long-form text keeps a comfortable
   line length whatever the viewport.

   Note on tokens: --border-ink and --border-soft are whole shorthands
   (1px solid <colour>), so they are used bare. Writing
   `border:var(--border-ink)` expands to `1px solid 1px solid #0d0d0d`,
   which is invalid and silently dropped - which is what had happened to every
   bordered rule in this section.

   The rail's motion is scroll-driven CSS and nothing else; no JavaScript is
   involved at any point. Every animated property is authored so its
   *unanimated* value is the finished one, so a browser without
   animation-timeline gets a complete static rail rather than a half-drawn
   one. Section titles never fade: dimming text below 4.5:1 to signal "not
   here yet" would trade a contrast failure for a visual effect. */
.blog-index,.post-layout,main>.breadcrumb{max-width:var(--container-max);margin-inline:auto;padding-inline:var(--gutter)}
main>.breadcrumb{padding-bottom:0}
.blog-index .breadcrumb{max-width:none;padding-inline:0}
.blog-index>h1,.post-head h1{margin:0 0 12px}
.blog-index>.lede{max-width:60ch;margin:0 0 32px;color:var(--text-lede)}
.empty{margin:32px 0;color:var(--text-muted)}
.breadcrumb{margin:0 0 24px}
.breadcrumb ol{display:flex;flex-wrap:wrap;gap:8px;font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-mono);color:var(--text-muted)}
.breadcrumb li+li::before{content:"/";margin-right:8px;color:var(--hairline-soft)}
.breadcrumb a{color:inherit}

/* Topic chips */
.topics{margin:0 0 28px}
.topics ul{display:flex;flex-wrap:wrap;gap:8px}
.topics a,.topics [aria-current]{display:block;padding:5px 12px;border:var(--border-ink);font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-chip);color:inherit}
.topics a:hover,.topics a:focus-visible{background:var(--ink);color:var(--paper)}
.topics [aria-current]{background:var(--ink);color:var(--paper)}

/* Index cards. One hairline grid: the 1px gap is the rule between cards. */
.post-cards{display:grid;gap:1px;background:var(--hairline-soft);border:var(--border-soft)}
@media (min-width:820px){.post-cards{grid-template-columns:1fr 1fr}.post-card.is-lead{grid-column:1/-1}}
.post-card{position:relative;display:flex;flex-direction:column;gap:10px;padding:30px 26px 26px;background:var(--paper);transition:background .12s}
.post-card:hover,.post-card:focus-within{background:var(--field-bg)}
.card-image{width:100%;aspect-ratio:16/7;object-fit:cover;margin-bottom:4px}
.card-meta{display:flex;flex-wrap:wrap;font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-mono);color:var(--text-muted)}
.card-read::before,.read-time::before{content:"\00b7";margin:0 10px}
.card-title{font:800 clamp(21px,2.2vw,29px)/1.12 var(--font-sans);letter-spacing:var(--ls-h3)}
.is-lead .card-title{font-size:clamp(28px,3.6vw,46px);line-height:1.04;letter-spacing:var(--ls-h2)}
.card-title a{color:inherit}
/* The whole card is clickable, but it contains exactly one link. The overlay
   is drawn from the title link, so that link's accessible name is the
   headline and a screen reader is told about one destination, not three. */
.card-title a::after{content:"";position:absolute;inset:0}
.card-title a:hover,.card-title a:focus-visible{text-decoration:underline;text-underline-offset:4px}
.card-excerpt{max-width:62ch;color:var(--text-body)}
.is-lead .card-excerpt{font:var(--t-card-body);max-width:72ch}
/* Above the overlay, so a tag stays a link of its own. */
.card-tags{position:relative;z-index:1;display:flex;flex-wrap:wrap;gap:8px;margin-top:auto;padding-top:8px}
.card-tags li{font-family:var(--font-mono);font-size:11.5px;letter-spacing:var(--ls-chip);color:var(--text-muted)}
.card-tags a{color:inherit;text-underline-offset:3px}

/* Post */
.post-layout{display:grid;gap:44px;align-items:start}
@media (min-width:1080px){.post-layout{grid-template-columns:minmax(0,1fr) 264px}}
.post-head{margin:0 0 36px;padding-bottom:24px;border-bottom:var(--border-ink)}
.post-head h1{font-size:clamp(30px,4.6vw,52px);line-height:1.05;letter-spacing:-.01em}
.post-head .post-meta{margin-top:14px}
.post-meta{display:flex;flex-wrap:wrap;font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-mono);color:var(--text-muted)}
.post-tags{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 0}
.post-tags a,.post-tags li{font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-chip);color:var(--text-muted)}
.post-tags a{color:inherit;text-underline-offset:3px}
.post-cover{margin:0 0 32px}
.post-cover figcaption{margin-top:10px;font:var(--t-figcaption);color:var(--text-muted)}
.post-body{max-width:68ch}
.post-body>*+*{margin-top:1.1em}
/* A section spaces itself through its own heading, so it carries no margin of
   its own - otherwise the two stack. */
.ps{margin-top:0}
.ps>*+*{margin-top:1.1em}
.post-body h2{margin-top:2em;font-size:clamp(21px,2.4vw,28px);line-height:1.2}
.post-body>.ps:first-child h2{margin-top:0}
.post-body h3{margin-top:1.6em;font-size:18px}
.post-body p{line-height:1.65}
/* The reset strips markers everywhere; prose is the one place they carry
   meaning, so they come back here. */
.post-body ul{padding-left:1.2em;list-style:disc}
.post-body ol{padding-left:1.2em;list-style:decimal}
.post-body li+li{margin-top:.4em}
.post-body table{width:100%;border-collapse:collapse;font-size:15px}
.post-body th,.post-body td{text-align:left;padding:10px 12px;border-bottom:var(--border-soft);vertical-align:top}
.post-body th{font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-mono);font-weight:400;color:var(--text-muted)}
.post-body blockquote{margin-inline:0;padding-left:20px;border-left:2px solid var(--accent);color:var(--text-body)}
.post-body code{font-family:var(--font-mono);font-size:.9em}
.post-body a{text-underline-offset:3px}

/* The reading rail: the article's own sections, with the key point for
   whichever one is on screen fading in beside it. Below 1080px there is no
   margin to put it in, and a table of contents stacked above an article on a
   phone is a wall to scroll past rather than a help - so it is not rendered at
   all.

   There is no progress bar. A scroll indicator measures the scrollbar, which
   the scrollbar already does. */
.rail{display:none}
@media (min-width:1080px){.rail{display:block;position:sticky;top:96px}}
.rail-title{margin-bottom:14px;font-family:var(--font-mono);font-size:11px;letter-spacing:var(--ls-panel);text-transform:uppercase;color:var(--text-muted)}
.rail-list{padding-left:18px;display:grid;gap:16px}
.rail-list li{position:relative}
.rail-list a{display:block;font:500 13.5px/1.35 var(--font-sans);color:var(--text-body)}
.rail-list a:hover,.rail-list a:focus-visible{color:var(--ink)}
/* The marker is what says "you are here" - never the text. */
.rail-list li::before{content:"";position:absolute;left:-18px;top:4px;width:2px;height:calc(100% - 4px);background:var(--ink);opacity:0}
.rail-note{margin-top:6px;padding-left:10px;border-left:var(--border-soft);font:400 12.5px/1.45 var(--font-sans);color:var(--text-muted)}
@keyframes rail-mark{0%{opacity:0}12%,88%{opacity:1}100%{opacity:0}}
@keyframes rail-note{0%{opacity:0;transform:translateY(5px)}14%,86%{opacity:1;transform:none}100%{opacity:0;transform:translateY(-5px)}}
@supports (animation-timeline:view()){
.rail-list li::before{animation:rail-mark linear both}
.rail-note{animation:rail-note linear both}
/* A timeline name cannot be computed, so the pairing is written out. Ten is
   the cap; RAIL_ANIMATED_MAX in lib/post.js states the same number and a test
   holds the two together. An eleventh section still lists and still links -
   it simply never animates, which is what every entry does in a browser that
   never entered this block. */
.post-layout{timeline-scope:--s1,--s2,--s3,--s4,--s5,--s6,--s7,--s8,--s9,--s10}
.post-body>section:nth-of-type(1){view-timeline-name:--s1}
.post-body>section:nth-of-type(2){view-timeline-name:--s2}
.post-body>section:nth-of-type(3){view-timeline-name:--s3}
.post-body>section:nth-of-type(4){view-timeline-name:--s4}
.post-body>section:nth-of-type(5){view-timeline-name:--s5}
.post-body>section:nth-of-type(6){view-timeline-name:--s6}
.post-body>section:nth-of-type(7){view-timeline-name:--s7}
.post-body>section:nth-of-type(8){view-timeline-name:--s8}
.post-body>section:nth-of-type(9){view-timeline-name:--s9}
.post-body>section:nth-of-type(10){view-timeline-name:--s10}
.rail-list li:nth-child(1)::before,.rail-list li:nth-child(1) .rail-note{animation-timeline:--s1}
.rail-list li:nth-child(2)::before,.rail-list li:nth-child(2) .rail-note{animation-timeline:--s2}
.rail-list li:nth-child(3)::before,.rail-list li:nth-child(3) .rail-note{animation-timeline:--s3}
.rail-list li:nth-child(4)::before,.rail-list li:nth-child(4) .rail-note{animation-timeline:--s4}
.rail-list li:nth-child(5)::before,.rail-list li:nth-child(5) .rail-note{animation-timeline:--s5}
.rail-list li:nth-child(6)::before,.rail-list li:nth-child(6) .rail-note{animation-timeline:--s6}
.rail-list li:nth-child(7)::before,.rail-list li:nth-child(7) .rail-note{animation-timeline:--s7}
.rail-list li:nth-child(8)::before,.rail-list li:nth-child(8) .rail-note{animation-timeline:--s8}
.rail-list li:nth-child(9)::before,.rail-list li:nth-child(9) .rail-note{animation-timeline:--s9}
.rail-list li:nth-child(10)::before,.rail-list li:nth-child(10) .rail-note{animation-timeline:--s10}
}

/* 9. CASE STUDIES. Eleven projects that were one prose blob separated by
   headings, which made every case look exactly like every other and made a
   finished build indistinguishable from one still in progress. What a reader
   scans for - state, kind, year, stack, where it lives - is structured now, so
   it lands in the same place every time and the differences are the only thing
   that moves.

   The index numeral is a CSS counter rather than markup: document order
   already carries the sequence, so the number is decoration and belongs in the
   stylesheet. It is set in the mono meta row at --text-muted (6.64:1) instead
   of being blown up as a pale ghost numeral, because a number large enough to
   read is a number that has to meet contrast. */
main{counter-reset:case}
main>section.case{margin-top:64px;padding:0 var(--gutter);counter-increment:case}
.case-head{border-top:var(--border-ink);padding-top:22px}
.case-meta{display:flex;flex-wrap:wrap;align-items:center;gap:12px;font-family:var(--font-mono);font-size:12px;letter-spacing:var(--ls-mono);color:var(--text-muted)}
.case-meta::before{content:counter(case,decimal-leading-zero)}
.case-state{padding:3px 9px;border:var(--border-ink);letter-spacing:var(--ls-chip);text-transform:uppercase;color:var(--ink)}
/* State is the strongest signal on the page, so it is the one thing that
   changes colour. Live is filled; anything still moving is outlined. */
[data-state=live] .case-state{background:var(--accent);border-color:var(--accent);color:var(--paper)}
[data-state=building] .case-state{border-style:dashed}
.case-note{font-style:italic}
.case-head h2{margin-top:14px;font:800 clamp(30px,4.2vw,54px)/1 var(--font-sans);letter-spacing:var(--ls-h2)}
.case-tagline{margin-top:8px;max-width:34ch;font:500 clamp(17px,1.9vw,23px)/1.25 var(--font-sans);color:var(--text-lede)}
.case-url{display:inline-flex;align-items:center;gap:7px;margin-top:14px;font-family:var(--font-mono);font-size:12.5px;letter-spacing:var(--ls-mono)}
/* Drawn, not typed: an arrow glyph in generated content is announced as
   "north east arrow" by some screen readers, and this one is decoration. */
.case-url::after{content:"";width:.42em;height:.42em;border-top:2px solid;border-right:2px solid;transform:rotate(45deg)}
.case-lede{margin-top:24px;max-width:64ch;font:var(--t-card-body);color:var(--text-list)}
/* The stack as a hairline spec grid - the same boxed-grid language the rest of
   the site uses, and unmistakably not prose. */
.case-stack{margin-top:26px;display:grid;gap:1px;background:var(--hairline-soft);border:var(--border-soft)}
@media (min-width:700px){.case-stack{grid-template-columns:repeat(2,1fr)}}
.case-stack>div{padding:13px 16px;background:var(--paper)}
.case-stack dt{font-family:var(--font-mono);font-size:11px;letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--text-muted)}
.case-stack dd{margin:5px 0 0;font-size:15px;line-height:1.45;color:var(--text-list)}
.case-metrics{display:flex;flex-wrap:wrap;gap:36px;margin-top:26px}
.metric-value{display:block;font:800 clamp(26px,3vw,38px)/1 var(--font-sans);letter-spacing:var(--ls-stat)}
.metric-label{display:block;margin-top:5px;font-family:var(--font-mono);font-size:11.5px;letter-spacing:var(--ls-mono);color:var(--text-muted)}
.case-body{margin-top:26px;max-width:68ch}
.case-body>*+*{margin-top:1.05em}
.case-body p{line-height:1.65;color:var(--text-body)}
.case-body h3{margin-top:1.6em;font-size:18px;color:var(--ink)}
.case-body ul{padding-left:1.2em;list-style:disc}
.case-body li+li{margin-top:.4em}
.case-body a{text-underline-offset:3px}
