
/* The colours are not written here. They live in `janus/skins.py`, and the
   same `ONYX` string is printed into this sheet and into the skin sheet linked
   after it — one palette, two stylesheets, nothing that can drift apart. That
   matters because this sheet is the fallback: a page that renders without the
   skin sheet must still be the product, not a different-looking one.

   Every ink tone in that string clears 4.5:1 on every surface it is allowed to
   sit on, and every interactive fill clears 3:1 — measured on black, not
   guessed and not inverted from a paper palette.

   There is one face. The app used to carry a second, and a media query and an
   attribute rule to decide between them; both are gone, along with the button
   in the header, because Onyx is black at every hour and a toggle with one
   destination is a broken control. */
:root{

/* ONYX. Pure black, the way Apple's own dark screens are: the page is #000,
   the panels are the two greys immediately above it, and the only saturated
   thing on screen is the blue you are meant to press.

   There is one face, not two. Chalk had a day face and a night face; Onyx is
   black at every hour, and `_sheet()` below prints it under every one of the
   selectors those two faces used to divide between.

   Contrast was measured on #000, not inherited: every ink below clears 4.5:1
   on the black and on all three panels, and `--accent` is a darker blue than
   `--accent-ink` precisely so that white lettering on the filled button clears
   it too — the brighter blue is for words on the black, where it has the
   contrast to spare. */
--bg:#000000;--dot:#101014;
--panel:#0E0E11;--panel2:#151519;--panel3:#1D1D22;
--line:#212127;--line2:#2E2E36;--line3:#48484F;
--ink:#F5F5F7;--ink2:#A1A1A6;--ink3:#86868B;
/* `--accent` is a fill and `--accent-ink` is a letterform. White on the fill
   clears 4.9:1; the letterform on black clears 8:1. */
--accent:#0A6FE0;--accent-ink:#4DA3FF;--on-accent:#FFFFFF;
--good:#30D158;--warn:#FF9F0A;--bad:#FF453A;
/* `--c1`..`--c4` are wayfinding for the four phases of the run, never data.
   Every one of them appears beside a phase name in words and a step number, so
   nothing in the product is encoded by colour alone. */
--c1:#4DA3FF;--c2:#BF5AF2;--c3:#64D2FF;--c4:#FF9F0A;
/* One face, the system face, at every size. The serif token still exists
   because every rule in the app names it; here it resolves to the grotesque,
   which is what makes the product read as software rather than as print. */
--serif:-apple-system,BlinkMacSystemFont,"SF Pro Display","SF Pro Text","Helvetica Neue","Segoe UI",Roboto,Helvetica,Arial,sans-serif;
--sans:-apple-system,BlinkMacSystemFont,"SF Pro Display","SF Pro Text","Helvetica Neue","Segoe UI",Roboto,Helvetica,Arial,sans-serif;
--mono:-apple-system,BlinkMacSystemFont,"SF Pro Display","SF Pro Text","Helvetica Neue","Segoe UI",Roboto,Helvetica,Arial,sans-serif;
/* The light falling across the top of the canvas. On black it is a glow rather
   than a wash — strong enough to give the canvas a top, faint enough that no
   figure sitting in it loses contrast. */
--wash:radial-gradient(58% 52% at 72% 26%,rgba(10,132,255,.13),
rgba(10,132,255,.045) 45%,transparent 74%);
--beam-a:rgba(77,163,255,.22);--beam-b:rgba(50,120,210,.09);
--beam-core:rgba(230,242,255,.34);--sheen:rgba(255,255,255,.42);
color-scheme:dark
;
--rail:236px;--r:14px}

*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;background:var(--bg);color:var(--ink);
font:13.5px/1.55 var(--sans);letter-spacing:-.006em;
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
/* `… review --out` writes the same body into a plain page with no app frame.
   The scroll lock therefore belongs to the frame, not to the document: put it
   on body and every standalone file loses its scrollbar. */
.wrap{max-width:1120px;margin:0 auto;padding:30px 24px 90px}
/* `--accent-ink` rather than `--accent` for text links: the same blue, several
   steps lighter, which is what carries a 14px line on black (8:1 against 5:1).
   The darker one stays for fills, where white lettering has to clear it. */
a{color:var(--accent-ink);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:2px}
:focus-visible{outline:2px solid var(--accent-ink);outline-offset:2px;
border-radius:4px}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:var(--line2);border-radius:9px;
border:3px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:var(--line3)}

/* ---------- frame ---------- */
/* One column, and it is the width of the window — never wider.
   With no column named, the implicit track was sized `auto`, which means "as
   wide as the widest thing in it has to be". The widest thing is the topbar,
   whose product name is a long line of text, so a narrow window — or an
   ordinary one at 125% zoom — laid the whole app out at around 1275px and then
   clipped it back to the glass, with `overflow:hidden` and no scrollbar. Every
   page lost its right-hand third and there was no way to scroll to it: the
   sign-off column, the account menu and half of several tables were simply
   gone. Naming the track `minmax(0,1fr)` makes the frame follow the window,
   and the things inside it shrink or scroll on their own terms. */
.app{display:grid;grid-template-columns:minmax(0,1fr);
grid-template-rows:auto minmax(0,1fr);height:100vh;overflow:hidden}
/* Two columns, and only two. The third — a scrolling pane pinned to the right
   of every page — held the one figure a subscriber opens this app for, and
   held it in the place the eye reaches last, in a gutter too narrow to lay a
   six-assistant grid out in. Those readings are in the pages now, across the
   width, where they are read rather than scrolled to. */
.cols{display:grid;grid-template-columns:var(--rail) minmax(0,1fr);
overflow:hidden}
/* A pane that isn't rendered must not leave its column behind: with no rail,
   the canvas would land in the rail's own slot and the rest of the window would be
   empty black. The track list is built from the panes that actually exist. */
.cols.solo{grid-template-columns:minmax(0,1fr)}
.col{overflow-y:auto;overflow-x:hidden;min-width:0}
.rail{border-right:1px solid var(--line);background:var(--panel);
padding:14px 10px 40px}
/* The light falls across the top of the working surface as a background
   layer rather than an element, so it never scrolls with the content, never
   sits above anything, and cannot be clicked. It drifts, slowly, and it is the
   only thing on a rendered page that keeps moving after the page has settled:
   a screen somebody reads all day should be alive when they arrive at it and
   still while they are working in it. */
.canvas{background:var(--bg);background-image:var(--wash),radial-gradient(circle,
var(--dot) 1px,transparent 1px);background-size:100% 620px,24px 24px;
background-repeat:no-repeat,repeat;background-position:14% 0,0 0;
padding:26px 26px 140px;animation:drift 24s ease-in-out infinite alternate}
@keyframes drift{from{background-position:10% 0,0 0}
to{background-position:22% 0,0 0}}
/* the home page is one thing to do, so it sits in the middle of the viewport */
.canvas.hero{display:grid;align-content:center;padding:26px}

/* ---------- topbar ---------- */
.top{display:flex;align-items:center;gap:14px;padding:0 16px;
background:var(--panel);border-bottom:1px solid var(--line);min-height:58px}
/* Which reading is on screen. Pushed to the right of the identity and left of
   the URL box, so it reads as a property of the page rather than as a control:
   nothing in here is clickable, and nothing in here changes what is shown. */
.stamps{display:flex;gap:7px;margin-left:auto;flex:none}
/* The URL box takes the free space when it is alone in the bar. With stamps in
   front of it they have already taken it, and a second `margin-left:auto` would
   split the gap between them and float the stamps into the middle. */
.top .stamps+form{margin-left:0}
.stamp{font-size:10.5px;padding:4px 10px;border-radius:999px;
background:var(--panel3);border:1px solid var(--line2);color:var(--ink2);
white-space:nowrap}
@media(max-width:900px){.stamps{display:none}}
/* The way back to the start page: the one screen that is a URL box and nothing
   else. It is set in the serif so it reads as a mark rather than a control,
   and it is ruled off from the rest of the bar because everything to its right
   is about one product and it is not. */
.brand{flex:none;font-family:var(--serif);font-weight:600;font-size:16.5px;line-height:1;
color:var(--ink);letter-spacing:-.02em;padding-right:14px;
border-right:1px solid var(--line2)}
.brand:hover{color:var(--accent-ink);text-decoration:none}
/* The way back to the product's overview. Named rather than drawn as a
   chevron: a labelled control is the difference between "there is a way back"
   and "there might be one". It sits beside the mark on every page of a
   product. */
.home{display:inline-flex;align-items:center;gap:7px;flex:none;padding:7px 13px;
border-radius:999px;font-size:12.5px;font-weight:600;color:var(--ink2);
background:var(--panel2);border:1px solid var(--line2);letter-spacing:-.01em}
.home:hover{color:var(--on-accent);background:var(--accent);
border-color:var(--accent);text-decoration:none}
.home i{font-style:normal;font-size:13px;line-height:1}
/* On the overview itself the same chip stays put and says so, so the control
   never moves or disappears between pages. */
.home.here{color:var(--accent-ink);
background:color-mix(in srgb,var(--accent) 12%,transparent);
border-color:color-mix(in srgb,var(--accent) 38%,transparent);cursor:default}
.home.here:hover{color:var(--accent-ink);
background:color-mix(in srgb,var(--accent) 12%,transparent);
border-color:color-mix(in srgb,var(--accent) 38%,transparent)}
/* The product name is itself the way home, the way a masthead is. */
a.top-id{color:inherit}
a.top-id:hover{text-decoration:none}
a.top-id:hover h1{color:var(--accent-ink)}
.top-id{min-width:0}
.top-id h1{margin:0;font-size:15px;font-weight:600;letter-spacing:-.015em;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.top-id p{margin:1px 0 0;font-size:11.5px;color:var(--ink3);white-space:nowrap;
overflow:hidden;text-overflow:ellipsis}
.top form{margin-left:auto;display:flex;gap:8px;align-items:center;
flex:0 1 470px;min-width:200px}
.top input[type=url]{flex:1;min-width:0;padding:9px 13px;font-size:13px;
background:var(--bg);border:1px solid var(--line2);border-radius:10px;
color:var(--ink);font-family:inherit}
.top input[type=url]::placeholder{color:var(--ink3)}
.top input[type=url]:focus{outline:none;border-color:var(--accent);
box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
/* Fully rounded, the way the brand sheet draws a button. */
.btn{padding:9px 17px;font-size:13px;font-weight:600;background:var(--accent);
color:var(--on-accent);border:0;border-radius:999px;cursor:pointer;
white-space:nowrap;font-family:inherit;display:inline-flex;align-items:center;
gap:7px;letter-spacing:-.01em;transition:background .12s,border-color .12s}
.btn:hover{background:var(--accent-ink);text-decoration:none;
color:var(--on-accent)}
.btn:active{background:color-mix(in srgb,var(--accent) 82%,var(--ink))}
.btn.ghost{background:transparent;color:var(--ink2);
border:1px solid var(--line2)}
.btn.ghost:hover{color:var(--ink);border-color:var(--accent);
background:color-mix(in srgb,var(--accent) 10%,transparent)}
/* The one press a page exists for. Bigger, and it says so from across the room
   — the search button used to be the same size as "see every source". */
.btn.big{padding:12px 22px;font-size:14.5px;
box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 16%,transparent)}

/* Live or mock, offered as one control.
   Every press in this app that starts a run spends money at up to three
   different APIs, and until now the only way to find out what a screen produced
   was to buy it. So each of those presses is offered twice: the live one, which
   calls everything, and the mock beside it, which calls nothing.
   The mock is dashed rather than merely quiet. A ghost button reads as "the
   lesser option" and gets clicked by people who wanted the real thing; a dashed
   outline reads as "not the real thing", which is exactly what it is. The line
   underneath is not decoration either — it is where each button says what it
   costs, and a pair of buttons where only one of them spends money must say
   which one that is before it is pressed, not after. */
.press{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.press form{margin:0}
.press .why{flex-basis:100%;margin:2px 0 0;color:var(--ink3);
font-size:11.5px;line-height:1.5}
.btn.mock{background:transparent;color:var(--ink2);
border:1px dashed var(--line2);box-shadow:none}
.btn.mock:hover{color:var(--ink);border-color:var(--accent);
background:color-mix(in srgb,var(--accent) 8%,transparent)}
/* A mock run, while it is running and on everything it produced. Same warning
   colour as every other "do not read this as a measurement" on the site, because
   it is the same claim. */
.mock-flag{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;
border-radius:999px;font-size:11px;font-weight:600;color:var(--warn);
background:color-mix(in srgb,var(--warn) 12%,transparent);
border:1px dashed color-mix(in srgb,var(--warn) 40%,transparent)}

/* ---------- journey rail: six numbered steps, grouped by phase ---------- */
.rail-h{font-size:10px;text-transform:uppercase;letter-spacing:.11em;
color:var(--ink3);font-weight:600;padding:14px 10px 6px;margin:0}
.rail-h:first-child{padding-top:4px}
.r{display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:10px;
color:var(--ink2);font-size:12.5px;border:1px solid transparent}
.r:hover{background:var(--panel2);color:var(--ink);text-decoration:none}
.r.on{background:var(--panel3);color:var(--ink);border-color:var(--line2)}
.r .n{flex:none;width:20px;height:20px;border-radius:7px;display:grid;
place-items:center;font:600 10.5px/1 var(--mono);background:var(--panel3);
border:1px solid var(--line2);color:var(--ink3)}
.r.state-ok .n{background:color-mix(in srgb,var(--good) 15%,transparent);
border-color:color-mix(in srgb,var(--good) 48%,transparent);color:var(--good)}
.r.state-warn .n{background:color-mix(in srgb,var(--warn) 14%,transparent);
border-color:color-mix(in srgb,var(--warn) 45%,transparent);color:var(--warn)}
.r span.t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
white-space:nowrap}
.r .dot{flex:none;width:6px;height:6px;border-radius:50%;background:var(--line3)}
.r.state-ok .dot{background:var(--good);
box-shadow:0 0 0 3px color-mix(in srgb,var(--good) 16%,transparent)}
.r.state-warn .dot{background:var(--warn)}
.r.state-busy .n{background:color-mix(in srgb,var(--accent) 16%,transparent);
border-color:color-mix(in srgb,var(--accent) 50%,transparent);
color:var(--accent-ink)}
.r.state-busy .dot{background:var(--accent);animation:pulse 1.1s ease-in-out infinite}
.r.state-bad .n{background:color-mix(in srgb,var(--bad) 14%,transparent);
border-color:color-mix(in srgb,var(--bad) 45%,transparent);color:var(--bad)}
.r.state-bad .dot{background:var(--bad)}
@keyframes pulse{0%,100%{opacity:1;
box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 45%,transparent)}
50%{opacity:.45;box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 0%,transparent)}}
/* Two rows that belong to the same step read as one thing when a hairline
   joins their numbers, which is what a stepper is for. */
.rail-step{position:relative}
.rail-step:before{content:"";position:absolute;left:19px;top:-9px;height:9px;
width:1px;background:var(--line2)}
.rail-step:first-of-type:before{display:none}
/* With the run pinned on top, the first step is no longer the first `a` in the
   column, so the stub above it has to be switched off by position instead. */
.rail-home~.rail-h+.rail-step:before{display:none}
.r .rc{flex:none;font:600 10px/1 var(--mono);color:var(--ink3);
border:1px solid var(--line2);border-radius:20px;padding:3px 6px}
.r.on .rc{color:var(--ink);border-color:var(--line3)}
/* The stages inside a step, hung off that step's number by a hairline so they
   read as belonging to it. Step five is a section rather than a page — the
   plan, the brief, the method library, the check, the proof — and those five
   places belong on the map the customer already knows, indented under the step
   that contains them. Sending somebody to a second column of their own says
   they have left the run, which they have not: they are one level further in.
   They stand open on every page, like the steps themselves: a column that
   changes shape depending on what you pressed is a column you cannot learn. */
.rail-sub{margin:2px 0 4px 19px;padding:3px 0 4px 12px;
border-left:1px solid var(--line2)}
.rail-sub+.rail-step:before{display:none}
.rs{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:9px;
color:var(--ink3);font-size:12px;border:1px solid transparent}
.rs:hover{background:var(--panel2);color:var(--ink);text-decoration:none}
.rs.on{background:var(--panel3);color:var(--ink);border-color:var(--line2)}
.rs .sn{flex:none;width:14px;font:600 10px/1 var(--mono);color:var(--ink3);
text-align:center}
.rs.on .sn{color:var(--accent-ink)}
/* Three counts, three kinds of thing. The to-do list is the only one that is
   work somebody has to do, so it is the only one drawn as a filled count; the
   ready-to-send count is outlined because it is a subset of that work; the
   library size is set flat with no pill at all, because a stack of guides is
   not a queue of jobs, and a bordered "22" beside a bordered "8" invites a
   comparison that means nothing. */
.rs .rc-work{color:var(--accent-ink);border-color:transparent;
background:color-mix(in srgb,var(--accent) 20%,transparent)}
.rs .rc-ready{color:var(--ink2)}
.rs .rc-reading{border-color:transparent;padding:3px 2px;font-weight:400}
.rs span.t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
white-space:nowrap}
.rs .rc{flex:none;font:600 10px/1 var(--mono);color:var(--ink3);
border:1px solid var(--line2);border-radius:20px;padding:2px 6px}
.rs.on .rc{color:var(--ink);border-color:var(--line3)}
/* The pages that are always there. A step's number is a claim about order;
   these have no order, so their marker is a bullet and it is drawn in the
   recessive ink rather than a phase colour. Quieter than a step, and never
   mistakable for one. */
.rail-aside .n{background:none;border:1px solid var(--line2);color:var(--ink3);
font-size:9px}
.rail-aside.on .n{background:var(--accent);border-color:var(--accent);
color:var(--on-accent)}
/* The worked-out category, shown wherever it was used. Dashed, because a
   dashed border is how the rest of this stylesheet says "provisional", and a
   reading is exactly that until somebody confirms or replaces it. Quiet enough
   not to compete with the headline; never so quiet it can be missed, because a
   customer reading a whole run built on the wrong noun has to be able to see
   which noun it was. */
.rd{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
border:1px dashed var(--line3);border-radius:9px;padding:8px 12px;
margin:0 0 14px;font-size:12.5px;color:var(--ink2);background:var(--panel)}
.rd b{color:var(--ink);font-weight:600}
.rd-k{color:var(--ink3)}
.rd-from{color:var(--ink3);font-size:11.5px}
.rd a{margin-left:auto}
.rail-note{color:var(--ink3);font-size:11.5px;line-height:1.5;padding:12px 10px 0;
margin:16px 0 0;border-top:1px solid var(--line)}
/* The run, pinned above the five steps it contains. Separated by a rule because
   it is not a sixth step — it is the page all five of them live on. */
.rail-home{margin-bottom:10px;padding-bottom:10px;
border-bottom:1px solid var(--line)}
.rail-home .n{background:color-mix(in srgb,var(--accent) 15%,transparent);
border-color:color-mix(in srgb,var(--accent) 42%,transparent);
color:var(--accent-ink);font-size:12px}
.rail-home .t{font-weight:600;color:var(--ink)}
/* The one line in the column allowed to wrap. Everything else in the rail is
   a name that stays on one line and ellipses if it must, but this is a
   sentence — "readable with no product named" — and a clipped sentence tells
   the reader nothing at all. */
.rail-home small{display:block;color:var(--ink3);font-size:11px;font-weight:400;
letter-spacing:0;white-space:normal;line-height:1.35;margin-top:2px}
.rail-home .t{white-space:normal}

/* ---------- the walkthrough banner ---------- */
.guide{background:linear-gradient(180deg,var(--panel2),var(--panel));
border:1px solid var(--line2);border-radius:var(--r);padding:18px 20px 16px;
margin-bottom:16px;position:relative;overflow:hidden}
.guide:before{content:"";position:absolute;inset:0 auto 0 0;width:3px;
background:var(--gc,var(--accent))}
/* The eyebrow takes its colour from the phase, so the chip, the rail dot and
   the node icon for a step are the same colour without being told twice. */
.guide-step{display:inline-flex;align-items:center;gap:7px;font:600 10.5px/1
var(--sans);text-transform:uppercase;letter-spacing:.13em;
color:var(--gc,var(--accent-ink));
background:color-mix(in srgb,var(--gc,var(--accent)) 12%,transparent);
border:1px solid color-mix(in srgb,var(--gc,var(--accent)) 28%,transparent);
padding:5px 11px;border-radius:999px}
.guide h1{margin:12px 0 7px;font-family:var(--serif);font-size:28px;
font-weight:600;line-height:1.15;letter-spacing:-.032em}
.guide p{margin:0;color:var(--ink2);font-size:13.5px;max-width:70ch;
line-height:1.6}
.guide-do{margin-top:13px;padding-top:13px;border-top:1px solid var(--line);
display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.guide-do span{color:var(--ink2);font-size:13px;flex:1;min-width:220px}
.guide-do b{color:var(--ink)}
.guide-do form{margin:0}
#approve-questions,#resolve,#start-work{scroll-margin-top:18px}
#approve-questions button{min-height:44px;padding:9px 13px;margin:6px 4px 6px 0;
font:600 13px var(--sans);color:var(--ink);background:var(--panel2);
border:1px solid var(--line2);border-radius:8px;cursor:pointer}
#approve-questions button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
#approve-questions label{display:block}
#approve-questions input:not([type=hidden]){width:100%;padding:10px;font:inherit;
color:var(--ink);background:var(--panel2);border:1px solid var(--line2);border-radius:8px}
/* the same block, at the foot of a page: one obvious way onwards */
.next{display:flex;align-items:center;gap:13px;flex-wrap:wrap;
background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
padding:15px 18px;margin-top:22px}
.next-k{font:600 10.5px/1 var(--mono);text-transform:uppercase;
letter-spacing:.09em;color:var(--ink3)}
.next b{font-size:14px;display:block;letter-spacing:-.01em}
.next p{margin:2px 0 0;color:var(--ink3);font-size:12.5px}
.next-body{flex:1;min-width:180px}

/* ---------- canvas: the pipeline as connected nodes ---------- */
.flow{max-width:640px;margin:0 auto}
.node{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
display:block;color:inherit;position:relative;
transition:border-color .12s,background .12s}
.node:hover{border-color:var(--line3);background:var(--panel2);
text-decoration:none}
.node.state-ok{border-color:color-mix(in srgb,var(--good) 36%,transparent)}
.node.state-warn{border-color:color-mix(in srgb,var(--warn) 34%,transparent)}
.node.state-idle{border-style:dashed;background:transparent}
.node.state-busy{border-color:color-mix(in srgb,var(--accent) 52%,transparent);
box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 10%,transparent)}
.node.state-bad{border-color:color-mix(in srgb,var(--bad) 46%,transparent)}
.node.on{border-color:var(--accent);
box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 15%,transparent)}
/* the badge sits above the card edge, the way a run status reads in a builder:
   attached to the node, but never mistaken for its content */
.node .pill.float{position:absolute;top:-11px;right:14px;
background:var(--panel);border:1px solid var(--line2)}
/* The float badge sits over the card edge, so it needs an opaque ground of its
   own — a tint of the status colour mixed into the panel, not a transparency. */
.node .pill.float.ok{color:var(--good);
border-color:color-mix(in srgb,var(--good) 48%,transparent);
background:color-mix(in srgb,var(--good) 12%,var(--bg))}
.node .pill.float.warn{color:var(--warn);
border-color:color-mix(in srgb,var(--warn) 42%,transparent);
background:color-mix(in srgb,var(--warn) 12%,var(--bg))}
.node .pill.float.busy{color:var(--accent-ink);
border-color:color-mix(in srgb,var(--accent) 52%,transparent);
background:color-mix(in srgb,var(--accent) 12%,var(--bg));
animation:pulse 1.1s ease-in-out infinite}
.node .pill.float.bad{color:var(--bad);
border-color:color-mix(in srgb,var(--bad) 48%,transparent);
background:color-mix(in srgb,var(--bad) 12%,var(--bg))}
.node.state-busy .node-ico{background:var(--accent)!important;
color:var(--on-accent)}
.node.state-bad .node-ico{background:var(--bad)!important;color:var(--on-accent)}
.node-head{display:flex;align-items:center;gap:12px;padding:14px 16px 0}
.node-ico{flex:none;width:30px;height:30px;border-radius:50%;display:grid;
place-items:center;font:700 12px/1 var(--mono);color:var(--on-accent)}
.node.state-idle .node-ico{background:var(--panel3)!important;color:var(--ink3);
border:1px solid var(--line2)}
.node-name{flex:1;min-width:0;font-family:var(--serif);font-size:17px;
font-weight:600;letter-spacing:-.02em}
.node.state-idle .node-name{color:var(--ink2);font-weight:600}
.node-step{flex:none;font:600 10px/1 var(--mono);text-transform:uppercase;
letter-spacing:.08em;color:var(--ink3)}
.pill{flex:none;font:600 10.5px/1 var(--mono);text-transform:uppercase;
letter-spacing:.05em;padding:5px 9px;border-radius:7px;display:inline-flex;
align-items:center;gap:5px}
.pill.ok{background:color-mix(in srgb,var(--good) 14%,transparent);
color:var(--good)}
.pill.warn{background:color-mix(in srgb,var(--warn) 14%,transparent);
color:var(--warn)}
.pill.idle{background:var(--panel2);color:var(--ink3);border:1px solid var(--line)}
.pill.busy{background:color-mix(in srgb,var(--accent) 16%,transparent);
color:var(--accent-ink)}
.pill.bad{background:color-mix(in srgb,var(--bad) 14%,transparent);
color:var(--bad)}
.node-out{border-top:1px solid var(--line);padding:12px 16px;display:flex;
align-items:center;gap:10px;flex-wrap:wrap}
.out-k{font-size:11.5px;color:var(--ink3);margin-right:2px}
.tag{font:500 12px/1 var(--mono);padding:5px 10px;border-radius:7px;
background:var(--panel3);border:1px solid var(--line2);color:var(--ink2)}
.tag b{color:var(--ink);font-weight:600}
/* Three tag tints, one per phase that produces an output: blue for what was
   collected, violet for what was understood, cyan for what was measured. The
   class names are older than the palette and have been left alone deliberately
   — renaming them would touch every call site in the app to say nothing new.
   What each one resolves to is `--c1`, `--c2` and `--c3` in `skins.ONYX`. */
/* Every colour here is a token, and that is not tidiness. These three rules
   used to end in fixed hex values picked off the brand sheet for the night
   palette — `#CFDBE6`, `#EDE8DC`, `#C3CFAE` — which are near-white, and a
   near-white number on a near-white panel is not a number. In daylight the
   flow page therefore showed "Real posts" with the count missing, on the one
   screen whose whole job is saying what each step produced. The tint stays
   per-phase; the ink follows the palette. */
.tag.blue{background:color-mix(in srgb,var(--c1) 13%,transparent);
border-color:color-mix(in srgb,var(--c1) 32%,transparent);color:var(--c1)}
.tag.blue b{color:var(--ink)}
.tag.sand{background:color-mix(in srgb,var(--c2) 11%,transparent);
border-color:color-mix(in srgb,var(--c2) 30%,transparent);color:var(--c2)}
.tag.sand b{color:var(--ink)}
.tag.green{background:color-mix(in srgb,var(--c3) 13%,transparent);
border-color:color-mix(in srgb,var(--c3) 32%,transparent);color:var(--accent-ink)}
.tag.green b{color:var(--ink)}

/* connector between nodes: a labelled arrow, so the thing that flows down the
   pipeline is named rather than implied */
.link{height:54px;display:grid;place-items:center;position:relative}
.link:before{content:"";position:absolute;top:0;bottom:0;width:1px;
background:var(--line2)}
.link:after{content:"";position:absolute;bottom:0;width:0;height:0;
border-left:4px solid transparent;border-right:4px solid transparent;
border-top:5px solid var(--line3)}
.link i{position:relative;background:var(--panel2);padding:4px 11px;
border-radius:20px;border:1px solid var(--line2);font:500 11px/1.3 var(--mono);
color:var(--ink2);font-style:normal;white-space:nowrap;max-width:88%;
overflow:hidden;text-overflow:ellipsis}
.link.empty i{color:var(--ink3);background:var(--bg)}

/* ---------- where you stand, at the top of the page ----------
   Three states and no fourth: absent, a run in flight, a run stored. Every
   figure in here is printed with the thing it is divided by, and every animated
   thing in here is animating because something actually changed — a square
   appearing means an answer landed, not that a designer liked movement.

   This was a column pinned to the right of every page, scrolling on its own.
   It is a band at the top of the page now, for two reasons. The figure a
   subscriber opens the app for was sitting where the eye arrives last, in
   furniture that reads as chrome rather than as content; and 372px is not
   enough width to lay out twelve questions against six assistants, so the
   grid it exists to show was permanently cramped and clipped.

   All of it is switched off wholesale under `prefers-reduced-motion`, at the
   foot of this stylesheet. Nothing here needs its animation to be readable;
   the animation is how you notice, not how you understand. */
.stand{background:var(--panel);border:1px solid var(--line);
border-radius:var(--r);padding:15px 20px 19px;margin-bottom:12px}
.stand-h{display:flex;align-items:center;justify-content:space-between;gap:8px;
padding-bottom:12px;border-bottom:1px solid var(--line)}
.stand-h b{display:flex;align-items:center;gap:8px;font-size:10.5px;
text-transform:uppercase;letter-spacing:.11em;color:var(--ink3);font-weight:600}
.stand-h>span{font:600 11px/1 var(--mono);color:var(--ink3)}
.stand.live{border-color:color-mix(in srgb,var(--accent) 40%,transparent)}
.pv-clock{font:600 12px/1 var(--mono);color:var(--accent-ink)}
.beat{width:7px;height:7px;border-radius:50%;background:var(--accent);
animation:pulse 1.1s ease-in-out infinite}
/* The three readings side by side rather than stacked down a gutter, and as
   many to a row as the window has room for. Each is its own card so the band
   reads as three findings and not one long run of shapes.

   Wrapping flex rather than a grid of auto-fit tracks, because a grid leaves
   the last card sitting in one track with an empty track beside it — on an
   840px page that is a card ending halfway across, which reads as a page that
   failed to load. Flex lets whatever is on the last row grow into the width
   that is going spare. */
.stand-cards{display:flex;flex-wrap:wrap;gap:12px;margin-top:15px;
align-items:stretch}
.stand-cards>.pv{flex:1 1 292px;min-width:0}
.pv{background:var(--panel2);border:1px solid var(--line);border-radius:12px;
padding:14px 16px 15px;display:flex;flex-direction:column;min-width:0}
.pv-h{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
margin-bottom:10px}
/* No card ends short of the one beside it: the reading takes the slack and the
   key or the caption under it stays at the foot, so three cards of different
   lengths still line up top and bottom. A card stopping halfway up its own
   border reads as a card that failed to load. */
.pv-b{flex:1;min-width:0;display:flex;flex-direction:column;
justify-content:center}
/* Cards in a row are as tall as the tallest, so a short one has slack to
   spend. It goes into the *rows*, equally — the same choice made for the map
   on step 4 — because pushing the rows apart instead opens one visible gap per
   gap, and a chart floating in the middle of its own card looks like a chart
   that lost its other half. */
.pv-b>.sov{flex:1 0 auto}
.pv>.pv-key,.pv>.pv-cap{margin-top:auto;padding-top:11px}
.pv-h b{font-size:12px;font-weight:600;color:var(--ink)}
.pv-more{font-size:11px;color:var(--ink3);white-space:nowrap}
.pv-cap,.pv-sub{margin:8px 0 0;font-size:11px;line-height:1.45;color:var(--ink3)}
.pv-now{margin:14px 0 12px;font-size:13.5px;color:var(--ink);line-height:1.45}
.pv-warn{margin:13px 0 0;padding:9px 13px;border-radius:9px;font-size:12px;
color:var(--warn);background:color-mix(in srgb,var(--warn) 12%,transparent);
border:1px solid color-mix(in srgb,var(--warn) 34%,transparent)}
/* The way out of a run that is spending. Deliberately quiet — it sits under a
   run somebody is watching, and a loud red button beside a working progress bar
   reads as "something is wrong" rather than "you may change your mind". The
   sentence beside it is not optional: it is the one place the product admits
   that the call already in flight has been paid for either way. */
.pv-stop{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
margin:14px 0 0;padding-top:13px;border-top:1px solid var(--line2)}
.pv-stop button{flex:0 0 auto;padding:7px 14px;font-size:12.5px;
font-weight:500;border-radius:8px;cursor:pointer;color:var(--ink2);
background:transparent;border:1px solid var(--line2)}
.pv-stop button:hover{color:var(--warn);border-color:var(--warn);
background:color-mix(in srgb,var(--warn) 8%,transparent)}
.pv-stop-note{flex:1 1 220px;margin:0;font-size:11px;line-height:1.45;
color:var(--ink3)}
p.pv-stop-note{margin:14px 0 0;padding-top:13px;
border-top:1px solid var(--line2)}
.pv-bar{height:5px;margin:0;border-radius:9px;background:var(--panel3);
overflow:hidden}
.pv-bar i{display:block;height:100%;width:var(--w);border-radius:9px;
background:var(--accent);transition:width .5s ease}
.pv-sub{padding:0}

/* The headline, and the sentence saying what it is out of. The number is never
   allowed on screen without it — a rate with a hidden denominator is the exact
   false precision this product exists to replace. */
/* The number and the sentence saying what it is out of, on one line. Stacked
   and centred was what a 372px gutter forced; across a page they read as the
   single statement they are, and the denominator can be a sentence rather than
   four wrapped fragments. */
/* `flex-direction` is set explicitly, and must stay set: `.pv` above is a
   column, and inheriting that turned the denominator's flex basis into a
   320px height — three hundred pixels of nothing between the figure and the
   readings under it. */
.pv-score{display:flex;flex-direction:row;align-items:baseline;flex-wrap:wrap;
gap:4px 24px;padding:16px 2px 4px;background:none;border:0;border-radius:0}
.pv-n{font:600 54px/1 var(--serif);color:var(--ink);flex:none;
letter-spacing:-.04em;animation:rise .55s cubic-bezier(.2,.7,.3,1) backwards}
.pv-n b{font-size:26px;font-weight:600;color:var(--ink3);margin-left:2px}
.pv-n small{font:400 15px/1 var(--mono);color:var(--ink3);margin-left:6px}
.pv-d{margin:0;flex:1 1 320px;min-width:0;font-size:12.5px;line-height:1.5;
color:var(--ink3);max-width:64ch}

/* The answer grid: a row per question, a column per assistant. */
.agrid{display:grid;grid-template-columns:minmax(0,1fr) repeat(var(--cols),30px);
gap:4px;align-items:center}
.gq{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
font-size:11.5px;color:var(--ink2);padding-right:4px}
/* Wide enough for the longest assistant name anybody runs, because a heading
   that wraps to two lines pushes its own column out of line with the rest. */
.gp{font:600 8.5px/1 var(--mono);color:var(--ink3);text-align:center;
letter-spacing:.02em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.gc{display:block;width:30px;height:16px;border-radius:5px;
background:var(--panel3);border:1px solid var(--line2);
animation:cellin .3s ease-out backwards;animation-delay:var(--d,0s)}
.gc.s-top{background:var(--good);border-color:var(--good)}
.gc.s-seen{background:color-mix(in srgb,var(--good) 34%,transparent);
border-color:color-mix(in srgb,var(--good) 55%,transparent)}
.gc.s-lost{background:var(--panel3);border-color:var(--line3)}
.gc.s-bad{background:color-mix(in srgb,var(--bad) 34%,transparent);
border-color:color-mix(in srgb,var(--bad) 60%,transparent)}
.gc.s-failed{background:repeating-linear-gradient(45deg,var(--panel3),
var(--panel3) 3px,var(--panel2) 3px,var(--panel2) 6px);border-color:var(--line2)}
.gc.s-none{background:transparent;border-style:dashed}
.pv-key{display:flex;flex-wrap:wrap;gap:4px 12px;margin:11px 0 0;font-size:11px;
color:var(--ink3)}
.pv-key span{display:inline-flex;align-items:center;gap:5px}
.pv-key .gc{width:13px;height:10px;border-radius:3px;animation:none}

/* Share of the answers. The client's bar is the only accent one, and it stays
   in rank order — being fourth is the finding. */
.sov{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:11.5px}
.sov-n{flex:0 0 34%;max-width:190px;min-width:0;overflow:hidden;
text-overflow:ellipsis;white-space:nowrap;color:var(--ink2)}
.sov-t{flex:1;min-width:0;height:9px;border-radius:9px;background:var(--panel3);
position:relative}
.sov-t i{display:block;height:100%;width:var(--w);border-radius:9px;
background:var(--line3);position:relative;
animation:grow .6s cubic-bezier(.2,.7,.3,1) backwards;
animation-delay:var(--d,0s)}
/* Three things happen per bar and then stop: it draws, a sheen crosses the
   track behind it, and a cap lands on the end. The sheen travels by moving its
   own background rather than by being transformed, because a transform would
   carry the highlight out past the ends of the track. */
.sov-t:after{content:"";position:absolute;inset:0;border-radius:9px;
background:linear-gradient(90deg,transparent,var(--sheen),transparent);
background-size:55% 100%;background-repeat:no-repeat;background-position:-65% 0;
animation:sheen 1s ease-out backwards;
animation-delay:calc(var(--d,0ms) + 140ms)}
.sov-t i:after{content:"";position:absolute;right:-1px;top:-2px;width:2px;
height:13px;border-radius:1px;background:var(--line3);
animation:cap .36s ease-out backwards;animation-delay:calc(var(--d,0ms) + 560ms)}
.sov.you .sov-t i:after{background:var(--accent)}
@keyframes sheen{from{background-position:-65% 0}to{background-position:165% 0}}
@keyframes cap{from{opacity:0;transform:translateY(-4px)}to{opacity:1}}
/* Figures line up in a column whatever they are counting. */
[data-count],.sov-v,.pv-n{font-variant-numeric:tabular-nums}
.sov-v{flex:0 0 28px;text-align:right;font:600 11px/1 var(--mono);
color:var(--ink3)}
.sov.you .sov-n{color:var(--ink);font-weight:600}
.sov.you .sov-t i{background:var(--accent)}
.sov.you .sov-v{color:var(--accent-ink)}
/* Where this product's own overall rate sits, marked on a factor's bar. A
   percentage means nothing on its own — 15% is a catastrophe against an
   average of 34% and a triumph against 4% — so the comparison is drawn rather
   than left in the sentence underneath. */
.sov-ref{position:absolute;top:-3px;bottom:-3px;left:var(--at);width:2px;
border-radius:2px;background:var(--line3)}
/* A job somebody has ticked off. Dimmed and struck rather than removed: a
   plan that hides finished work cannot be checked against what was decided,
   and the row is still the place to take the tick back. */
tr.job-done{opacity:.6}
tr.job-done .job-t b{text-decoration:line-through}
/* “How to fix this”, folded under a check that did not pass. Its own control
   rather than the source-group summary it first borrowed: that one is laid out
   as “label ……… count”, which pushed a lone sentence to the far edge and left
   the marker stranded on the other. Closed by default — six checks each
   carrying three paragraphs would bury the thing somebody opened the page for,
   which is which of them failed. */
details.fixit{margin:9px 0 2px;border:1px solid var(--line);border-radius:9px;
background:var(--panel2)}
details.fixit>summary{cursor:pointer;font-size:12px;font-weight:600;
color:var(--accent-ink);padding:8px 12px;list-style:none}
details.fixit>summary::-webkit-details-marker{display:none}
details.fixit>summary:before{content:"▸  "}
details.fixit[open]>summary:before{content:"▾  "}
details.fixit .den{margin:0 12px 9px}
details.fixit ul.den{padding-left:30px}

/* Who the assistants cite. Two axes measuring two different things: across is
   how often the assistants cited a site, up is how much of that site already
   carries a rival's name. Which makes the corners readable without a caption —
   bottom right is a site doing a lot of the answering with nobody's name on it
   yet, top right is a publisher a rival has already worked. Dot size is the
   third fact and not a repeat of the first: how many separate questions that
   site turned up on. The three pictures this replaced each encoded one number
   twice and needed a paragraph underneath saying how to read them. */
/* A whole row to itself. Squeezed into a third of one alongside the grid and
   the share bars, the plot still drew every dot and every name — at about a
   third of the size, which is a chart nobody can read without zooming. The
   child selector is deliberate: `.stand-cards>.pv` sets the flex basis for
   the cards in that row and outranks a bare class. */
.stand-cards>.pv-cited{flex:1 1 100%}
.cite-plot{display:block;width:100%;height:auto;overflow:visible;margin:2px 0 0}
.cite-plot circle{animation:pop .45s cubic-bezier(.2,.8,.3,1.2) backwards;
animation-delay:var(--d,0s)}
/* The fill only re-states the height, on purpose. One variable on two channels
   makes a chart quicker to read; it was two variables sharing one channel that
   wrecked the versions before this. */
/* Grey up to amber, mixed in oklch. Two things were wrong with running it from
   blue: blue is what marks the client's own dot, so the same colour was saying
   both “this is you” and “no rival is on this one”, and a straight sRGB blend
   from blue to amber passes through grey, which made every site in the middle
   of the range — the ones actually worth arguing about — look unmeasured. From
   a neutral the chroma just climbs amber's own hue, so nothing else is claimed
   and blue is left meaning exactly one thing. The ramp is bent (the share is
   raised to a power before it is mixed) so the low-middle separates instead of
   sitting in one flat tan. Nothing is read off the colour — the height already
   carries that number exactly — so bending it costs nothing. */
.d-src{fill:color-mix(in oklch,var(--warn) var(--mix,0%),var(--ink3));
fill-opacity:.5;stroke:color-mix(in oklch,var(--warn) var(--mix,0%),var(--ink3));
stroke-opacity:1;stroke-width:1.3}
.d-you{fill:var(--accent);fill-opacity:.9;stroke:var(--accent-ink);
stroke-width:2}
/* Named on the chart, not in a key underneath. A legend of twelve domains is a
   second chart to join up by colour, and here the colour already means
   something else. */
.cite-plot text{font-family:var(--mono)}
.l-src{font-size:10px;fill:var(--ink2);animation:fade .5s ease backwards;
animation-delay:var(--d,0s)}
.l-you{font-size:10.5px;font-weight:600;fill:var(--accent-ink);
animation:fade .5s ease backwards;animation-delay:var(--d,0s)}
.g-line{stroke:var(--line);stroke-width:1;stroke-dasharray:2 5}
.g-axis,.g-tick{stroke:var(--line2);stroke-width:1}
.g-num{font-size:10px;fill:var(--ink3);font-variant-numeric:tabular-nums}
.g-title{font-size:10.5px;fill:var(--ink3)}
/* What a dot's size means, said with two dots rather than a sentence. */
.pv-key .k-size{gap:7px}
.pv-key .k-size i{flex:none;width:var(--s);height:var(--s);border-radius:50%;
background:color-mix(in srgb,var(--ink3) 42%,transparent);
border:1px solid color-mix(in srgb,var(--ink3) 70%,transparent)}
.pv-key .k-note{color:var(--accent-ink);font-weight:600}
/* The key sits under the plot, not at the bottom of whatever height the
   card next to it happens to want. This card is a whole row on its own,
   so `margin-top:auto` had nothing to push against but empty card. */
.pv-cited>.pv-key{margin-top:6px;padding-top:0}
.cite-empty{margin:0;font-size:11.5px;line-height:1.5;color:var(--ink3)}

/* What the run is doing, newest first. */
.feed{list-style:none;margin:0;padding:0}
/* The log starts at the top of its card and grows down, and the space under it
   is left alone. Spreading four lines over the height of a twelve-row grid was
   a card full of gaps: this is a running log, and a log with seventy pixels
   between entries no longer reads as one thing happening after another. It
   fills as the run goes on, which is the point of it being on screen. */
.pv-feed>.pv-b{justify-content:flex-start}
.feed li{padding:6px 0 6px 15px;font:400 11.5px/1.4 var(--mono);
color:var(--ink3);position:relative;overflow-wrap:anywhere;
animation:fade .4s ease-out backwards;animation-delay:var(--d,0s)}
.feed li:before{content:"";position:absolute;left:2px;top:11px;width:5px;
height:5px;border-radius:50%;background:var(--line3)}
.feed li.now{color:var(--ink)}
.feed li.now:before{background:var(--accent);
animation:pulse 1.1s ease-in-out infinite}

@keyframes cellin{from{opacity:0;transform:scale(.4)}
to{opacity:1;transform:scale(1)}}
@keyframes grow{from{width:0}to{width:var(--w)}}
@keyframes pop{from{opacity:0;transform:scale(0)}to{opacity:1;transform:scale(1)}}
@keyframes fade{from{opacity:0;transform:translateY(-3px)}
to{opacity:1;transform:none}}
@keyframes rise{from{opacity:0;transform:translateY(9px)}
to{opacity:1;transform:none}}

/* ---------- content below the flow ---------- */
/* The working surface. It used to stop at 840px — a book measure — which was
   right when every page was prose and wrong once the pages carried evidence
   charts, before-and-after comparisons and tables with six columns in them.
   Those were being folded into a third of a laptop screen and scrolled
   sideways. The page is wide now; the prose inside it still isn't, because
   paragraphs cap their own measure. */
.sheet{max-width:1200px;margin:0 auto}
.sec{scroll-margin-top:18px;padding-top:34px}
.sec-h{display:flex;align-items:center;gap:10px;margin:0 0 4px}
/* The conditions a chart was read under, on the same line as its title
   rather than in a footnote below it. On a before-and-after that caption
   is not decoration — "same wording, same surfaces, paired questions only"
   is what makes the two bars comparable at all, and a reader who scrolls
   past it has been allowed to read a comparison that might not be one. */
.sec-h .aside{margin-left:auto;font-family:var(--mono);font-size:11px;
color:var(--ink3);text-align:right}
@media(max-width:700px){.sec-h .aside{display:none}}
.sec-h .n{width:24px;height:24px;border-radius:50%;display:grid;
place-items:center;font:700 11px/1 var(--mono);color:var(--on-accent);flex:none}
.sec-h h2{margin:0;font-family:var(--serif);font-size:23px;font-weight:600;
letter-spacing:-.025em;text-transform:none;color:var(--ink)}
.sec-plain{color:var(--ink2);font-size:13.5px;margin:0 0 16px;max-width:74ch;
line-height:1.6}
/* Playfair announces; Inter labels. `h2` is the small uppercase eyebrow used all
   over the app, so it stays in the sans — a serif at 10px is unreadable. */
h1{font-family:var(--serif);font-size:28px;font-weight:600;line-height:1.15;
margin:0 0 6px;letter-spacing:-.032em}
h2{font-size:10.5px;margin:0 0 12px;text-transform:uppercase;letter-spacing:.13em;
color:var(--ink3);font-weight:600}
h3{font-family:var(--serif);font-size:17px;font-weight:600;margin:0;
letter-spacing:-.02em}
.sub{color:var(--ink3);font-size:12.5px;margin:0 0 18px;max-width:76ch}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
padding:19px 21px;margin-bottom:12px;overflow-x:auto}
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));
gap:8px;margin-top:14px}
.tile{background:var(--panel2);border:1px solid var(--line);border-radius:11px;
padding:11px 13px}
.tile b{display:block;font-size:20px;font-weight:650;line-height:1.15;
letter-spacing:-.03em}
.tile span{display:block;color:var(--ink3);font-size:10px;text-transform:uppercase;
letter-spacing:.08em;margin-top:5px}
table{width:100%;border-collapse:collapse;font-size:13px}
td,th{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);
vertical-align:top}
th{color:var(--ink3);font-weight:500;font-size:10.5px;text-transform:uppercase;
letter-spacing:.08em}
tr:last-child td{border-bottom:none}
.badge{display:inline-block;padding:1px 7px;border-radius:5px;font-size:11px;
font-family:var(--mono);margin-left:6px}
.strong{background:color-mix(in srgb,var(--good) 16%,transparent);
color:var(--good)}
.mid{background:color-mix(in srgb,var(--warn) 16%,transparent);color:var(--warn)}
.weak{background:color-mix(in srgb,var(--bad) 15%,transparent);color:var(--bad)}
.chips{margin-top:10px;display:flex;gap:7px;flex-wrap:wrap;align-items:center}
.chip{padding:5px 11px;font-size:12px;background:var(--panel2);
border:1px solid var(--line);border-radius:20px;color:var(--ink2)}
a.chip:hover{border-color:var(--accent);color:var(--accent-ink);
text-decoration:none;
background:color-mix(in srgb,var(--accent) 10%,transparent)}
.tabs{display:flex;gap:7px;margin-bottom:18px;flex-wrap:wrap}
.tab{padding:7px 15px;border-radius:999px;background:var(--panel2);
border:1px solid var(--line);color:var(--ink2);font-size:12.5px;
display:inline-block}
.tab:hover{border-color:var(--line3);color:var(--ink);text-decoration:none}
.tab.on{background:var(--accent);color:var(--on-accent);
border-color:var(--accent);font-weight:600}
code{font-family:var(--mono);font-size:12px;color:var(--ink2);
background:var(--panel2);padding:1px 5px;border-radius:4px}
pre{overflow-x:auto}
pre.method{font-family:var(--mono);font-size:12px;color:var(--ink2);
background:var(--panel2);border:1px solid var(--line);border-radius:9px;
padding:11px 13px;margin:8px 0;line-height:1.6}
/* A written message shown where it will be read, rather than one that makes
   its own card wider than the window. `overflow-x:auto` on a `pre` only
   scrolls once something has stopped the block growing, and inside a card
   nothing had — so a hundred-character line of somebody's outreach email
   pushed the whole page sideways and clipped every paragraph beside it. */
pre.msg{white-space:pre-wrap;overflow-wrap:anywhere;max-width:100%}
.pending{border:1px dashed var(--line2);border-radius:11px;padding:14px;
color:var(--ink3);font-size:12.5px}
.err{background:color-mix(in srgb,var(--bad) 9%,transparent);
border:1px solid var(--bad);border-radius:11px;padding:14px;font-size:13px}
.score{display:flex;align-items:center;gap:16px}
/* The grade reads as a dial on the brand sheet, so it is round here rather than
   a rounded square, and the figure inside it is set in the display face. */
.dial{width:58px;height:58px;border-radius:50%;display:grid;place-items:center;
font-family:var(--serif);font-size:22px;font-weight:600;flex:none;letter-spacing:-.025em}
.dial.sm{width:42px;height:42px;font-size:17px}

/* The product, at the top of the analysis page. Small, factual, and the fastest
   way to confirm the right page was fetched — the slug in the topbar is not
   something anyone recognises at a glance. */
.card.product{padding:17px 19px}
.product-row{display:flex;gap:17px;align-items:flex-start}
.shot{width:104px;height:104px;object-fit:contain;border-radius:12px;flex:none;
background:var(--panel3);border:1px solid var(--line)}
/* No picture collected. Still a square, and still exactly the size of the one
   beside it: a card that changes shape when a shot is missing is how a person
   stops being able to tell "nothing to show" from "something broke". */
.shot.none{display:flex;align-items:center;justify-content:center;
font-family:var(--serif);font-size:34px;font-weight:600;color:var(--ink3);
border-style:dashed;letter-spacing:-.03em;cursor:default}
/* ---------- the products list on the home page ---------- */
/* One line per product, ruled rather than boxed: eleven cards is a page you
   scroll and eleven lines is a page you read. What each line carries is the
   picture, the name, what that product has to show for itself, how far it got
   through the five steps, and the press that is waiting — the old list was a
   blue link with "measured 1 time" under it, which answered none of those.
   Grouped, so the products waiting on somebody are not buried under the ones
   that are finished. */
.phead{margin:30px 0 16px;font-size:21px;letter-spacing:-.032em}
.pgroup{display:flex;align-items:center;gap:9px;margin:0 0 6px;padding:0 14px}
.pgroup .dot{width:6px;height:6px;border-radius:999px;flex:none}
.pgroup .dot.c1{background:var(--c1)}
.pgroup .dot.c2{background:var(--c2)}
.pgroup .dot.c3{background:var(--c3)}
.pgroup .dot.pin{background:var(--accent-ink)}
.pgroup b{font-size:10px;text-transform:uppercase;letter-spacing:.11em;
color:var(--ink3);font-weight:600}
.pgroup>span:last-child{font-size:11px;color:var(--ink3)}
.plist{margin:0 0 28px}
/* `auto` for the note and the press so a long one is never clipped, and the
   name takes what is left. A fixed column for either put "24 improvements
   waiting" on two lines on the one row most worth reading. */
/* The row is a link and the pin is a button, so they are siblings rather than
   one inside the other — a button inside an anchor is markup browsers disagree
   about, and the customer gets whichever their browser decided. The rule and
   the hover therefore belong to the line that holds both, so pointing at
   either half lights the whole row. */
.prow-line{display:flex;align-items:center;border-radius:10px;
border-bottom:1px solid var(--line)}
.prow-line:last-child{border-bottom:0}
.prow-line:hover{background:var(--panel)}
.prow-line:hover .prow-next b{color:var(--accent-ink)}
.prow-line:hover .prow-next i{color:var(--accent-ink)}
.prow{flex:1;min-width:0;
display:grid;grid-template-columns:40px minmax(0,1fr) auto 62px auto;
gap:18px;align-items:center;padding:13px 14px;color:inherit}
.prow:hover{text-decoration:none}
/* The pin. Always drawn, never revealed on hover: a control that only exists
   under the pointer cannot be reached from a keyboard and does not exist at
   all on a touch screen. Unpinned it is quiet enough to ignore; pinned it is
   the accent, filled, and reads as a state rather than as a button. */
.pinf{flex:none;display:flex;margin:0 8px 0 2px}
.pin{display:inline-flex;align-items:center;justify-content:center;
width:30px;height:30px;padding:0;border:0;border-radius:8px;cursor:pointer;
background:transparent;color:var(--ink3);
transition:color .12s,background .12s}
.pin svg{width:17px;height:17px;display:block;fill:none}
.pin:hover{color:var(--ink);background:var(--panel3)}
.pin.on{color:var(--accent-ink)}
.pin.on svg{fill:currentColor}
.pin.on:hover{color:var(--accent-ink)}
/* Product photographs are shot on white, so the plate behind one is white. On
   the panel grey they read as broken images rather than as photographs. */
.shot.mini{width:40px;height:40px;border-radius:8px;padding:3px;
background:#fff;border:0}
.shot.mini.none{background:var(--panel3);border:1px solid var(--line);
border-style:dashed;font-size:15px}
.prow-id{min-width:0;display:flex;align-items:baseline;gap:10px}
.prow-id b{font-size:14px;font-weight:600;letter-spacing:-.016em;
color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.prow-meta{font-size:12px;color:var(--ink3);white-space:nowrap;flex:none}
.prow-note{font-size:11.5px;color:var(--ink3);white-space:nowrap;
justify-self:end}
/* A mock run bought nothing and asked nobody. Same warning colour as every
   other "do not read this as a measurement" in the app, because it is the same
   claim — and never a bare number, which is what a measurement looks like. */
.prow-note.mock{color:var(--warn);display:inline-flex;align-items:center;
gap:6px}
.prow-note.mock:before{content:"";width:5px;height:5px;border-radius:999px;
background:var(--warn);flex:none}
/* Five steps, five marks. Which ones are lit, not how many — a product with no
   forum posts can still have questions mined from real search demand, and a
   filled prefix would report that as further along than it is. */
.pdots{display:flex;gap:5px;align-items:center}
.pdots i{width:6px;height:6px;border-radius:999px;background:var(--line2);
flex:none;font-style:normal}
.pdots i.on{background:var(--accent-ink)}
/* Ran, and found nothing. A ring rather than a fill or a blank, because it is
   neither of those, and reading it as either misstates what was paid for. */
.pdots i.ran{background:transparent;
box-shadow:inset 0 0 0 1.5px var(--accent-ink)}
.prow-next{display:flex;align-items:center;gap:9px;justify-self:end;
white-space:nowrap}
.prow-next b{font-size:13px;font-weight:500;color:var(--ink)}
.prow-next i{font-style:normal;color:var(--ink3);font-size:13px}
@media(max-width:820px){
/* The two middle columns are the first to go: the press that is waiting is
   what the row is for, and it keeps its place beside the name. */
.prow{grid-template-columns:40px minmax(0,1fr) auto;gap:14px}
.prow-note,.pdots{display:none}}

.product-body{min-width:0;flex:1}
.product-name{font-family:var(--serif);font-size:24px;font-weight:600;
margin:0 0 4px;line-height:1.18;letter-spacing:-.032em}
.product-line{margin:0;font-size:13px;color:var(--ink2)}
.product-line b{color:var(--ink);font-size:15px}
.product-desc{margin:8px 0 0;font-size:12.5px;color:var(--ink3);max-width:72ch}
.product-geo{display:flex;gap:12px;align-items:center;margin-top:14px;
padding-top:14px;border-top:1px solid var(--line)}
.product-geo b{font-size:13px}
.product-geo .of{font-size:11px;color:var(--ink3);font-weight:400}
.product-geo p{margin:2px 0 0;max-width:74ch}
.sep{color:var(--line3)}

/* The sources side tab: every real conversation, openable. */
.srcgroup{margin-bottom:8px}
.srcgroup>summary{cursor:pointer;font-size:12px;font-weight:600;padding:5px 0;
display:flex;justify-content:space-between;gap:8px;align-items:center}
.srcn{color:var(--ink3);font-weight:400;font-size:11px;border:1px solid
var(--line2);border-radius:20px;padding:0 7px}
.srcrow{display:block;background:var(--panel2);border:1px solid var(--line);
border-radius:9px;padding:8px 10px;margin:5px 0;color:var(--ink2)}
a.srcrow:hover{border-color:var(--line3);text-decoration:none;color:var(--ink)}
.srcrow.dead{opacity:.6}
.srcq{display:block;font-size:11.5px;line-height:1.45}
.srcm{display:block;color:var(--ink3);font-size:10.5px;margin-top:4px;
font-family:var(--mono)}
.gap{background:var(--panel2);border:1px solid var(--line);border-left:2px solid
var(--warn);border-radius:9px;padding:8px 10px;margin:5px 0}
.gap b{font-size:11.5px}
.warnchip{border-color:var(--warn);color:var(--warn)}

/* ── The evidence chart a brief opens with ────────────────────────────────
   A brief used to open with two numbers in a sentence — "buyers raise this 31
   times, you are named in 15% of answers" — and the whole argument for doing
   the work is the *distance* between those two, which a sentence hides and two
   bars cannot. So the brief now opens with the four readings the job rests on,
   drawn against each other, each with its own denominator printed beside it.

   Every bar is its own true proportion: the fill is the numerator over the
   denominator printed at the end of the row, never a share of the biggest bar
   on the chart. Scaling to the biggest bar makes a 6% reading look like most
   of the track, which is the exact false-precision failure this product is
   positioned against. */
.ev{margin:14px 0 0}
.ev-h{font-family:var(--mono);font-size:11px;color:var(--ink3);
letter-spacing:.01em;margin:0 0 12px}
.ev-r{display:flex;align-items:center;gap:14px;padding:5px 0;font-size:12px}
/* The labels are right-aligned against the tracks, so the bars all start on
   one line and the eye compares lengths rather than hunting for their feet. */
.ev-n{flex:0 0 30%;max-width:280px;min-width:0;text-align:right;
color:var(--ink2);line-height:1.35;overflow-wrap:anywhere}
.ev-t{flex:1;min-width:0;height:22px;border-radius:5px;background:var(--panel3);
position:relative;overflow:hidden}
.ev-t i{display:block;height:100%;width:var(--w);border-radius:5px;
background:var(--line3);transform-origin:left center;
animation:grow .6s cubic-bezier(.2,.7,.3,1) backwards;
animation-delay:var(--d,0s)}
/* Three roles, three colours, and they mean the same thing on every brief:
   clay is the reading the job is aimed at fixing, sage is you elsewhere, and
   plain grey is everybody else and every raw count. `short` rather than `gap`
   because `.gap` is already the warning box further down this sheet, and a row
   picking that up grew an orange bracket down its left edge. */
.ev-r.you .ev-t i{background:var(--accent)}
.ev-r.short .ev-t i{background:var(--bad)}
.ev-r.short .ev-n{color:var(--ink);font-weight:600}
.ev-v{flex:0 0 130px;font-variant-numeric:tabular-nums;font-size:13px;
color:var(--ink);text-align:right;white-space:nowrap}
.ev-v span{font-family:var(--mono);font-size:11.5px;color:var(--ink3);
margin-left:7px}
.ev-say{margin:16px 0 0;font-size:13.5px;line-height:1.6;color:var(--ink2);
max-width:82ch}
.ev-say b{color:var(--ink)}
.ev+.kv,.ev+p,.ev+table{margin-top:20px}
.ev+.ev{margin-top:22px;padding-top:20px;border-top:1px solid var(--line)}
@media(max-width:760px){.ev-n{flex-basis:38%}.ev-v{flex-basis:96px}}

/* ── Every factor, before and after ───────────────────────────────────────
   Stage five's summary used to be a stack of `.sov` pairs, one factor after
   another, and the comparison a customer actually wants — *which of these
   moved and which did not* — cannot be made by scrolling. One row per factor,
   the two readings stacked on the same track so the difference is a shape
   rather than a subtraction, the movement on the right, and beside it the
   honest word about whether the movement can be stood behind. */
.ba{display:flex;align-items:center;gap:18px;padding:15px 0;
border-top:1px solid var(--line)}
.ba:first-of-type{border-top:0}
.ba-n{flex:0 0 30%;max-width:260px;min-width:0}
.ba-n b{display:block;font-size:13.5px;font-weight:600;letter-spacing:-.01em}
.ba-n span{display:block;color:var(--ink3);font-size:11px;margin-top:3px;
font-family:var(--mono)}
.ba-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
.ba-t i{display:block;height:11px;border-radius:6px;background:var(--panel3);
position:relative}
.ba-t i:before{content:"";position:absolute;inset:0 auto 0 0;width:var(--w);
border-radius:6px;background:var(--line3);transform-origin:left center;
animation:grow .6s cubic-bezier(.2,.7,.3,1) backwards}
/* The lower bar is the reading after the work, so it carries the accent — the
   pair reads top-to-bottom as then-and-now without needing a legend. */
.ba-t i.after:before{background:var(--accent)}
.ba-t i.down:before{background:var(--bad)}
.ba-v{flex:0 0 44px;text-align:right;font-variant-numeric:tabular-nums;
font-size:17px;font-weight:600;letter-spacing:-.02em;color:var(--ink3)}
.ba-v.up{color:var(--accent-ink)}
.ba-v.down{color:var(--bad)}
/* Never a verdict. Two short mono lines saying what this movement is and is
   not allowed to be read as — "direction only / too few questions" — because a
   number with no such line beside it will be read as a result. */
.ba-w{flex:0 0 160px;font-family:var(--mono);font-size:10.5px;line-height:1.45;
color:var(--ink3)}
@media(max-width:820px){.ba{flex-wrap:wrap}.ba-n{flex-basis:100%;max-width:none}
.ba-w{flex-basis:100%;text-align:left}}

/* A stage of the buying journey, holding its questions. */
.stagegroup{margin:24px 0 0;padding-top:19px;border-top:1px solid var(--line)}
.stagegroup:first-of-type{border-top:0;padding-top:0}
.stagehead{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.stagehead h3{margin:0;font-size:15px;letter-spacing:-.018em}
.stageblurb{margin:5px 0 13px;color:var(--ink3);font-size:12.5px;max-width:74ch}
.stage{margin-top:15px}
.stage-name{font-weight:600;font-size:13px}
.stage-blurb{color:var(--ink3);font-size:12px;margin:2px 0 9px;max-width:74ch}
.prompt{background:var(--panel2);border:1px solid var(--line);border-radius:10px;
padding:10px 13px;margin-bottom:6px}
.method{color:var(--ink3);font-size:11.5px;margin-top:3px}
.method a{color:var(--ink2);text-decoration:underline;text-underline-offset:2px}
.src{color:var(--ink3);margin-left:8px;font-size:11px}
.clean{color:var(--good);font-size:11px;font-family:var(--mono)}
.leak{color:var(--bad);font-weight:700;font-size:11px;font-family:var(--mono)}
.ok{color:var(--good);font-size:13px}
.issue{padding:10px 13px;background:var(--panel2);border:1px solid var(--line);
border-radius:10px;margin-bottom:6px;font-size:13px}
.sev{font-size:10.5px;font-family:var(--mono);text-transform:uppercase}
.high{color:var(--bad)}.medium{color:var(--warn)}.low{color:var(--ink3)}
.kv{display:grid;grid-template-columns:112px 1fr;gap:7px 16px;margin:0;
font-size:13px}
.kv dt{color:var(--ink3);font-size:12px}
.kv dd{margin:0;overflow-wrap:anywhere}
details{margin-top:12px}
details summary{cursor:pointer;color:var(--ink3);font-size:12px;list-style:none;
padding:6px 0;user-select:none}
details summary::-webkit-details-marker{display:none}
details summary:before{content:"▸ ";color:var(--ink3)}
details[open] summary:before{content:"▾ "}
details summary:hover{color:var(--accent-ink)}
.explain{background:color-mix(in srgb,var(--accent) 8%,transparent);
border:1px solid color-mix(in srgb,var(--accent) 22%,transparent);
border-radius:12px;padding:14px 16px;margin-bottom:14px;font-size:13.5px;
line-height:1.6;max-width:74ch}
.explain b{color:var(--accent-ink)}
.warnbar{background:color-mix(in srgb,var(--warn) 8%,transparent);
border:1px solid color-mix(in srgb,var(--warn) 34%,transparent);
border-radius:12px;padding:13px 16px;margin-bottom:13px;font-size:13px;
max-width:74ch}
.warnbar b{color:var(--warn)}
.okbar{background:color-mix(in srgb,var(--good) 8%,transparent);
border:1px solid color-mix(in srgb,var(--good) 30%,transparent);
border-radius:12px;padding:13px 16px;margin-bottom:13px;font-size:13px;
max-width:74ch}
.okbar b{color:var(--good)}
.lede{font-size:16px;line-height:1.6;color:var(--ink2);margin:0 0 18px;
max-width:70ch}
.qgroup{margin-bottom:20px}
.qgroup-head{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
margin-bottom:3px}
.qgroup-head h3{margin:0}
.qgroup-note{color:var(--ink3);font-size:12px;margin:0 0 10px;max-width:72ch}
.q{display:flex;gap:12px;align-items:flex-start;background:var(--panel2);
border:1px solid var(--line);border-radius:12px;padding:13px 15px;
margin-bottom:6px}
.q .n{flex:none;width:22px;height:22px;border-radius:7px;background:var(--panel3);
border:1px solid var(--line2);color:var(--ink3);font:500 11px/1 var(--mono);
display:grid;place-items:center;margin-top:2px}
.q p{margin:0;font-size:14.5px;line-height:1.45}
/* the topic a question came out of, printed beside the question itself */
.qtopic{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
margin:-2px 0 0 34px;padding:0}
.qtopic .method{margin:0}
.copy{padding:7px 13px;font-size:12.5px;font-weight:600;background:var(--panel2);
color:var(--ink);border:1px solid var(--line2);border-radius:9px;cursor:pointer;
font-family:inherit}
.copy:hover{border-color:var(--accent);color:var(--accent-ink)}
.derived{border-left:2px solid var(--line2);margin:8px 0 0 2px;
padding:2px 0 2px 12px}
.derived p{margin:6px 0 2px;color:var(--ink2);font-size:12.5px;line-height:1.45}
.jargon{border-bottom:1px dotted var(--ink3);cursor:help;text-decoration:none}
.hero{margin-bottom:6px}
.hero .meta{color:var(--ink3);font-size:12.5px}
.phase-state{font-size:10px;font-family:var(--mono);text-transform:uppercase;
letter-spacing:.06em;padding:2px 7px;border-radius:5px}
.state-built{background:color-mix(in srgb,var(--good) 14%,transparent);
color:var(--good)}
.state-empty{background:color-mix(in srgb,var(--warn) 14%,transparent);
color:var(--warn)}
.state-todo{background:var(--panel2);color:var(--ink3);border:1px solid var(--line)}

/* charts — the appearance ramp, and the shapes drawn from it.
   The ramp is diverging: green where the client was named well, a neutral grey
   where it was named and nothing more, a warm brick where it was not. Every
   step is paired with a glyph in the markup because red against green is the
   one pair colour-blind readers cannot separate.

   These are NOT the interface's own green and red. Those two are chosen to sit
   quietly beside text — below the chroma floor at which a colour stops reading
   as a hue and starts reading as grey — which is exactly right for a calm
   interface and useless for a chart cell that has to mean something. So the
   ramp keeps their hue angles (126° and 34°) and steps them up in chroma and
   apart in lightness until they are actually separable.

   Measured, not judged, against the Machado-Oliveira-Fernandes simulation on
   this app's own surface: the worst adjacent pair now separates at ΔE 14.8
   under protanopia and deuteranopia and 16.2 under normal vision, against 6.9
   and 9.8 for the palette this replaced. Each arm also validates as an ordinal
   ramp on its own — one hue, monotone lightness, visible step gaps.

   What this does NOT fix is green against brick, which no hue choice can: see
   the header of `measure/chart.py` for why the glyph, not the colour, is what
   carries "named" against "not named". */
:root{
--good3:#B8D884;--good2:#819F4D;--good1:#556D28;
--mid:#828E99;--bad1:#A44935;--bad2:#E57157;
/* The number printed *inside* a coloured cell, as opposed to the cell itself.
   It is the ramp colour pulled most of the way to whatever ink the page is
   using, so on paper it lands as a dark green or a dark brick and on a night
   ground as a pale one — one expression, correct on both, and correct in a
   palette nobody has written yet.

   It is written that way because the two fixed tints it replaces were picked
   on the dark ground and only ever checked there. In daylight they were a pale
   green number on a pale green cell and a pale salmon number on a pale salmon
   cell: the map of what buyers ask and who answers it, unreadable, on the one
   page this product exists to show. */
--mc-good:color-mix(in srgb,var(--good3) 35%,var(--ink));
--mc-bad:color-mix(in srgb,var(--bad2) 35%,var(--ink))}
.vz-good3{--vz:var(--good3)}.vz-good2{--vz:var(--good2)}
.vz-good1{--vz:var(--good1)}.vz-mid{--vz:var(--mid)}
.vz-bad1{--vz:var(--bad1)}.vz-bad2{--vz:var(--bad2)}
.vz-dead{--vz:var(--line3)}

.vz-hero{padding:6px 0 16px}
/* The one number the page exists to report, in the display face at the size the
   brand sheet gives its score. */
.vz-figure{font-family:var(--serif);font-size:64px;font-weight:600;line-height:1;
letter-spacing:-.045em;color:var(--ink);font-variant-numeric:tabular-nums}
.vz-chip{display:inline-block;margin-top:10px;padding:4px 11px;font-size:11px;
font-weight:600;border-radius:999px;background:var(--panel3);color:var(--ink2);
border:1px solid var(--line2)}
.vz-lede{margin:10px 0 0;font-size:14px;line-height:1.6;color:var(--ink2);
max-width:56ch}
.vz-tile em{display:block;margin-top:3px;font-size:10px;font-style:normal;
color:var(--ink3)}
.vz-strip{margin-top:14px;padding:10px 12px;border-radius:10px;
background:var(--panel2);border:1px solid var(--line);font-size:11.5px;
color:var(--ink2);line-height:1.55}

.vz-legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin:10px 0 12px}
.vz-key{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;
color:var(--ink3)}
.vz-mark{font-style:normal;font-size:11px;line-height:1;color:var(--vz)}
.vz-key .vz-mark{width:14px;text-align:center}

.vz-bar{display:inline-block;height:6px;width:120px;background:var(--panel3);
border-radius:4px;overflow:hidden;vertical-align:middle}
.vz-fill{display:block;height:100%;width:var(--w);border-radius:4px;
transform-origin:left;animation:grow .5s cubic-bezier(.2,.7,.3,1) both}

.vz-grid{margin:2px 0 4px;overflow:visible}
.vz-row{display:grid;grid-template-columns:minmax(0,330px) repeat(var(--cols),64px);
gap:4px;align-items:center;margin-bottom:3px;justify-content:start}
.vz-head{margin-bottom:6px;align-items:end}
.vz-col{font-size:9.5px;color:var(--ink3);text-align:center;line-height:1.25;
word-break:normal;overflow-wrap:anywhere}
.vz-rowlab{font-size:11px;color:var(--ink2);white-space:nowrap;overflow:hidden;
text-overflow:ellipsis;padding-right:8px}
.vz-cell{position:relative;height:26px;border-radius:6px;display:flex;
align-items:center;justify-content:center;cursor:default;
background:color-mix(in srgb,var(--vz) 26%,transparent);
border:1px solid color-mix(in srgb,var(--vz) 55%,transparent);
animation:cellin .3s ease-out both;animation-delay:calc(var(--i,0) * 14ms)}
.vz-cell .vz-mark{font-size:12px;color:var(--vz)}
.vz-cell.vz-dead{background:repeating-linear-gradient(45deg,
var(--panel2) 0 4px,var(--panel) 4px 8px);border-style:dashed}
.vz-cell.vz-none{background:var(--panel);border:1px solid var(--line);
animation:none}
.vz-shaky:after{content:"";position:absolute;top:2px;right:2px;width:4px;
height:4px;border-radius:50%;background:var(--warn)}
.vz-cell:hover,.vz-cell:focus-visible{outline:none;
border-color:color-mix(in srgb,var(--vz) 95%,transparent);
transform:translateY(-1px)}

/* The run at a glance: one row per assistant, one cell per question.

   Deliberately three states and no glyph, unlike `.vz-cell` above. At this
   size a mark is unreadable, so the only distinctions drawn are the ones that
   survive being eight pixels wide: filled for named, plain for answered, an
   outline for a call that never came back. A cell here is a target you point
   at to read the question, not a value you read off the picture — the
   detailed grid underneath is where outcomes are told apart.

   `minmax(0,1fr)` in both directions, never a bare `1fr`. A plain `1fr` track
   will not shrink below its content's minimum, so fifty of them side by side
   run off the right-hand edge of whatever they are in. */
.vz-dense{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
gap:5px 14px;align-items:center;margin:4px 0 12px}
.vz-dn-lab{font-size:11px;color:var(--ink2);white-space:nowrap;
text-align:right;overflow:hidden;text-overflow:ellipsis;max-width:180px}
.vz-dn-num{font:500 11px/1 var(--mono);color:var(--ink3);white-space:nowrap}
.vz-dn-row{display:grid;grid-template-columns:repeat(var(--cols),minmax(0,1fr));
gap:2px;min-width:0}
/* Reset outright. Every state class here is prefixed for the same reason: a
   bare one picks up whatever else in this sheet happens to share its name,
   and it does not lose that argument, it wins half of it — keeping the colour
   it declared and inheriting the padding it did not. */
.vz-dn-cell{position:relative;aspect-ratio:1;border-radius:1px;
background:var(--line2);padding:0;margin:0;border:0;min-width:0;
align-self:center;cursor:default;outline:none;
transition:box-shadow .12s,background .12s}
.vz-dn-cell.vz-dn-named{background:var(--accent)}
.vz-dn-cell.vz-dn-failed{background:transparent;
box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--warn) 85%,transparent)}
.vz-dn-cell.vz-dn-none{background:transparent;
box-shadow:inset 0 0 0 1px var(--line)}
/* A ring, not a scale. Scaling the cell was the obvious feedback and it
   cannot work here: the tooltip is inside the cell, so it grew to two and a
   bit times its size along with it. */
.vz-dn-cell:hover,.vz-dn-cell:focus-visible{z-index:41;
box-shadow:0 0 0 2px var(--ink2);border-radius:2px}
.vz-dn-cell.vz-dn-failed:hover,.vz-dn-cell.vz-dn-failed:focus-visible{
box-shadow:0 0 0 2px var(--ink2),inset 0 0 0 1.5px var(--warn)}
.vz-dn-key{display:flex;gap:20px;flex-wrap:wrap;font-size:11px;
color:var(--ink3);margin:0 0 4px}
.vz-dn-key span{display:flex;align-items:center;gap:7px}
.vz-dn-key i{width:11px;height:11px;border-radius:2px;flex:none;
background:var(--line2)}
.vz-dn-key i.vz-dn-named{background:var(--accent)}
.vz-dn-key i.vz-dn-failed{background:transparent;
box-shadow:inset 0 0 0 1.5px var(--warn)}
/* A 270px tip hanging off a cell in the first column would sit outside the
   card. Pinned to the near edge at either end of the strip instead. */
.vz-tl .vz-tip{right:auto;left:-2px}
.vz-tr .vz-tip{right:-2px;left:auto}
.vz-dn-cell:hover .vz-tip,.vz-dn-cell:focus-visible .vz-tip{
opacity:1;visibility:visible}

.vz-tip{position:absolute;right:0;bottom:calc(100% + 7px);z-index:40;
width:270px;padding:9px 11px;border-radius:10px;background:var(--panel3);
border:1px solid var(--line3);color:var(--ink);font-size:11px;line-height:1.5;
text-align:left;opacity:0;visibility:hidden;pointer-events:none;
box-shadow:0 10px 26px rgba(0,0,0,.55);transition:opacity .12s}
.vz-tipq{color:var(--ink3)}
.vz-cell:hover .vz-tip,.vz-cell:focus-visible .vz-tip,
.vz-seg:hover .vz-tip,.vz-dot:hover .vz-tip,.vz-dot:focus-visible .vz-tip{
opacity:1;visibility:visible}

.vz-div{display:flex;align-items:stretch;height:30px;margin:4px 0 6px;
background:var(--panel2);border-radius:9px;overflow:hidden}
.vz-half{flex:1;display:flex;min-width:0}
.vz-half.vz-left{justify-content:flex-end}
.vz-axis{width:2px;background:var(--line3);flex:none}
.vz-seg{position:relative;width:var(--w);background:var(--vz);
animation:grow .5s cubic-bezier(.2,.7,.3,1) both;transform-origin:left}
.vz-left .vz-seg{transform-origin:right}
.vz-ends{display:flex;justify-content:space-between;font-size:10px;
color:var(--ink3);margin-bottom:4px}

.vz-league{margin:4px 0}
.vz-lrow{display:grid;grid-template-columns:150px minmax(0,1fr) 46px;gap:10px;
align-items:center;margin-bottom:5px;font-size:11.5px;color:var(--ink3)}
.vz-lname{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vz-ltrack{height:9px;background:var(--panel2);border-radius:5px;overflow:hidden}
.vz-lfill{display:block;height:100%;width:var(--w);border-radius:5px;
background:var(--line3);transform-origin:left;
animation:grow .5s cubic-bezier(.2,.7,.3,1) both;
animation-delay:calc(var(--i,0) * 45ms)}
.vz-lval{text-align:right;font-variant-numeric:tabular-nums}
.vz-me{color:var(--ink);font-weight:600}
.vz-me .vz-lfill{background:var(--accent)}
.vz-rest{opacity:.6;font-style:italic}

.vz-plot{position:relative;height:230px;margin:14px 0 4px;
background:var(--panel2);border:1px solid var(--line);border-radius:11px}
.vz-quad{position:absolute;inset:0;
background:linear-gradient(to right,transparent calc(50% - 1px),
var(--line2) calc(50% - 1px) calc(50% + 1px),transparent calc(50% + 1px)),
linear-gradient(to bottom,transparent calc(50% - 1px),
var(--line2) calc(50% - 1px) calc(50% + 1px),transparent calc(50% + 1px))}
.vz-qlab{position:absolute;font-size:9.5px;color:var(--ink3);
text-transform:uppercase;letter-spacing:.05em}
.vz-q1{top:9px;left:11px}.vz-q2{top:9px;right:11px}
.vz-q3{bottom:9px;left:11px}.vz-q4{bottom:9px;right:11px}
.vz-dot{position:absolute;width:11px;height:11px;margin:0 0 -6px -6px;
border-radius:50%;background:var(--accent);border:2px solid var(--panel2);
cursor:default;animation:cellin .4s ease-out both;
animation-delay:calc(var(--i,0) * 28ms)}
.vz-dot.vz-stuck{background:transparent;border:2px solid var(--ink3)}
.vz-dot:hover,.vz-dot:focus-visible{outline:none;z-index:30;
box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 28%,transparent)}
.vz-dot .vz-tip{right:auto;left:50%;margin-left:-135px}
.vz-dotkey{width:10px;height:10px;border-radius:50%;background:var(--accent);
display:inline-block}
.vz-dotkey.vz-stuck{background:transparent;border:2px solid var(--ink3)}
.vz-axes{display:flex;justify-content:space-between;font-size:10px;
color:var(--ink3)}

.vz-fold{margin-top:10px}
.vz-fold>summary{font-size:11.5px;color:var(--ink3)}
.vz-fold .card{background:none;border:0;padding:0;margin:14px 0 0}
.vz-fold .card h3{margin-top:0}

/* ── Step 4: the standing rail and the working area ───────────────────────
   Two rules hold this page together and both are structural, not decorative.

   Nothing here is a percentage without its denominator beside it — that much
   is the same rule as everywhere else in this app.

   The other is about empty space. A row is a grid whose panels stretch to the
   tallest of them; a panel ends in something pinned to its own floor; and a
   panel given more height than its content needs hands that height to its
   *rows*, equally. Pushing the rows apart with `space-between` instead would
   open one visible gap per gap — growing them reads as a generous row height.
   A column that ends short of the one beside it looks like a page that failed
   to load, which is the last thing a screen full of measurements should look
   like. */
/* Steps 4 and 5 are dashboards rather than documents, so they get more width
   than the rest — a 330px standing column beside a six-column map needs it.
   The reading measure is kept by the paragraphs themselves (`.den`, `.sub`,
   `.sec-plain` all cap their own width), not by squeezing the page: capping
   the page squeezes the tables and charts too, and those are the half of the
   screen a customer opens this tool for. */
.sheet.vis{max-width:1560px}
/* The standing column and the working column, side by side. `align-items:start`
   rather than stretch: the rail is a stack of finished cards and has no slack
   to give, while the working column runs to whatever the plan needs. */
.vis .r-rail{display:grid;grid-template-columns:330px minmax(0,1fr);gap:14px;
align-items:start;margin-bottom:14px}
.vis .workcol{display:flex;flex-direction:column;gap:14px;min-width:0}
/* The standing column follows the page down. Stretching it to match the
   working column instead would spend six hundred pixels on nothing under
   "what changed", and the point of a standing column is that the figure is
   there whenever you look up from the work — which is exactly when you have
   scrolled away from the top of it. */
.vis .rail-std{position:sticky;top:14px;max-height:calc(100vh - 28px);overflow-y:auto;
scrollbar-width:none}
/* The rail is a capped-height flex column, so its cards must be told not to
   shrink into it. Left to the default they compress to fit the viewport, and
   the first thing to collapse is the trend chart — an SVG sized from its own
   ratio has no content to stop it, so a fortnight of readings flattens into a
   30-pixel smear. The column scrolls; the cards keep their height. */
.vis .rail-std>*{flex:none}
.vis .rail-std::-webkit-scrollbar{display:none}
@media(max-width:1180px){.vis .rail-std{position:static;max-height:none}}
.vis .workcol>*{margin:0}
/* Under about 1180px the two columns stop fitting and the rail goes above the
   work rather than being crushed: a brand name truncated to "B…" is not a
   label, and a map with no room for its assistant columns is not a map. */
@media(max-width:1180px){.vis .r-rail{grid-template-columns:minmax(0,1fr)}}
.vis .card{display:flex;flex-direction:column}
.vis .card>.foot{margin-top:auto;padding-top:12px}
.vis .foot .den{margin-top:0}
.vis .spread{flex:1;display:flex;flex-direction:column}
.vis .spread>*{flex:1 0 auto;align-content:center}
.vis .card.fill{flex:1}
.den{font-size:11.5px;line-height:1.55;color:var(--ink3);margin:10px 0 0;
max-width:88ch}

/* the rail */
.rail-std{display:flex;flex-direction:column;gap:12px}
.rail-std .card{margin-bottom:0}
.rail-std h3{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
color:var(--ink3);font-weight:600;margin:0 0 12px}
.std-fig{display:flex;align-items:baseline;gap:11px}
.std-fig .figure{font-family:var(--serif);font-size:46px;line-height:1;
font-weight:600;letter-spacing:-.04em;font-variant-numeric:tabular-nums}
.std-den{font-size:11.5px;color:var(--ink3);line-height:1.35}
.std-chips{display:flex;flex-direction:column;gap:5px;align-items:flex-start;
margin-top:10px}
.mv{display:inline-flex;align-items:center;gap:5px;font:600 11.5px var(--sans);
padding:4px 10px;border-radius:999px;border:1px solid transparent;
font-variant-numeric:tabular-nums}
.mv.up{color:var(--good);background:color-mix(in srgb,var(--good) 13%,transparent);
border-color:color-mix(in srgb,var(--good) 32%,transparent)}
.mv.down{color:var(--bad);background:color-mix(in srgb,var(--bad) 13%,transparent);
border-color:color-mix(in srgb,var(--bad) 32%,transparent)}
.mv.flat{color:var(--ink3);background:var(--panel3);border-color:var(--line2)}
.std-rates{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.rt{background:var(--panel2);border:1px solid var(--line);border-radius:10px;
padding:11px 12px}
.rt .k{font-size:9.5px;letter-spacing:.07em;text-transform:uppercase;
color:var(--ink3);font-weight:600}
.rt .v{font:600 20px var(--mono);margin-top:4px;font-variant-numeric:tabular-nums}
.rt .c{margin-top:4px}
.rt .c .mv{padding:2px 7px;font-size:10px}
.rt .n{font-size:10px;color:var(--ink3);margin-top:4px}

/* the trend chart */
.tr{display:block;width:100%;height:auto;margin:14px 0 0}
.tr-grid{stroke:var(--line);stroke-width:1}
.tr-zero{stroke:var(--line2);stroke-width:1}
.tr-yl,.tr-xl{fill:var(--ink3);font:9px var(--mono)}
.tr-xl{font-family:var(--sans)}
.tr-xl.now{fill:var(--ink2);font-weight:600}
.tr-cap{fill:var(--ink3);font:8.5px var(--sans);letter-spacing:.05em}
.tr-line{fill:none;stroke:var(--good3);stroke-width:2;stroke-linejoin:round;
stroke-linecap:round}
.tr-fill{fill:color-mix(in srgb,var(--good3) 16%,transparent)}
.tr-dot{fill:var(--panel);stroke:var(--good3);stroke-width:1.5}
.tr-dot.now{fill:var(--good3);stroke:var(--panel);stroke-width:2}
.tr-dot.mock{stroke-dasharray:2 2}
.tr-bar.up{fill:color-mix(in srgb,var(--good3) 70%,transparent)}
.tr-bar.down{fill:color-mix(in srgb,var(--bad2) 70%,transparent)}

/* the field, and what changed */
.fldset,.chset,.grset{display:flex;flex-direction:column}
.fld{display:grid;grid-template-columns:1fr auto;gap:3px 9px;padding:7px 0;
border-top:1px solid var(--line)}
.fldset>.fld:first-child{border-top:0}
.fld .fl{font-size:12px;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;
white-space:nowrap}
.fld .fl em{font-style:normal;color:var(--ink3);font-size:10.5px}
.fld .fv{font:600 11.5px var(--mono);color:var(--ink);
font-variant-numeric:tabular-nums}
.fld .ft{grid-column:1/-1;height:6px;background:var(--panel3);border-radius:4px;
overflow:hidden}
.fld .ft i{display:block;height:100%;width:var(--w);background:var(--line3);
border-radius:4px}
.fld.you .ft i{background:var(--accent-ink)}
.fld.you .fl{color:var(--ink)}
.ch{display:grid;grid-template-columns:1fr;gap:4px;padding:11px 0;
border-top:1px solid var(--line)}
.chset>.ch:first-child{border-top:0}
.ch .cm{font:600 11.5px var(--mono);font-variant-numeric:tabular-nums}
.ch .cm.u{color:var(--good)}.ch .cm.d{color:var(--bad)}.ch .cm.f{color:var(--ink3)}
.ch .ct{font-size:12px;line-height:1.5;color:var(--ink2)}
.ch .ct b{color:var(--ink)}
.ch .cw{font-size:10px;color:var(--ink3)}

/* the verdict on today's change */
.vis .verdict{display:flex;gap:13px;align-items:flex-start;padding:13px 16px;
border-radius:12px;background:var(--panel2);border:1px solid var(--line2);
margin-bottom:12px}
.vis .verdict .vi{font:600 10px var(--sans);letter-spacing:.1em;
text-transform:uppercase;padding:5px 10px;border-radius:5px;white-space:nowrap}
.vis .verdict .vi.ok{background:color-mix(in srgb,var(--good) 16%,transparent);
color:var(--good)}
.vis .verdict .vi.no{background:var(--panel3);color:var(--ink3)}
.vis .verdict p{margin:0;font-size:12.5px;line-height:1.55;color:var(--ink2)}
.vis .verdict p b{color:var(--ink)}

/* the map */
.mapwrap{overflow-x:auto}
.tmap{width:100%;border-collapse:separate;border-spacing:3px;
font-variant-numeric:tabular-nums}
.tmap th{font:600 10.5px var(--sans);color:var(--ink3);text-align:center;
padding:0 0 6px;letter-spacing:.05em;font-weight:600}
.tmap th.mr{text-align:left;font-size:11.5px;color:var(--ink2)}
/* Observed or modelled, under the assistant's name, and only drawn where
   one table holds both kinds at once. See view.topic_map. */
.tmap th em{display:block;font-style:normal;font-weight:500;font-size:9px;
letter-spacing:.02em;color:var(--ink3);opacity:.8;margin-top:2px}
.tmap td.mr{font-size:12.5px;color:var(--ink2);padding-right:10px;
white-space:nowrap}
.tmap td.mr em{display:block;font-style:normal;font-size:10.5px;color:var(--ink3)}
.mc{border-radius:7px;padding:9px 6px;text-align:center;border:1px solid transparent;
min-width:62px}
.mc .mv-n{font:600 14px var(--mono);display:block;line-height:1.15}
.mc .mv-d{font-size:9.5px;color:var(--ink3);display:block;margin-top:2px}
.mc.b4{background:color-mix(in srgb,var(--good3) 30%,transparent);
color:var(--mc-good)}
.mc.b3{background:color-mix(in srgb,var(--good3) 17%,transparent);
color:var(--mc-good)}
.mc.b2{background:color-mix(in srgb,var(--mid) 20%,transparent);color:var(--ink2)}
.mc.b1{background:color-mix(in srgb,var(--bad2) 22%,transparent);
color:var(--mc-bad)}
.mc.na{color:var(--ink3);background-image:repeating-linear-gradient(45deg,
transparent 0 4px,color-mix(in srgb,var(--line3) 50%,transparent) 4px 6px)}
.mc.all{border-color:var(--line2)}
/* A change, at the size a table cell and a question row have room for. The
   arrow carries the direction as well as the colour, because red against green
   is the one pair a colour-blind reader cannot separate — the same rule the
   marks in chart.py follow. */
.dm{display:block;font:600 10px var(--mono);margin-top:3px;
font-variant-numeric:tabular-nums}
.gr .dm{display:inline-block;margin:0 0 0 7px}
.dm.up{color:var(--good)}
.dm.down{color:var(--bad)}
.dm.flat{color:var(--ink3)}
/* each topic's own history, banded to itself */
.tmap th.trh{padding-left:8px;color:var(--ink3);font-weight:600}
.tmap td.trc{padding-left:8px;vertical-align:middle;width:104px}
.sp{display:block}
.sp-line{fill:none;stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.sp.up .sp-line,.sp.up .sp-now{stroke:var(--good3)}
.sp.down .sp-line,.sp.down .sp-now{stroke:var(--bad2)}
.sp.flat .sp-line,.sp.flat .sp-now{stroke:var(--line3)}
.sp.up .sp-fill{fill:color-mix(in srgb,var(--good3) 18%,transparent)}
.sp.down .sp-fill{fill:color-mix(in srgb,var(--bad2) 18%,transparent)}
.sp.flat .sp-fill{fill:none}
.sp-now{fill:var(--panel);stroke-width:2}

/* ground held and ground lost */
.vis .split{display:grid;grid-template-columns:1fr 1fr;gap:0 22px;flex:1}
.vis .split>div{display:flex;flex-direction:column;min-width:0}
.vis .split .lose{border-left:1px solid var(--line);padding-left:22px}
.vis .split h4{font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;
font-weight:600;margin:0 0 3px}
.vis .split .win h4{color:var(--good)}
.vis .split .lose h4{color:var(--bad)}
.vis .split .sub{font-size:11.5px;color:var(--ink3);margin:0 0 10px}
.gr{display:grid;grid-template-columns:1fr auto;gap:10px;padding:10px 0;
border-top:1px solid var(--line);align-items:center}
.grset>.gr:first-child{border-top:0}
.gr .gt{font-size:12.5px;color:var(--ink);line-height:1.4}
.gr .gt em{display:block;font-style:normal;font-size:11px;color:var(--ink3);
margin-top:2px}
.gr .gt em b{color:var(--ink2);font-weight:600}
.gr .gv{font:600 15px var(--mono);font-variant-numeric:tabular-nums}

/* Stage 5's board. Every factor is drawn to the same shape — two facing
   voices, the distance between them, then what to do about it — because the
   comparison is only readable if the reader stops having to work out what kind
   of thing each panel holds. */
.eg-board{padding:0;overflow:hidden}
.eg-intro{display:flex;align-items:flex-start;justify-content:space-between;
gap:20px;padding:20px 21px;border-bottom:1px solid var(--line)}
.eg-intro h3{font-size:21px}
.eg-intro p{margin:5px 0 0;color:var(--ink2);font-size:12.5px;max-width:60ch}
.eg-stats{display:grid;grid-template-columns:repeat(2,minmax(120px,1fr));gap:6px;
flex:0 1 390px}
.eg-stat{background:var(--panel2);border:1px solid var(--line);border-radius:9px;
padding:9px 10px;min-width:0}
.eg-stat b{display:block;font:600 18px/1 var(--mono);color:var(--ink)}
.eg-stat span{display:block;margin-top:5px;color:var(--ink3);font-size:9.5px;
line-height:1.3}
.eg-stat.warn{border-top:2px solid var(--warn)}
.eg-stat.bad{border-top:2px solid var(--bad)}
.eg-stat.ok{border-top:2px solid var(--good)}
.eg-factor{padding:18px 21px 20px;border-top:1px solid var(--line)}
.eg-factor:first-of-type{border-top:0}
.eg-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;
margin-bottom:12px}
.eg-head>div{display:flex;align-items:center;gap:8px;min-width:0;flex-wrap:wrap}
.eg-head h4{font:600 19px/1.2 var(--serif);margin:0;color:var(--ink);letter-spacing:-.025em}
.eg-rank{display:grid;place-items:center;width:22px;height:22px;border-radius:6px;
background:var(--accent);color:var(--on-accent);font:600 11px/1 var(--mono)}
.eg-importance{color:var(--ink3);font-size:10.5px}
.eg-type{flex:none;display:inline-flex;align-items:center;padding:5px 10px;
border-radius:999px;border:1px solid var(--line2);font-size:10.5px;font-weight:600;
line-height:1.2;color:var(--ink2);background:var(--panel2)}
.eg-type.warn{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 50%,var(--line))}
.eg-type.bad{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 50%,var(--line))}
.eg-type.ok{color:var(--good);border-color:color-mix(in srgb,var(--good) 50%,var(--line))}
/* One continuous evidence route. The seven labels are the product promise,
   repeated on every factor so the reader never has to remember which screen
   held the passage, question, answer, source, action or result. */
.jx-chain{list-style:none;margin:0 0 13px;padding:0;display:grid;
grid-template-columns:repeat(7,minmax(0,1fr));border:1px solid var(--line);
border-radius:10px;overflow:hidden;background:var(--panel2)}
.jx-chain li{position:relative;min-width:0;padding:11px 11px 12px;
border-left:1px solid var(--line)}
.jx-chain li:first-child{border-left:0}
.jx-chain li:not(:last-child):after{content:'›';position:absolute;right:-6px;
top:12px;z-index:1;width:12px;height:12px;border-radius:50%;background:var(--panel2);
color:var(--ink3);font:600 12px/12px var(--sans);text-align:center}
.jx-chain .jc-n{display:grid;place-items:center;width:18px;height:18px;
border-radius:5px;background:var(--panel3);color:var(--ink3);
font:600 9px/1 var(--mono);margin-bottom:7px}
.jx-chain h5{margin:0 0 6px;font-size:8.5px;line-height:1.25;letter-spacing:.07em;
text-transform:uppercase;color:var(--ink3)}
.jx-chain p{margin:0;font-size:10.5px;line-height:1.45;color:var(--ink2);
overflow-wrap:anywhere}
.jx-chain p>b{color:var(--ink);font-weight:600}
.jx-chain small{display:block;margin-top:5px;color:var(--ink3);font-size:9px;
line-height:1.4}
.jx-chain .watch{margin:0;border:0;background:transparent;padding:0;display:block}
.jx-chain .watch .wk{display:none}
.jx-chain .watch span{font-size:10px;line-height:1.45}
.jx-chain .watch form{display:block!important;margin:0 0 5px}
.jx-chain .watch .go{font-size:9px;padding:4px 7px}
.jx-paths{margin:-2px 0 13px;border:1px solid var(--line);border-radius:9px;
padding:0 12px;background:var(--panel2)}
.jx-paths>summary{padding:10px 0;font-size:10.5px}
.jx-paths table{margin-bottom:12px}
.jx-paths td{vertical-align:top;font-size:11px;line-height:1.45}
.jx-paths td small{display:block;margin-top:4px;color:var(--ink3);font-size:9.5px}
/* Two, not four. This was the board's four-voice row; the board no longer
   uses it, and its one remaining caller — the library page's hard-to-use /
   usable pair — was laying two cells into four tracks and leaving half the
   row empty. */
.eg-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));border:1px solid
var(--line);border-radius:11px;overflow:hidden;background:var(--panel2)}
.eg-grid section{min-width:0;padding:12px 13px;border-left:1px solid var(--line)}
.eg-grid section:first-child{border-left:0}
.eg-grid h5{display:block;margin:0 0 8px;font-size:9.5px;
font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--ink3)}
.eg-grid p{margin:0;overflow-wrap:anywhere}
.eg-quote{font-size:11.5px;line-height:1.5;color:var(--ink2)}
.eg-meta{font-size:10px;line-height:1.4;color:var(--ink3);margin-top:7px!important}
.eg-empty{display:flex;gap:7px;font-size:11.5px;line-height:1.5;color:var(--ink3)}
.eg-empty span{color:var(--line3)}
/* ---------- the two voices ---------- */
/* Buyers and the assistants, facing each other, drawn to one shape so the
   distance between them is what the eye reads first. The colours are the
   product's own and they carry a meaning that is also written in words in
   every panel heading: warm for the people, the interface blue for the
   machines. Nothing here is encoded by colour alone. */
.pg-voices{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
border:1px solid var(--line);border-radius:10px;overflow:hidden}
/* A column, so the counts sit on the floor of the panel rather than wherever
   the quote above them happened to stop. The two voices rarely hold the same
   amount of evidence — an absent product has no sentence to quote — and
   without this the shorter panel ends halfway up its neighbour and the row
   reads as though half of it failed to load. */
.pg-voice{display:flex;flex-direction:column;padding:15px 17px;
background:var(--panel2);min-width:0}
.pg-voice .pg-tally{margin-top:auto}
.pg-voice+.pg-voice{border-left:1px solid var(--line)}
.pg-voice.buyer{border-top:2px solid var(--c4)}
.pg-voice.ai{border-top:2px solid var(--c1)}
.pg-voice h5{display:block;margin:0 0 9px;font-size:9.5px;letter-spacing:.09em;
text-transform:uppercase;font-weight:600}
.pg-voice.buyer h5{color:var(--c4)}
.pg-voice.ai h5{color:var(--c1)}

/* The searches themselves. A query is the one piece of evidence on this page
   somebody can write a page title from, so it is set at reading size and the
   volume beside it is the quiet half. */
.pg-asks{list-style:none;margin:0 0 13px;padding:0;display:flex;
flex-direction:column;gap:7px}
.pg-asks li{display:flex;justify-content:space-between;gap:12px;
align-items:baseline;font-size:12px;line-height:1.45;color:var(--ink)}
.pg-asks li i{flex:none;font-style:normal;font-size:10.5px;color:var(--ink3);
font-variant-numeric:tabular-nums}

.pg-said{margin:0;padding:11px 13px;border-radius:8px;background:var(--panel3);
font-size:11.5px;line-height:1.55;color:var(--ink2);overflow-wrap:anywhere}
.pg-said cite{display:block;margin-top:8px;font-style:normal;font-size:10px;
color:var(--ink3)}
.pg-said.pos{border-left:2px solid var(--good)}
.pg-said.neg{border-left:2px solid var(--bad)}

/* Counts under each voice, in the same shape on both sides — the comparison is
   only honest if the two columns are read the same way. */
.pg-tally{display:flex;gap:16px;flex-wrap:wrap;margin:12px 0 0;padding-top:11px;
border-top:1px solid var(--line)}
/* The quote is what grows, so the two panels reach the same floor whichever
   of them has more to say. */
.pg-said,.pg-voice>.eg-empty{margin-bottom:2px}
.pg-tally div{min-width:0}
.pg-tally b{display:block;font:600 14px/1.2 var(--mono);color:var(--ink);
font-variant-numeric:tabular-nums}
.pg-tally b.miss{color:var(--bad)}
.pg-tally b.win{color:var(--good)}
.pg-tally span{display:block;margin-top:3px;font-size:9.5px;letter-spacing:.05em;
text-transform:uppercase;color:var(--ink3)}

/* The sentence the two panels add up to. */
.pg-distance{display:flex;align-items:flex-start;gap:12px;margin-top:12px;
padding:14px 16px;border:1px solid var(--line2);border-radius:10px;
background:var(--panel)}
.pg-distance>span{flex:none;width:66px;padding-top:2px;font-size:9.5px;
letter-spacing:.09em;text-transform:uppercase;color:var(--ink3);font-weight:600}
.pg-distance p{margin:0;font-size:13px;line-height:1.55;color:var(--ink)}
.pg-distance p+p{margin-top:7px;font-size:12px;color:var(--ink3)}

/* What to do, addressed one line at a time. */
.pg-script{margin-top:12px;border:1px solid var(--line);border-radius:10px;
background:var(--panel2);overflow:hidden}
.pg-row{display:grid;grid-template-columns:104px minmax(0,1fr);gap:16px;
padding:12px 16px;border-bottom:1px solid var(--line)}
.pg-row:last-child{border-bottom:0}
.pg-row>span{padding-top:3px;font-size:9.5px;letter-spacing:.09em;
text-transform:uppercase;color:var(--ink3);font-weight:600}
.pg-row p{margin:0;font-size:12.5px;line-height:1.55;color:var(--ink2);
overflow-wrap:anywhere}
.pg-row p+p{margin-top:6px;color:var(--ink3);font-size:11.5px}
.pg-row p b{color:var(--ink);font-weight:600}
.pg-script .watch{margin:0;border:0;border-top:1px solid var(--line);
border-radius:0}

@media (max-width:900px){
.pg-voices{grid-template-columns:minmax(0,1fr)}
.pg-voice+.pg-voice{border-left:0;border-top:1px solid var(--line)}
.pg-voice.ai{border-top:2px solid var(--c1)}
.pg-row{grid-template-columns:minmax(0,1fr);gap:5px}
}

.eg-detail{margin:14px 0 0;border:1px solid var(--line);border-radius:var(--r);
background:var(--panel);padding:0 21px}
.eg-detail>summary{padding:15px 0;font-weight:600;color:var(--ink2)}

/* the plan, as openable steps */
.steps{display:flex;flex-direction:column;flex:1}
.step{border-top:1px solid var(--line)}
.steps>.step:first-child{border-top:0}
.step>summary{list-style:none;cursor:pointer;display:block;padding:13px 0}
.step>summary::-webkit-details-marker{display:none}
.step>summary::marker{content:""}
/* The app draws its own disclosure triangle on every `details summary`. A step
   lays its summary out as a row, so the shared triangle is moved into that row
   as a column of its own rather than left to land in front of it. */
.step>summary:before{content:"";display:none}
.shead:after{content:"▸";color:var(--ink3);font-size:11px;line-height:1}
.step[open]>summary .shead:after{content:"▾";color:var(--accent-ink)}
/* The grid goes on a span inside the summary, never on the summary itself: a
   summary carries an implicit marker box that joins the grid and pushes every
   real cell one column along. */
.shead{display:grid;grid-template-columns:24px minmax(0,1fr) auto 12px;gap:13px;
align-items:center}
.step>summary:focus-visible{outline:2px solid var(--accent-ink);
outline-offset:2px;border-radius:6px}
.step .rk{font:600 11.5px var(--mono);color:var(--on-accent);
background:var(--accent-ink);width:22px;height:22px;border-radius:6px;
display:grid;place-items:center}
.step .ttl{font-size:13.5px;font-weight:600;color:var(--ink);line-height:1.35;
display:block}
.step .sub{display:block;font-size:11.5px;color:var(--ink3);margin-top:3px}
.step .eff{font-size:10.5px;color:var(--ink3);text-align:right;
line-height:1.35;max-width:22ch}
.step>summary:hover .ttl{color:var(--accent-ink)}
.sbody{padding:2px 0 16px 37px;display:flex;flex-direction:column;gap:14px}
.sbody h5{margin:0 0 5px;font-size:10px;letter-spacing:.09em;
text-transform:uppercase;color:var(--ink3);font-weight:600}
.sbody p{margin:0;font-size:12.5px;line-height:1.6;color:var(--ink2)}
.scols{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.quote{margin:9px 0 0;padding:10px 13px;border-left:2px solid var(--line3);
background:var(--panel2);border-radius:0 8px 8px 0;font-size:12.5px;
line-height:1.55;color:var(--ink2);font-style:italic}
.quote cite{display:block;margin-top:6px;font-style:normal;font-size:10.5px;
color:var(--ink3)}
.watch{display:flex;gap:11px;align-items:flex-start;padding:11px 13px;
border-radius:9px;background:color-mix(in srgb,var(--accent) 10%,transparent);
border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);
font-size:12px;line-height:1.55;color:var(--ink2)}
.watch b{color:var(--ink)}
.watch .wk{font:600 9.5px var(--sans);letter-spacing:.09em;text-transform:uppercase;
color:var(--accent-ink);white-space:nowrap;padding-top:2px}

/* the evidence, folded */
.ev{border-top:1px solid var(--line)}
.ev>summary{cursor:pointer;padding:12px 0;font-size:12.5px;color:var(--ink2)}
.ev>summary:hover{color:var(--ink)}
.ev>summary:focus-visible{outline:2px solid var(--accent-ink);outline-offset:2px;
border-radius:6px}
.evbody{padding-bottom:8px}
.evbody .card{background:none;border:0;padding:0;margin:0 0 14px}

@media (max-width:1100px){
.scols{grid-template-columns:1fr}
.eg-intro{display:block}.eg-stats{margin-top:14px;max-width:none}
.eg-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
.eg-grid section:nth-child(3){border-left:0;border-top:1px solid var(--line)}
.eg-grid section:nth-child(4){border-top:1px solid var(--line)}
.vis .split{grid-template-columns:1fr}
.vis .split .lose{border-left:0;padding-left:0;padding-top:16px;
border-top:1px solid var(--line);margin-top:16px}}

@keyframes grow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes cellin{from{opacity:0;transform:scale(.82)}
to{opacity:1;transform:scale(1)}}

/* Mobile navigation stays available behind a native disclosure. It is closed
   initially so the first measurement and action arrive before the full menu.
   Desktop keeps the existing column. Both use the same generated links. */
.rail-desktop{display:contents}.rail-mobile{display:none}
@media (max-width:900px){
.rail-desktop{display:none}.rail-mobile{display:block;width:100%}
.rail-mobile>summary{cursor:pointer;padding:8px 4px;font-weight:600}
.rail-mobile>summary:focus-visible{outline:2px solid var(--accent-ink)}
.rail-links{display:flex;flex-wrap:wrap;gap:5px;padding-top:8px}
.cols,.cols.solo{grid-template-columns:minmax(0,1fr);
grid-template-rows:auto minmax(0,1fr)}
.top form{flex-basis:280px}
/* It wraps rather than scrolls sideways. A strip that scrolls puts the end of
   the run off the edge of the glass, and the end of the run is step five —
   which is where the work is. Something a customer has to discover by dragging
   is something a customer does not find. */
.rail{border-right:0;border-bottom:1px solid var(--line);
padding:9px 12px;display:flex;align-items:stretch;flex-wrap:wrap;
gap:5px;overflow:visible;max-height:none}
/* The phase headings are the one thing that cannot survive the turn: sideways
   they read as items rather than as headings for what follows them. */
.rail-h{display:none}
.rail .r,.rail .rs{flex:none;white-space:nowrap}
.rail .r span.t,.rail .rs span.t{flex:none;overflow:visible}
/* The hairline stub joining one step's number to the next, and the state dot
   trailing each row, are both vertical-column furniture. */
.rail-step:before,.rail .dot{display:none}
.rail-home{margin:0 4px 0 0;padding:6px 12px 6px 10px;border-bottom:0;
border-right:1px solid var(--line2);border-radius:9px 0 0 9px}
/* "Overview / the whole run" stacks two lines in a strip one line tall. */
.rail-home small{display:none}
/* The five stages stay grouped, hung off a hairline the way they are hung off
   step five's number in the column, so turning the map sideways never loses
   which step they are inside. */
.rail-sub{display:flex;align-items:stretch;flex-wrap:wrap;gap:5px;
margin:0;padding:0 0 0 9px;border-left:1px solid var(--line2);border-top:0}
.rail-note{display:none}}

@media (max-width:620px){
.guide-do{align-items:stretch;flex-direction:column}
.guide-do span{min-width:0}
.guide-do .btn,.guide-do form{width:100%}
.guide-do .btn{min-height:44px;white-space:normal;text-align:center;justify-content:center}
.sheet{overflow-wrap:anywhere}
.eg-stats{grid-template-columns:1fr}.eg-head{display:block}.eg-type{margin-top:9px}
.jx-chain{grid-template-columns:1fr}
.jx-chain li{border-left:0;border-top:1px solid var(--line);display:grid;
grid-template-columns:24px minmax(0,1fr);gap:8px}
.jx-chain li:first-child{border-top:0}
.jx-chain li:not(:last-child):after{content:'↓';left:8px;right:auto;top:auto;
bottom:-6px}
.jx-chain .jc-n{margin:0}
.eg-grid{grid-template-columns:1fr}
.eg-grid section{border-left:0;border-top:1px solid var(--line)}
.eg-grid section:first-child{border-top:0}}

/* Fields take the palette like everything else. They were left to the browser
   before, which paints a white box — tolerable on the old charcoal and wrong on
   both of these grounds. The focus ring is deliberately loud: this is the form
   somebody signs in with, and a field you cannot tell is focused is a field
   people type their password into twice. */
input[type=text],input[type=email],input[type=password],input[type=search],
input[type=number],textarea,select{width:100%;font:inherit;font-size:14px;
color:var(--ink);background:var(--panel2);border:1px solid var(--line2);
border-radius:8px;padding:11px 13px}
input::placeholder,textarea::placeholder{color:var(--ink3)}
input[type=text]:focus,input[type=email]:focus,input[type=password]:focus,
input[type=search]:focus,input[type=number]:focus,textarea:focus,select:focus{
outline:none;border-color:var(--accent);
box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}

/* Meridian sets labels in sentence case. The uppercase-and-tracked treatment
   above came from the previous design, where the small type was a typewriter
   face and the tracking was what made it legible; in a text face the same
   treatment reads as shouting, and as terminal output rather than as a
   product. The sizes and weights are left alone — only the shouting goes. */
.rail-h,.next-k,.node-step,.pill,.r .rc,.rs .rc,.stand-h b,.stand-h>span,h2,
.tile span,th,.sev,.phase-state,.guide-step{text-transform:none;
letter-spacing:.01em}
/* At sentence case these labels no longer need to be tiny to stay quiet, and
   10px was always at the edge of readable. */
.rail-h,.stand-h b,h2,.tile span,th,.sev,.phase-state{font-size:11.5px}


/* ---------- the front door ----------
   Everything below draws the public side of the site: the header a stranger
   meets, and the landing page under it. It is written against the same tokens
   as the rest of this stylesheet, so the signed-out half and the signed-in
   half are one design rather than two that happen to share a logo. That is
   the whole point of it living here instead of in its own file.

   Every class is prefixed `lp-` where a plain name was already taken by the
   app's own chrome — `.cols`, `.col` and `.den` all mean something else a few
   hundred lines up, and a landing page quietly restyling the report would be
   exactly the kind of failure the rest of this file is arranged to prevent. */

/* The public header. The signed-in bar is about one product; this one is about
   the site, so it is a different bar rather than the same bar with the product
   parts blanked out. Same height and same ground, so crossing the sign-in
   boundary does not feel like arriving at a different company. */
.pub{display:flex;align-items:center;gap:22px;padding:0 26px;min-height:58px;
background:var(--panel);border-bottom:1px solid var(--line);
position:relative;z-index:3;flex-wrap:wrap}
.pub .mast{font-family:var(--serif);font-weight:600;font-size:20px;line-height:1;color:var(--ink);
letter-spacing:-.025em;flex:none}
.pub .mast:hover{text-decoration:none;color:var(--accent-ink)}
.pub .mast i{font-style:normal;color:var(--accent-ink)}
.pub nav{display:flex;gap:20px;align-items:center;font-size:13.5px}
.pub nav a{color:var(--ink2)}
.pub nav a:hover{color:var(--ink);text-decoration:none}
.pub nav a.on{color:var(--ink);font-weight:600}
.pub .sp{margin-left:auto;display:flex;gap:16px;align-items:center;
font-size:13.5px}
.pub .sp a{color:var(--ink2)}
.pub .sp a:hover{color:var(--ink);text-decoration:none}
/* `.pub .sp a` is two classes and an element and was quietly beating `.btn`,
   which is one class — so the header's only real call to action rendered as a
   green pill with invisible writing on it. Stated here rather than by making
   `.btn` louder everywhere, because the collision is local to this bar. */
.pub .sp a.btn,.pub .sp a.btn:hover{color:var(--on-accent)}
@media (max-width:760px){.pub nav{display:none}}

/* The signed-in account cluster: which plan, and the way out. It is added to
   every private page on the way out of the handler rather than written into
   each page, for the same reason the CSRF field is — a control that has to be
   remembered at a dozen call sites is one that will be missing from one of
   them, and here that one is the page somebody cannot sign out of. */
.acct{margin-left:auto;display:flex;gap:12px;align-items:center;flex:none;
font-size:12.5px;padding-left:14px}
.acct a{color:var(--ink3)}
.acct a:hover{color:var(--ink);text-decoration:none}
.acct .plan{color:var(--ink2);background:var(--panel2);
border:1px solid var(--line2);border-radius:999px;padding:5px 11px;
font-weight:600;letter-spacing:-.01em}
.acct .plan:hover{border-color:var(--accent);color:var(--accent-ink)}
/* The URL box already claims the middle of the bar and pushes everything to
   its own margin, so on those pages the cluster stops claiming it too. */
.top form+.acct{margin-left:0}

/* ---------- landing ---------- */
/* The public pages set their own width, so the canvas stops setting one for
   them. The wash and the drifting light stay — they are the ground the whole
   product sits on, and the front door is the first place anybody sees it. */
/* The front page fills the window rather than sitting in a 1180px column
   with a third of a wide screen empty on either side. The gutter is the only
   fixed thing about it: everything inside is fluid, and the type scales with
   the viewport (the `vw` terms below) so a headline on a 27-inch monitor is
   the same *proportion* of the screen it is on a laptop, not a laptop
   headline stranded in the middle of one.

   Declared on the canvas rather than on `.lp` because the public footer is a
   sibling of `.lp`, not a child, and the two have to line up. Every other
   public page keeps the 26px it had — hence the fallback. */
.canvas.wide-canvas{padding:0 0 40px;--lp-gut:clamp(24px,4.4vw,108px)}
.lp{max-width:none;margin:0;padding:0 var(--lp-gut,26px) 90px}
.lp-sec{border-bottom:1px solid var(--line);padding:clamp(52px,4.4vw,86px) 0}
.lp-sec:last-child{border-bottom:0}
/* Deliberately not scoped to `.lp`. Inside the app an `h2` is a small section
   label — 11.5px, quiet, one of many on a dense screen — and that rule was
   landing on the public pages too, where an `h2` is the *page's* heading. "Start
   a free trial" was rendering at the size of a column header, which is why the
   sign-up page read as an unfinished fragment rather than as a front door. */
h2.lp-h{font-family:var(--serif);font-size:clamp(26px,1.9vw,36px);
font-weight:600;
letter-spacing:-.032em;margin:0 0 8px;color:var(--ink);text-transform:none}
/* Wider page, same line length. A measure is capped in `ch` rather than
   pixels, so it grows with the type and never with the window: prose set
   across 1700px is prose nobody finishes. */
.lp-lede{font-size:clamp(15.5px,1.05vw,19px);line-height:1.6;color:var(--ink2);
max-width:62ch;margin:0 0 30px}

/* The hero. The claim on the left, the reading that backs it on the right.
   The two halves swapped sides: a stranger reads the left half first, and what
   they need first is what this is, not a figure about a brand they have never
   heard of. The figure is still in the fold, one glance away, doing what a
   sentence claiming accuracy cannot.

   It is taller and quieter than it was — one line of eyebrow, two words of
   headline, two short lines under it. Everything that used to sit here in
   prose is further down the page, where somebody who wants it is looking. */
.lp-hero{display:grid;grid-template-columns:1.02fr .98fr;
gap:clamp(48px,5vw,110px);
align-items:center;padding:clamp(64px,6vw,110px) 0 clamp(58px,5.4vw,98px);
border-bottom:1px solid var(--line)}
@media (max-width:900px){.lp-hero{grid-template-columns:1fr;gap:38px;
padding:44px 0 40px}}
/* The line above the headline. A live dot, because the thing being measured
   is happening right now, and small caps because it is a label and not a
   sentence. */
.lp-eyebrow{display:flex;align-items:center;gap:9px;margin:0 0 26px;
font-size:clamp(11px,.78vw,13.5px);font-weight:600;letter-spacing:.13em;
text-transform:uppercase;color:var(--ink2)}
.lp-eyebrow i{width:.62em;height:.62em;border-radius:50%;
background:var(--accent);flex:none}
/* Two short lines under the headline and nothing else. `text-wrap:pretty` is
   deliberate rather than `balance`: the break is written into the markup. */
.lp-lead{font-size:clamp(17px,1.2vw,22px);line-height:1.55;color:var(--ink2);
margin:0 0 clamp(32px,2.6vw,46px);max-width:40ch;letter-spacing:-.011em}
/* The second route: watch it rather than run it. Quiet by design — the filled
   button is the thing to press. */
.lp-play{display:inline-flex;align-items:center;gap:8px;
font-size:clamp(14px,.95vw,17px);color:var(--ink2)}
.lp-play span{color:var(--accent-ink);font-size:12px}
.lp-play:hover{color:var(--ink);text-decoration:none}
.lp-date{font-family:var(--serif);font-weight:400;font-style:italic;font-size:15.5px;
color:var(--ink3);margin:0 0 22px;letter-spacing:-.02em}
.lp-fig{display:flex;align-items:flex-start;gap:4px;font-family:var(--serif);font-weight:600;
line-height:.84;color:var(--ink);letter-spacing:-.02em}
.lp-fig strong{font-size:clamp(84px,11vw,132px);font-weight:600;
letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.lp-fig sup{font-size:clamp(26px,3.6vw,42px);position:relative;top:.18em;
color:var(--ink2)}
.lp-den{margin:24px 0 0;font-family:var(--serif);font-weight:400;font-size:18px;line-height:1.55;
max-width:38ch;color:var(--ink2);letter-spacing:-.011em}
.lp-den b{color:var(--ink);font-weight:600;
box-shadow:inset 0 -.34em 0 var(--panel3)}
/* The confidence interval, drawn. A percentage printed without one is the
   false precision this product exists to refuse, so it is in the hero. */
.lp-band{margin-top:28px;max-width:330px}
.lp-band .lab{display:flex;justify-content:space-between;font-size:12px;
color:var(--ink3);margin-bottom:9px}
.lp-track{height:22px;position:relative;border-left:1px solid var(--line2);
border-right:1px solid var(--line2)}
.lp-track:before{content:"";position:absolute;top:50%;left:0;right:0;height:1px;
background:var(--line2)}
.lp-span{position:absolute;top:4px;height:14px;background:var(--panel3);
border-left:2px solid var(--accent);border-right:2px solid var(--accent)}
/* Last month's reading, and what the range says about the gap. It sits under
   the band rather than in the sentence above it because the verdict only
   means anything once you have seen how wide the range is — printed above,
   it would be an assertion, and printed here it is a reading of the picture
   directly overhead. */
.lp-prev{margin:12px 0 0;font-size:12.5px;line-height:1.55;color:var(--ink3);
max-width:330px}
.lp-prev b{color:var(--ink2);font-weight:600}
/* The reading, in a panel of its own. It sat loose on the page when it was the
   first thing read; beside a headline that size it needs an edge, or it reads
   as a caption to the sentence next to it rather than as a screen from the
   product. Same panel, line and radius as every card in the app. */
.lp-read{background:var(--panel);border:1px solid var(--line);
border-radius:var(--r);padding:clamp(28px,2.4vw,46px)}
.lp-read .lp-fig strong{font-size:clamp(74px,7.4vw,142px)}
.lp-read .lp-fig sup{font-size:clamp(26px,2.6vw,50px)}
.lp-read .lp-den{margin-top:clamp(20px,1.6vw,30px);
font-size:clamp(16px,1.15vw,21px);max-width:34ch}
.lp-read .lp-date{font-size:clamp(15.5px,1.05vw,19px)}
/* The band is a drawing, not a column of text, so it grows with the panel
   instead of staying at the 330px it needed when the panel was half of a
   1180px page. */
.lp-read .lp-band,.lp-read .lp-prev{max-width:min(100%,520px)}
.lp-read .lp-band .lab,.lp-read .lp-prev{font-size:clamp(12.5px,.85vw,15px)}
/* The headline, at the size the one statement on the page deserves. Four
   words, set as big as the column allows, with the word the whole product
   turns on carried in the blue. */
/* `.lp` is in the selector for a reason that is not tidiness. The layout
   sheet carries `[data-layout=focus] h1{font-size:23px}` — right for a working
   screen, and it is linked after this one, so at equal specificity it wins.
   That rule was quietly setting the front page's headline to the size of a
   panel title. The extra class outweighs it without `!important`. */
.lp .lp-copy h1{font-family:var(--serif);font-size:clamp(46px,6vw,118px);
line-height:.98;font-weight:600;letter-spacing:-.042em;
margin:0 0 clamp(26px,2vw,38px);max-width:11ch;color:var(--ink)}
.lp .lp-copy h1 em{font-style:italic;font-weight:400;color:var(--accent-ink);
letter-spacing:-.03em}
.lp-copy p{font-size:15.5px;line-height:1.62;color:var(--ink2);max-width:44ch;
margin:0 0 26px}

/* The thing to do. It is a real form rather than a link to a form: the first
   press a stranger makes should run the audit, not load another page asking
   them to press again. */
.lp-ask{display:flex;gap:9px;align-items:center;max-width:440px;flex-wrap:wrap}
.lp-ask input{flex:1;min-width:190px}
.lp-note{font-size:12.5px;color:var(--ink3);margin:16px 0 0;max-width:46ch;
line-height:1.55}
.lp-acts{display:flex;gap:20px;align-items:center;flex-wrap:wrap;margin-top:20px}
.quiet{font-size:13.5px;color:var(--ink2);border-bottom:1px solid var(--line2);
padding-bottom:3px}
.quiet:hover{color:var(--ink);border-color:var(--accent);text-decoration:none}

/* Where we look, in one band under the hero. Not a section: no heading, no
   card, no lede — a line of type on the left and the names on the right, read
   in about a second. The names come from the tier's own provider list, so this
   band cannot advertise a surface we stopped measuring. */
.lp-surfaces{display:flex;align-items:center;justify-content:space-between;
gap:40px;flex-wrap:wrap;padding:30px 0;border-bottom:1px solid var(--line)}
.lp-surfaces p{margin:0;font-size:clamp(12.5px,.9vw,15.5px);line-height:1.5;
color:var(--ink3);flex:none}
.lp-surfaces p b{display:block;color:var(--ink2);font-weight:600}
.lp-marks{display:flex;align-items:center;gap:clamp(20px,3.4vw,46px);
flex-wrap:wrap;flex:1;justify-content:flex-end}
/* Set as wordmarks rather than as a list: one weight, one tone, no bullets.
   They are recognised, not read. */
.lp-marks span{display:inline-flex;align-items:center;gap:.5em;
font-size:clamp(14px,1.15vw,21px);color:var(--ink2);
letter-spacing:-.02em;white-space:nowrap}
/* One grey for the mark and the name, inherited rather than set, so the row
   reads as one line of type with symbols in it and not as six logos. */
.lp-marks svg{width:1.12em;height:1.12em;flex:none;fill:currentColor}
@media (max-width:780px){.lp-marks{justify-content:flex-start}}

/* The sample read-out. Three columns of the real thing, with real structure
   and invented figures — so it is labelled as invented, loudly and inside the
   panel rather than in a footnote underneath it. A product whose whole claim
   is "every number has a denominator" cannot show numbers that came from
   nowhere without saying so where they are read. */
/* The marker is worded exactly as loudly as it was, and sits in the same
   place — first thing read, inside the panel. What changed is that it stopped
   being a warning sign. A hazard-yellow box announces a fault; these figures
   are not a fault, they are a worked example, and the box was the loudest
   thing on a page whose loudest thing should be the reading. So it is set as
   a caption in the page's own editorial voice: the word in the serif the rest
   of the sample is set in, a hairline rule, then the sentence. */
.lp-example{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
margin:0 0 18px;font-size:12.5px;line-height:1.55;color:var(--ink3)}
.lp-example b{position:relative;flex:none;font-family:var(--serif);
font-style:italic;font-weight:600;font-size:14.5px;color:var(--ink2);
letter-spacing:0;padding-right:13px}
.lp-example b:after{content:"";position:absolute;right:0;top:.2em;bottom:.1em;
width:1px;background:var(--line2)}
.lp-example span{flex:1;min-width:220px;max-width:74ch}
.lp-cols{display:grid;grid-template-columns:repeat(3,1fr);
border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
background:var(--panel)}
@media (max-width:860px){.lp-cols{grid-template-columns:1fr}}
/* Flex columns, so a column with fewer rows than its neighbour still reaches
   the same floor and its footnote sits on that floor rather than leaving a
   hole beside the taller one. */
.lp-col{padding:24px 26px 26px;border-right:1px solid var(--line);
display:flex;flex-direction:column}
.lp-col:last-child{border-right:0}
@media (max-width:860px){.lp-col{border-right:0;
border-bottom:1px solid var(--line)}
.lp-col:last-child{border-bottom:0}}
.lp-col h4{font-size:13.5px;font-weight:600;color:var(--ink);margin:0 0 5px;
letter-spacing:-.01em}
.lp-col .sub{font-size:12px;color:var(--ink3);margin:0 0 16px}
/* Each row arrives on its own beat, and its bar springs to length rather
   than easing to it. The stagger is what makes four rows read as one
   instrument being taken and not four numbers appearing; the overshoot is
   what makes a bar look like it landed on a value rather than crept up to
   one. Both are decoration over markup that is already correct: the figure,
   the width and the order are in the HTML before anything moves. */
.lp-line{display:grid;grid-template-columns:1fr 58px;gap:12px;align-items:baseline;
padding:10px 0;border-bottom:1px solid var(--line);font-size:14px;
color:var(--ink2)}
.motion .lp-line.in{animation:linein .52s cubic-bezier(.2,.7,.3,1) backwards;
animation-delay:var(--d,0ms)}
@keyframes linein{from{opacity:0;transform:translateY(9px)}
to{opacity:1;transform:none}}
.lp-line:last-of-type{border-bottom:0}
.lp-line b{font-family:var(--serif);font-size:17px;font-weight:600;
text-align:right;color:var(--ink);font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.lp-line.me{color:var(--ink)}
.lp-line.me b{color:var(--accent-ink)}
.lp-line.out b{color:var(--bad)}
.lp-line .rule{grid-column:1/-1;height:2px;background:var(--line);margin-top:6px}
.lp-line .rule i{display:block;height:100%;background:var(--line3);
transform-origin:left}
.motion .lp-line:not(.in) .rule i{transform:scaleX(0)}
.motion .lp-line.in .rule i{animation:spring .82s cubic-bezier(.2,.7,.3,1) both;
animation-delay:calc(var(--d,0ms) + 170ms)}
/* Past the mark, back, and settle — the shape of something with weight
   arriving at a stop, rather than a bar being drawn. */
@keyframes spring{0%{transform:scaleX(0)}
58%{transform:scaleX(1.05)}
78%{transform:scaleX(.982)}
91%{transform:scaleX(1.006)}
100%{transform:scaleX(1)}}
.lp-line.me .rule i{background:var(--accent)}
/* Pinned to the column's own floor — see the flex column above. */
.lp-foot-note{margin:16px 0 0;padding-top:14px;border-top:1px solid var(--line);
font-size:12px;color:var(--ink3);line-height:1.5}
.lp-col .lp-foot-note{margin-top:auto}

/* The recommendation. One, named, with the evidence for it in the sentence. */
.lp-move{display:grid;grid-template-columns:auto 1fr auto;gap:24px;
align-items:center;padding:26px;margin-top:18px;background:var(--panel);
border:1px solid var(--line);border-radius:var(--r)}
@media (max-width:860px){.lp-move{grid-template-columns:1fr;gap:14px}}
.lp-move .tag{font-size:12px;font-weight:600;color:var(--bad);
border:1px solid var(--bad);padding:8px 12px;border-radius:2px;
white-space:nowrap;justify-self:start}
.lp-move p{margin:0;font-family:var(--serif);font-weight:400;font-size:18px;line-height:1.5;
color:var(--ink);letter-spacing:-.011em}
.lp-move p b{font-weight:400;box-shadow:inset 0 -.34em 0 var(--panel3)}

/* How it works: the five steps the product actually runs, in order. The same
   five the rail shows once somebody is inside, so the shape of the thing is
   the same before and after paying for it.

   They were five columns of a card, which gave each step a box the width of a
   phrase and a paragraph inside it that nobody finished. They are rows now,
   beside the statement rather than under it: a number, a name, and one line —
   the shape of a contents page, which is what this section is. */
/* Centred rather than top-aligned: the statement is three lines and the list
   beside it is five rows, so aligning their tops leaves the left column
   stopping halfway up an empty half of the page. Centred, the whitespace sits
   around the heading instead of under it. */
.lp-how{display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(32px,6vw,80px);
align-items:center}
@media (max-width:900px){.lp-how{grid-template-columns:1fr;gap:34px}}
/* The statement, at hero weight but a step down from it: this is the second
   thing on the page, and two headlines at the same size compete. */
h2.lp-big{font-family:var(--serif);font-size:clamp(32px,3.9vw,74px);
line-height:1.02;font-weight:600;letter-spacing:-.04em;margin:0;
color:var(--ink);text-transform:none}
h2.lp-big em{font-style:italic;font-weight:400;color:var(--ink2);
letter-spacing:-.028em}
.lp-steps{display:block;border-top:1px solid var(--line)}
.lp-step{display:grid;grid-template-columns:clamp(44px,3.4vw,66px) 1fr;
gap:14px;padding:clamp(20px,1.6vw,30px) 2px;
border-bottom:1px solid var(--line)}
.lp-step .n{font-size:clamp(12px,.85vw,15px);font-weight:600;
color:var(--accent-ink);
letter-spacing:.02em;font-variant-numeric:tabular-nums;padding-top:2px}
.lp-step h4{margin:0 0 5px;font-size:clamp(16px,1.2vw,23px);font-weight:600;
color:var(--ink);letter-spacing:-.018em}
.lp-step p{margin:0;font-size:clamp(13.5px,.98vw,18px);line-height:1.55;
color:var(--ink3)}
.lp-steps .lp-foot-note{border-top:0;padding-top:16px}

/* Plans, on the front page rather than only behind a link. What it costs is
   one of the two questions a stranger came with. */
.lp-plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
gap:16px}
.lp-plan{background:var(--panel);border:1px solid var(--line);
border-radius:var(--r);padding:22px 22px 24px;display:flex;
flex-direction:column}
.lp-plan h3{margin:0 0 4px;font-size:15px;font-weight:600;color:var(--ink)}
.lp-plan .price{font-family:var(--serif);font-size:32px;font-weight:600;
color:var(--ink);margin:6px 0 2px;letter-spacing:-.032em;
font-variant-numeric:tabular-nums}
.lp-plan .price span{font-family:var(--sans);font-size:13px;color:var(--ink3)}
.lp-plan .why{font-size:13px;color:var(--ink2);line-height:1.55;margin:8px 0 14px}
.lp-plan ul{margin:0;padding-left:17px;font-size:13px;color:var(--ink2);
line-height:1.75}
.lp-plan .foot{margin-top:auto;padding-top:16px;font-size:12.5px;
color:var(--ink3)}

/* ---------- plans, on the page that is only about plans ----------
   The same card as the front page's, with the two things a dedicated page
   adds: room, and a way to act on it. Written as a modifier rather than as a
   second card so the two screens cannot drift into two different-looking
   price lists. */
/* No hero. The prices are the first thing on the page, so what sits above
   them is a label and one line of context — enough to know what the numbers
   buy, short enough that the cheapest plan is still on the first screen. */
.pl-head{padding:clamp(30px,2.8vw,52px) 0 clamp(20px,1.8vw,30px)}
.pl-head .lp-eyebrow{margin-bottom:14px}
.pl-head .lp-lead{margin:0;max-width:56ch}
.pl-plans{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
gap:clamp(16px,1.6vw,26px)}
.pl-plan{padding:clamp(26px,2.2vw,40px)}
.pl-plan h3{font-size:clamp(15px,1.05vw,19px);color:var(--ink2)}
.pl-plan .price{font-size:clamp(32px,3vw,54px);margin:10px 0 4px}
.pl-plan .price span{font-size:clamp(13px,.85vw,15px)}
.pl-plan .why{font-size:clamp(13px,.95vw,16px);margin:10px 0 18px}
.pl-plan ul{font-size:clamp(13px,.95vw,16px);line-height:1.85}
/* The press sits on the card's own floor, so three cards of different lengths
   still offer their button on one line across the row. */
.pl-plan .btn{margin-top:auto;align-self:flex-start}
.pl-plan .foot+.btn{margin-top:18px}
/* The band of things that do not vary. Same band as the front page's
   assistants, so "what is always included" and "where we look" read as one
   kind of statement in two places. */
/* Under the plans, the five names are the width of the row of cards above
   them, so they are set as five even columns rather than pushed right and
   left to wrap — one item wrapping alone to a second line is what a band of
   five short statements does at almost every window width. */
.pl-band{display:block;padding:26px 0}
.pl-band>p{margin:0 0 16px}
.pl-band .lp-marks{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
gap:14px 18px}
.pl-band .lp-marks span{font-size:clamp(13px,.92vw,16.5px);color:var(--ink3);
white-space:normal;align-items:flex-start}
@media (max-width:980px){.pl-band .lp-marks{
grid-template-columns:repeat(2,minmax(0,1fr))}}
/* The ticks are the same grey as the words beside them. A row of blue ticks
   is a row about the ticks. */
.pl-band .lp-marks svg{width:1em;height:1em;color:inherit}

/* What changes with the plan, as one row of bars per measure.

   It was a table, and a table of four rows across three plans is read as
   twelve unrelated numbers: nothing about "1 / 3 / 10" in three cells shows
   that the top plan is ten times the bottom one. Bars show it at a glance,
   and the figure stays printed on every bar, so the comparison is drawn and
   the fact is still written.

   Each measure is scaled on its own — they are different units and a shared
   axis would be meaningless — which is why every row carries its own labels
   rather than the block carrying one axis. Where a real ceiling exists (six
   assistants is the whole market) the bar is drawn against that instead of
   against the best plan, so four of six reads as the fraction it is. */
.pl-graph{border:1px solid var(--line);border-radius:var(--r);
background:var(--panel);overflow:hidden}
.pl-measure{display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.6fr);
gap:clamp(18px,3vw,56px);padding:clamp(20px,1.9vw,30px) clamp(20px,2vw,32px);
border-bottom:1px solid var(--line)}
.pl-measure:last-child{border-bottom:0}
.pl-measure h3{margin:0 0 5px;font-size:clamp(14px,1.05vw,18px);font-weight:600;
color:var(--ink);letter-spacing:-.015em}
.pl-measure p{margin:0;font-size:clamp(12.5px,.9vw,15px);line-height:1.55;
color:var(--ink3);max-width:44ch}
/* One grid for the whole measure, with each bar a `display:contents` row in
   it, so the three tracks share a start and a finish. Three separate grids —
   which is what a row-per-element layout gives you — size their own columns,
   and "1" against "12 a month" then puts three bars of a comparison on three
   different scales. */
.pl-bars{display:grid;grid-template-columns:clamp(54px,5vw,82px) minmax(0,1fr)
auto;column-gap:14px;row-gap:11px;align-items:center;align-content:center;
font-size:clamp(12.5px,.9vw,15px);color:var(--ink3)}
.pl-bar{display:contents}
.pl-bar .track{height:8px;border-radius:4px;background:var(--panel3)}
.pl-bar .track i{display:block;height:100%;width:var(--w,0%);min-width:6px;
border-radius:4px;background:var(--line3);transform-origin:left}
/* The leading plan on this measure is the only one in accent. Identity is
   carried by the name beside the bar, never by the colour — with colour off
   the row still says which plan is which and what it holds. */
.pl-bar.top .who{color:var(--ink)}
.pl-bar.top .track i{background:var(--accent)}
.pl-bar b{font-family:var(--serif);font-size:clamp(14px,1.05vw,19px);
font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums;
letter-spacing:-.02em;white-space:nowrap;justify-self:end}
.pl-extra{margin:16px 0 0;font-size:12.5px;color:var(--ink3);line-height:1.55}
@media (max-width:820px){.pl-measure{grid-template-columns:1fr;gap:14px}}

/* The last word. A stranger who read this far is deciding, so the two routes
   out of the page are here rather than only in the header they have scrolled
   away from. */
.lp-end{text-align:center;padding:50px 0 10px}
.lp-end h2{font-family:var(--serif);font-size:clamp(30px,3.6vw,66px);
font-weight:600;margin:0 0 14px;line-height:1.05;
letter-spacing:-.04em;color:var(--ink);text-transform:none}
.lp-end h2 em{font-style:italic;font-weight:400;color:var(--accent-ink);
letter-spacing:-.03em}
.lp-end p{color:var(--ink2);font-size:clamp(15px,1.05vw,19px);
margin:0 auto 24px;max-width:52ch;line-height:1.6}

/* The public footer. It used to live in landing.py and be drawn on the front
   page alone, which meant the privacy notice was reachable from exactly one
   screen — and not from the sign-up form, where somebody is actually agreeing
   to it. It is chrome now, so it is styled with the rest of the chrome. */
.lp-footer{max-width:none;margin:0;padding:34px var(--lp-gut,26px) 40px;
border-top:1px solid var(--line);display:grid;
grid-template-columns:1.6fr 1fr 1fr;gap:32px;color:var(--ink3);font-size:12.5px}
.lp-footer p{margin:4px 0;line-height:1.55;max-width:42ch}
.lp-footer .lp-foot-id b{display:block;color:var(--ink);font-weight:600;
font-family:var(--serif);font-size:16px;margin-bottom:7px;letter-spacing:-.02em}
.lp-footer nav{display:flex;flex-direction:column;gap:9px;align-items:flex-start}
.lp-footer nav b{color:var(--ink2);font-weight:600;font-size:11px;
letter-spacing:.06em;text-transform:uppercase;margin-bottom:2px}
.lp-footer a{color:var(--ink2);transition:color .14s,padding-left .14s}
.lp-footer a:hover{color:var(--accent-ink);text-decoration:none;padding-left:3px}
@media (max-width:780px){.lp-footer{grid-template-columns:1fr;gap:22px}}


/* ---------- the front door, in motion ----------
   Three rules govern everything below, and they are the same three the charts
   inside the app follow.

   1. **The page is complete before anything moves.** Every figure, bar and
      sentence is in the markup already; motion decides when you notice it,
      never what it says. Switching all of it off costs a reader nothing.
   2. **Nothing is hidden that script might fail to reveal.** The `.motion`
      class is set in the head — so there is no flash of a fully drawn page
      collapsing into a hidden one — and it is set *only* where the browser
      has an IntersectionObserver to un-hide things with and the reader has
      not asked for less motion. On top of that the head script arms a timer
      that reveals everything outright after 1.2 seconds, so a foot script
      that never runs cannot leave the front page blank.
   3. **Nothing loops forever.** An animation that repeats until you leave is
      a thing to look away from, not at. The one attention-seeking flourish on
      the page — the ring around the recommendation — runs three times and
      stops. */
.motion [data-reveal]{opacity:0;transform:translateY(16px)}
.motion [data-reveal].in{opacity:1;transform:none;
transition:opacity .62s cubic-bezier(.2,.7,.3,1),
transform .62s cubic-bezier(.2,.7,.3,1);
transition-delay:calc(var(--i,0) * 70ms)}
/* A block that has not been reached yet does not spend its entrance. Every
   bar, sweep and figure inside it is held at its first frame until the reader
   arrives, which is the whole point of revealing on scroll: without this the
   bars in the sample read-out finish growing while the reader is still in the
   hero, and arrive already over. */
.motion [data-reveal]:not(.in) *{animation-play-state:paused}

/* The hero reading arrives as a reading: the figure lifts in, the per-cent
   sign lands after it, and the plausible range draws outward from the middle
   the way a measurement earns its width. */
.lp-fig{animation:figin .75s cubic-bezier(.18,.75,.25,1) backwards;
animation-delay:var(--d,0ms)}
.lp-fig sup{animation:cap .4s ease-out backwards;
animation-delay:calc(var(--d,0ms) + 420ms)}
@keyframes figin{from{opacity:0;transform:translateY(20px) scale(.94)}
to{opacity:1;transform:none}}
.lp-track:before{transform-origin:left;
animation:grow .7s cubic-bezier(.2,.7,.3,1) backwards;animation-delay:.35s}
.lp-span{transform-origin:center;
animation:band .85s cubic-bezier(.2,.7,.3,1) backwards;animation-delay:.6s}
.lp-band .lab{animation:fade .5s ease-out backwards;animation-delay:.85s}
@keyframes band{from{opacity:0;transform:scaleX(.02)}
to{opacity:1;transform:scaleX(1)}}

/* The sample read-out. The bars already grew; now a light passes along each
   one as it lands, which is what makes six of them read as one instrument
   rather than six unrelated rules. */
.lp-line .rule{overflow:hidden;border-radius:2px}
.lp-line .rule i{position:relative}
.lp-line .rule i:after{content:"";position:absolute;top:0;right:0;bottom:0;
left:0;background:linear-gradient(90deg,transparent,
rgba(255,255,255,.42),transparent);background-size:220% 100%;
animation:sheen 1.05s ease-out backwards;
animation-delay:calc(var(--d,0ms) + 830ms)}
.lp-col{transition:background .2s}
.lp-col:hover{background:var(--panel2)}

/* The single recommendation. It is the one thing on the page we want pressed,
   so it is the one thing allowed to ask twice — three pulses, then silence.
   The evidence inside the sentence underlines itself as the card arrives. */
.lp-move .tag{animation:ring 2.4s ease-out 3;animation-delay:.4s}
@keyframes ring{0%,60%,100%{box-shadow:0 0 0 0 rgba(0,0,0,0)}
12%{box-shadow:0 0 0 0 var(--bad)}40%{box-shadow:0 0 0 8px rgba(0,0,0,0)}}
.lp-move p b{box-shadow:none;
background-image:linear-gradient(var(--panel3),var(--panel3));
background-repeat:no-repeat;background-position:0 92%;background-size:0 .34em;
transition:background-size .8s cubic-bezier(.2,.7,.3,1) .3s}
.motion .lp-move.in p b,html:not(.motion) .lp-move p b{background-size:100% .34em}

/* The five steps. Each draws its own accent rule as it arrives, left to
   right, so the strip reads as a sequence rather than as five boxes. */
.lp-step{position:relative;transition:background .2s}
.lp-step:hover{background:var(--panel2)}
.lp-step:before{content:"";position:absolute;top:-1px;left:0;width:28px;
height:2px;
background:var(--accent);transform:scaleX(0);transform-origin:left;
transition:transform .55s cubic-bezier(.2,.7,.3,1);
transition-delay:calc(var(--i,0) * 130ms + 120ms)}
.lp-step.in:before,html:not(.motion) .lp-step:before{transform:scaleX(1)}
.lp-step .n{transition:transform .2s}
.lp-step:hover .n{transform:translateX(3px)}

/* The plan comparison. Each measure is its own `[data-reveal]` block, so a
   row of bars is held at its first frame until the reader is level with it
   and then grows — the same spring the sample read-out uses, on the same
   stagger, because they are the same gesture: several numbers being taken by
   one instrument rather than appearing independently. Once, then still. The
   lengths, the labels and the order are all in the markup before anything
   moves, so switching this off costs a reader nothing. */
.pl-bar>*{animation:linein .5s cubic-bezier(.2,.7,.3,1) backwards;
animation-delay:var(--d,0ms)}
.pl-bar .track i{animation:spring .8s cubic-bezier(.2,.7,.3,1) both;
animation-delay:calc(var(--d,0ms) + 130ms)}
.pl-bar .track{overflow:hidden}

/* Cards that can be pressed or compared lift slightly. Only those: a page
   where everything moves under the cursor tells you nothing about what is
   worth pressing. */
.lp-plan,.lp-audience>div,.lp-proof>div{
transition:transform .2s cubic-bezier(.2,.7,.3,1),border-color .2s}
.lp-plan:hover,.lp-audience>div:hover,.lp-proof>div:hover{
transform:translateY(-3px);border-color:var(--line3)}
.lp-compare tbody tr{transition:background .15s}
.lp-compare tbody tr:hover{background:var(--panel2)}

/* Before and after, with the arrow between them actually pointing. */
.lp-case-grid .arr{animation:nudge 2.2s ease-in-out infinite}
@keyframes nudge{0%,100%{transform:translateX(0)}50%{transform:translateX(7px)}}
@media(max-width:780px){.lp-case-grid .arr{animation:nudgedown 2.2s ease-in-out infinite}}
@keyframes nudgedown{0%,100%{transform:rotate(90deg) translateX(0)}
50%{transform:rotate(90deg) translateX(7px)}}

/* The questions. The default disclosure triangle is replaced by a mark that
   turns, because the triangle renders differently in every browser and this
   one row of the page should not look like a different site's. */
.lp-faq summary{list-style:none}
.lp-faq summary::-webkit-details-marker{display:none}
.lp-faq summary::marker{content:""}
.lp-faq details{transition:border-color .18s}
.lp-faq details[open],.lp-faq details:hover{border-color:var(--line3)}
.lp-faq summary{position:relative}
.lp-faq summary:after{content:"+";position:absolute;right:2px;top:15px;
font-size:19px;line-height:1;color:var(--ink3);
transition:transform .25s cubic-bezier(.2,.7,.3,1),color .25s}
.lp-faq details[open] summary:after{transform:rotate(45deg);
color:var(--accent-ink)}
.lp-faq details[open] p{animation:fade .32s ease-out}

/* Motion is decoration here: every figure is in the markup before anything
   moves, so switching it all off costs the reader nothing. This covers the
   pulse that predates the charts as well. */
@media (prefers-reduced-motion:reduce){
*,*:before,*:after{animation:none!important;transition:none!important}
.vz-fill,.vz-seg,.vz-lfill{transform:none}
.vz-cell,.vz-dot{opacity:1}}


/* A soft light that follows the pointer, on every screen in the product.

   It is decoration and it is meant to be — the one thing it is for is making
   an eleven-hundred-pixel-wide console feel like an instrument rather than a
   document, and making the cursor easy to find on a dark page at a glance.

   Three things keep it from becoming a problem. It never takes a click, so
   nothing underneath it can be blocked. It is drawn once, moved by transform
   only, and updated on a frame rather than on every pointer event, so it costs
   a compositor layer and no layout. And it is only ever created for a mouse:
   see CURSOR_JS, which refuses a touch screen and refuses reduced motion.

   It screens rather than multiplies, which is the only thing that reads as a
   glow on black. It is held low enough that no text under it shifts in
   contrast — a decoration that makes a figure harder to read would be paying
   for feel with legibility, which is the wrong way round for this product. */
.cur{position:fixed;top:0;left:0;width:280px;height:280px;
margin:-140px 0 0 -140px;border-radius:50%;pointer-events:none;
z-index:2147483000;opacity:0;transition:opacity .45s ease;
mix-blend-mode:screen;
background:radial-gradient(circle closest-side,
color-mix(in srgb,var(--accent) 16%,transparent) 0,
color-mix(in srgb,var(--accent) 6%,transparent) 42%,transparent 72%)}
.cur.on{opacity:1}
@media (prefers-reduced-motion:reduce){.cur{display:none}}
