/* THE CLIENT HUB — the studio's frame from the studio's hue (the sitting of
   Sep 2; prototypes/pay.html was the spec, transcribed here). The app's
   construction — one bordered box on --outside, a light L of chrome on
   --tint, the room on --panel, the paper white — fed the STUDIO's brand
   (BrandStyle::hubCss sets --brand and --highlight; every tint in
   tokens.css derives), minus our rail. The paper is paper.css's, verbatim.
   The client's column sits to the RIGHT of the paper and OUTSIDE the grid
   math, the way the rail sits outside it on the left: --side wide, cells
   made of the line, edge to edge. Chrome is namespaced .hub-* / .side so
   these rules and studio.css can share the styleguide without colliding.
   Reads tokens.css only — never a literal color in a component.
   THE CHROME PASS, client host (Sep 3): the bar and the footer are the
   app masthead's height — one token, --bar (72), read here; the bar is
   its cell PLUS its line, the footer its cell over its line — 74 and 74.
   The column's cells stay 48 (--fh). */

/* ── the frame ── */
html{background:var(--outside);scrollbar-gutter:stable}   /* scrollbar-gutter, same reason as the app's (Sep 3): the frame sat 7px further right on a page that didn't scroll than on one that did; the gutter is reserved either way so the frame's left never jumps between pages */
body.hub{margin:0;font:400 15px/1.5 var(--ui);color:var(--ink);background:var(--panel);min-height:100vh;display:grid;grid-template-rows:calc(var(--bar) + 2px) auto 1fr auto;   /* the bar · the flash slot (always present, empty = 0 tall) · the room · the foot — a flash used to be an unplanned fourth child that took the room's 1fr (seen in the Kickoff 18 shoot) */
  max-width:calc(var(--content) + 2*var(--content-edge) + var(--side) + 4px);margin:0 auto;border-left:var(--line);border-right:var(--line)}
body.hub,body.hub *{box-sizing:border-box}
body.hub a{color:inherit;text-decoration:none}
body.hub button{font:inherit;color:inherit;border-radius:0}
body.hub.bare{display:block;max-width:calc(var(--content) + 2*var(--content-edge));border:0;background:#fff}
body.hub.bare main.bare .paper{min-height:100vh}

/* ── the bar: three cells — the studio's (its logo, or its name), the crumb, the session. The frame is the letterhead. (The evening diff of Sep 2.) A 72 cell plus its line (the chrome pass, Sep 3 — the app masthead's rule): the cells centre their contents; the studio's cell is THE LOCKUP BOX (Sep 16, below); the crumb and the session cell keep the 24 inset. ── */
.hub-bar{display:grid;grid-template-columns:auto 1fr var(--side);align-items:center;height:calc(var(--bar) + 2px);position:sticky;top:0;z-index:5;background:var(--tint);border-bottom:var(--line)}
.hub-bar .studio{display:flex;align-items:center;height:100%;padding:0 24px;font:500 15px/1 var(--ui);color:var(--ink);white-space:nowrap}   /* THE LOCKUP BOX (Sep 16): the cell is mail's box — Email\Lockup's 48 × 240, which the template writes as --lockup-h / --lockup-w from the class's constants, one rule for the bar and the letter — plus 24 each side; its width follows the art, never a fixed 200 (the old 36 × 152 box put a 5:1 logo at ~31 tall in the 72 cell) */
.hub-bar .studio img{height:var(--lockup-h);width:auto;max-width:var(--lockup-w);object-fit:contain;display:block}   /* the art fits to 48 tall, capped at 240 wide, anchored left: width:auto is the art's own width at 48, so the box IS the art up to the cap; past it, contain keeps the shape and the art fills the width, centred on the height (left and centre coincide, as in mail) */
.hub-bar .studio span{display:block;max-width:var(--lockup-w);overflow:hidden;text-overflow:ellipsis}   /* no logo: the name plate in the same box — the name at the bar's 15, never wider than the box, an ellipsis past it (the letter shrinks it first; CSS can't measure) */
.crumb{display:flex;align-items:center;height:100%;gap:12px;padding-left:24px;border-left:var(--line);white-space:nowrap;min-width:0;font:500 12px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink2)}   /* the chrome inset, like any cell — it follows a cell now, not the paper's edge; it draws its LEFT (the border rule) */
.crumb i{font-style:normal;color:var(--field)}   /* the separators are the line's color: chrome, not words */
.crumb{overflow:hidden}.crumb a,.crumb span{min-width:0;overflow:hidden;text-overflow:ellipsis}.crumb .here{flex-shrink:0}   /* a long trail on a narrow bar: the earlier crumbs give way, where you are keeps its word (Client ▸ Payments overran the session cell at 600 — the Kickoff 18 shoot) */
.crumb .here{color:var(--ink)}
.crumb a:hover{color:var(--ink)}
.crumb .back{display:none}   /* THE WAY BACK (Kickoff 24, ruled Sep 10): one up — shown only on the narrow bar, below */
.crumb .back i{color:var(--acting);margin-right:2px}
.hub-bar.narrow .crumb>:not(.back){display:none}.hub-bar.narrow .crumb .back{display:block}   /* the styleguide's forced narrow state — the same rules as the breakpoint's */
.hub-bar .who{display:grid;grid-template-columns:minmax(0,1fr) max-content;align-items:center;height:100%;column-gap:16px;padding:0 24px;border-left:var(--line);font:500 12px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink2);white-space:nowrap;min-width:0}   /* exactly the column's width: the bar's rule and the column's rule are one line */
.hub-bar .who span{overflow:hidden;text-overflow:ellipsis;text-transform:none;letter-spacing:0;min-width:0}   /* A CELL NEVER WRAPS AND NEVER TRUNCATES ITS WORD (Sep 17), by construction: the cell is a grid — the email on minmax(0,1fr), the word on max-content — so the cell's min-content IS its word (LOG OUT and the 24s), and the bar's session column is floored on min-content below: the email gives way to an ellipsis, down to nothing; the word cannot */
.hub-bar .who form{margin:0}
.hub-bar .who button{font:inherit;letter-spacing:inherit;text-transform:inherit;color:var(--acting);background:none;border:0;padding:0;cursor:pointer}
.hub-bar .who button:hover{color:var(--ink)}
/* THE PROFILE'S BAR sits HERE, with the base, ahead of every breakpoint (Sep 17): written after the phone block at equal specificity, these
   rules beat it — on the profile the bar never took its two rows at 528 and LOG IN was cut to "LO" at 375. Base first, breakpoints after. */
body.hub.profile{grid-template-rows:calc(var(--bar) + 2px) auto 1fr auto}
body.profile .hub-bar{grid-template-columns:auto 1fr auto}   /* the third cell is the session cell, as wide as its word — not the column's width: there is no column */
.hub-bar .session{display:flex;align-items:center;height:100%;padding:0 24px}   /* the session cell's place, NO rule to its left (ruled Sep 7). Not .door — client.css's .door is the sign-in page and margins its .go */
.hub-bar .session .go{width:auto;height:var(--fh);margin:0}   /* the hub's .go — the acting button everywhere on the hub; the bar's 48 in its 72 */
.hub-bar .session .go:hover{background:var(--ink)}

/* ── the room: the paper on the grid, the column beside it ── */
.room{display:grid;grid-template-columns:minmax(0,1fr) var(--side);align-items:stretch}
/* THE BORDER RULE (ruled Sep 2, evening, both axes; the vertical axis re-ruled Sep 3): a rule belongs to the thing AFTER it on the horizontal axis — a column draws its LEFT, the footer its TOP; a table's outer box draws the outside. On the vertical axis A RUN CLOSES WITH A RULE: the head draws the line under itself and every cell draws its own BOTTOM, so DOWNLOAD INVOICE ends on a line whichever cells a state shows (a :last-child can't see hidden siblings — no :last-child border rule anywhere here). Nothing is drawn twice, so nothing doubles or misaligns (the 4px line where DOWNLOAD INVOICE met the footer was the test). The paper draws no right edge: the column beside it draws its left. */
.room.one{grid-template-columns:minmax(0,1fr)}   /* the door, the stubs: one column (FLAGGED — undesigned) */
.door{padding:calc(6*var(--u)) var(--content-edge);max-width:calc(var(--side) + 2*var(--content-edge))}
.door h1{font:400 34px/1.1 var(--display);margin:0 0 12px}
.door p{margin:0 0 20px;color:var(--ink2)}
.door .go{margin-top:12px}
.door ul.plain{list-style:none;margin:0;padding:0}
.door ul.plain li{padding:12px 0;border-top:var(--line)}
.door ul.plain a{color:var(--acting);text-decoration:underline;text-underline-offset:3px}
.link-landing{padding:24px var(--content-edge);font-size:15px}
.link-landing a{color:var(--acting);text-decoration:underline}

/* ── the column: the studio's chrome on the client's side. --panel (the form ground), deeper than the frame's L, so the white fields are windows cut into it. ── */
.side{background:var(--panel);border-left:var(--line);padding-bottom:calc(8*var(--u))}   /* the column's rule is ITS left border — the same side the bar's session cell draws it, so the two are one line. The column ends on the same air as the paper (Sep 3): 8u below its last cell — the paper's own bottom padding — so both columns end 64 above the footer; before, a tall column's last line sat ON the footer's (measured 0). A short column already has the air (the room stretches it to the paper); the padding changes nothing there */
.status{padding:calc(8*var(--u)) 24px calc(8*var(--u))}   /* 8u below the bar, 8u before PAY BY — the column's one rhythm: eight units between every section (the after-line's 4u plus the next head's 4u make the other eight) */
.status .sum{font:500 34px/1.1 var(--mono);color:var(--ink)}   /* the money leads; numbers are MD IO by rule */
.status .when{font-size:15px;color:var(--ink);margin-top:10px}
.status .when b{font-weight:400;color:var(--ink2);margin-right:8px}   /* the label of the line, not a heading over the money */
.status .sub{font-size:13px;color:var(--ink2);margin-top:10px;line-height:1.6}
.status dl{margin:14px 0 0;display:grid;grid-template-columns:auto 1fr;column-gap:16px;row-gap:4px;font-size:13px}
.status dt{color:var(--ink2)}.status dd{margin:0;font-family:var(--mono);overflow-wrap:anywhere}
.status[data-state=paid] .when b{color:var(--acting)}

.side .head{padding:0 24px 10px;border-bottom:var(--line);font:500 11px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink3)}   /* a row header for a run of cells: it draws the line under itself; every cell below draws its own bottom — so the run is closed by a rule whichever cells a state shows */
.run+.run .head{padding-top:var(--break)}
.door .run .head{padding:var(--break) 24px 10px;border-bottom:var(--line);font:500 11px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink3)}   /* the hub root's card-on-file row (Kickoff 8): the column's row header, under the invoices. FLAGGED: undesigned */
.side .note{padding:0 24px var(--break);font-size:14px;line-height:1.6;color:var(--ink2)}
.side .note.after{padding-top:12px;font-size:13px}

/* ── the picker: a standard radio group — native radios in a list of rows — and ONE panel beneath it that shows what the chosen
   method means (the details, then the action). The panel never moves; only its contents change. Platform only: :has(), by the
   chosen row's POSITION (twelve static rules — no per-id CSS, no script). ── */
.pick .opt{display:flex;align-items:center;gap:12px;height:var(--fh);padding:0 24px;border-bottom:var(--line);font-size:15px;color:var(--ink);cursor:pointer}
/* the panel draws no top: the line over it is the last row's bottom (the run rule) */
.pick .opt:hover{background:var(--tint)}   /* hover swaps to the other ground — two tints, never a third */
.pick .opt:has(input:checked){background:#fff;font-weight:500}   /* the chosen thing is white, like Fixed | Per unit; medium is the one heavier weight the face has */
.pick .opt input{margin:0;width:16px;height:16px;accent-color:var(--acting);cursor:pointer}
.pick .opt:has(input:focus-visible){outline:2px solid var(--ink);outline-offset:-4px}
.pick .opt .pref{margin-left:auto;font:400 12px/1 var(--ui);color:var(--ink3)}
.how{display:none;padding:20px 24px 0;font-size:14px;line-height:1.6;color:var(--ink2)}
.how.open{display:block}   /* the styleguide's state */
.pick:has(.opt:nth-child(1) input:checked)~.how[data-n="1"],.pick:has(.opt:nth-child(2) input:checked)~.how[data-n="2"],
.pick:has(.opt:nth-child(3) input:checked)~.how[data-n="3"],.pick:has(.opt:nth-child(4) input:checked)~.how[data-n="4"],
.pick:has(.opt:nth-child(5) input:checked)~.how[data-n="5"],.pick:has(.opt:nth-child(6) input:checked)~.how[data-n="6"],
.pick:has(.opt:nth-child(7) input:checked)~.how[data-n="7"],.pick:has(.opt:nth-child(8) input:checked)~.how[data-n="8"],
.pick:has(.opt:nth-child(9) input:checked)~.how[data-n="9"],.pick:has(.opt:nth-child(10) input:checked)~.how[data-n="10"],
.pick:has(.opt:nth-child(11) input:checked)~.how[data-n="11"],.pick:has(.opt:nth-child(12) input:checked)~.how[data-n="12"]{display:block}
.pick+.head{padding-top:var(--break)}
.how b{color:var(--ink);font-weight:500}
.how p{margin:0 0 12px}
.how p:last-child{margin-bottom:0}
.how a{color:var(--acting);text-decoration:underline;text-underline-offset:3px}
.how form{margin:0}

/* ── label and value as a pair: the line-height is the only space between them, like the studio's name over its bank ── */
.kv{margin:20px 0 0}
.kv:first-child{margin-top:0}
.how .kv+form{margin-top:0}
.kv dt{font:500 13px/1.6 var(--ui);color:var(--ink)}
.kv dd{margin:0 0 16px;overflow-wrap:anywhere}
.kv dd:last-child{margin-bottom:0}
.kv+p{margin-top:16px}
.kv dd.c{display:flex;align-items:center;gap:12px;font-family:var(--mono);color:var(--ink);white-space:nowrap}
.kv dd.c button{margin-left:auto;padding:0;border:0;background:none;cursor:pointer;font:500 12px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--acting)}
.kv dd.c button:hover{color:var(--ink)}
.kv dd.c .show{margin-left:auto;font:500 12px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--acting);cursor:pointer;position:relative}
.kv dd.c .show:hover{color:var(--ink)}
.kv dd.c .show input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.kv dd.c .show+button{margin-left:0}
.kv dd.c .full,.kv dd.c:has(.show input:checked) .part,.kv dd.c:has(.show input:checked) .show .on{display:none}
.kv dd.c:has(.show input:checked) .full,.kv dd.c:has(.show input:checked) .show .off{display:inline}
.kv dd.c .show .off{display:none}

/* ── the Payment Element's place: Stripe's iframe renders its own labeled fields here, styled through the Appearance API into the
   app's field voice — label above, white field, THE line, --fh, no radius (pay.js reads the resolved tokens off this page). ── */
.pe{margin:0 0 calc(3*var(--u))}
.how p+.pe{margin-top:calc(3*var(--u))}
.pe:empty{display:none}
.pe .pe-noscript{margin:0;color:var(--ink2)}
.pe-error{margin:0 0 12px;color:var(--late);font-size:13px}

/* ── a KNOWN instrument as a field (pay.html: the saved bank account; slice 9b: the attached card too): label above, the line,
   the name in ink, CHANGE at the right in --acting — a form, since forgetting it is a POST. ── */
.pe-known{display:grid;grid-template-columns:1fr 1fr;gap:calc(3*var(--u)) var(--gutter)}
.pe-known .f{margin:0}
.pe-known .f.w{grid-column:1 / -1}
.pe-known .f>span{display:block;font:500 13px/1 var(--ui);color:var(--ink);margin-bottom:6px}
.pe-known .f>i{display:flex;align-items:center;justify-content:space-between;height:var(--fh);padding:0 12px;font:15px/1.3 var(--ui);font-style:normal;color:var(--ink);background:#fff;border:var(--line)}
.pe-known .f>i form{margin:0}
.pe-known .f>i button{padding:0;border:0;background:none;cursor:pointer;font:500 12px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--acting)}
.pe-known .f>i button:hover{color:var(--ink)}

/* ── the fee disclosure (pay.html): what this card adds, before the client confirms. Rows in the paper's totals voice. ── */
.fee{margin:0 0 calc(3*var(--u));display:grid;grid-template-columns:1fr auto;row-gap:6px;font-size:14px}
.fee .k{color:var(--ink2)}.fee .v{text-align:right;font-family:var(--mono);color:var(--ink)}
.fee .t{padding-top:8px;border-top:var(--line);font-weight:500}
.fee .t.v{font-size:16px}
.how .nofee{margin:0 0 calc(3*var(--u));color:var(--ink)}
.pick .opt .add{margin-left:auto;font-family:var(--mono);font-size:13px;color:var(--ink2)}   /* a fee knowable before the choice, on the row ("+ $5.00") */
.pick .opt .add+.pref{margin-left:12px}

/* ── CONFIRM YOUR BANK (Kickoff 44, pay.html ▸ Verify): the run's ONE panel — no picker above it, so it shows by construction;
   the code (or the two amounts) as a field in the panel's own voice, CONFIRM as the acting cell; the run ends with the break. ── */
.run.verify .how{display:block;padding-bottom:var(--break)}
.run.verify .two{display:grid;grid-template-columns:1fr 1fr;gap:0 var(--gutter)}
.run.verify .two .f{min-width:0}

/* ── the field voice inside the panel: Note (two rows at rest), the door's Email ── */
.hub .f{display:block;margin:20px 0 12px}
.hub .f>span{display:block;font:500 13px/1 var(--ui);color:var(--ink);margin-bottom:6px}
.hub .f>span em{font-style:normal;font-weight:400;color:var(--ink3);margin-left:4px}
.hub .f input{width:100%;height:var(--fh);padding:0 12px;font:15px/1.3 var(--ui);color:var(--ink);background:#fff;border:var(--line);border-radius:0}
.hub .f input:focus{outline:0;border-color:var(--ink);background:var(--highlight)}
.hub .f textarea{width:100%;min-height:calc(2 * 1.5 * 15px + 24px + 4px);padding:12px;font:15px/1.5 var(--ui);color:var(--ink);background:#fff;border:var(--line);border-radius:0;resize:none;display:block;field-sizing:content}   /* two rows at rest (the min-height is two lines + padding + the line); grows with what's typed where the platform supports it */
.hub .f textarea:focus{outline:0;border-color:var(--ink);background:var(--highlight)}
.hub .f.err>span{color:var(--late)}
.hub .f.err input{border-color:var(--late)}
.hub .f .msg{display:block;margin-top:6px;font-size:13px;color:var(--late)}
.hub .go{display:flex;align-items:center;width:100%;height:var(--fh);padding:0 24px;border:0;background:var(--acting);color:#fff;cursor:pointer;
  font:500 12px/1.3 var(--ui);letter-spacing:.08em;text-transform:uppercase;white-space:nowrap}
.hub .go:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.hub .go[disabled]{opacity:.6;cursor:progress}
.hub .go.inert,.hub .go.inert[disabled]{background:transparent;color:var(--ink3);border:var(--line);opacity:1;cursor:default}   /* STAFF MODE (ruled Sep 22): a client act the studio's own browser sees but cannot press — the off-field idiom (studio.css .f.off: the line, unfaded, the ground showing through), never pay.js's in-progress fade */
.hub .go.inert:hover{background:transparent;color:var(--ink3)}
.hub button[disabled]:not(.go){color:var(--ink3);cursor:default}

/* ── the column of a DOCUMENT page (the proposal sitting, Sep 3; prototypes/proposal.html): the client's derived status + the dated
   facts — a headline (display 28) · one quiet line · a dl of mono date · fact (the client's slice of Activity, not a stream). The
   DEPOSIT run, only when pay-before-signing is on: one acting cell — Pay $2,500 · sign later — a submit of the sign form (its
   package rides along) whose amount derives from the choice like the paper's total (the generated rules); the quiet line under.
   Executed: PAY $n DEPOSIT leads the run into the invoice's ordinary pay page. THE SIGN BLOCK SIGNS; THE COLUMN PAYS. ── */
.status .big{font:400 28px/1.15 var(--display);color:var(--ink)}
.status .big+.sub{margin-top:8px}
.status .facts{margin:calc(3*var(--u)) 0 0;display:grid;grid-template-columns:auto 1fr;column-gap:16px;row-gap:6px;font-size:13px;line-height:1.5}
.status .facts dt{font-family:var(--mono);color:var(--ink3);white-space:nowrap}
.status .facts dd{margin:0;color:var(--ink);font-family:var(--ui)}
.cell .n{font-family:var(--mono);letter-spacing:0;text-transform:none;color:var(--ink)}
.cell.go .n{color:#fff}
.cell .n span{display:none}   /* the deposit's amount: the generated rules show the chosen package's; "50%" until one is chosen */
main:has(input[name=package]):not(:has(input[name=package]:checked)) .side .cell.go[data-derived]{opacity:.35;pointer-events:none}   /* a derived deposit needs the choice first (ruled): shown by construction */
button.cell{font-family:var(--ui)}

/* ── a chrome cell: the document cells ── */
.cell{display:flex;align-items:center;width:100%;height:var(--fh);padding:0 24px;border:0;border-bottom:var(--line);background:transparent;text-align:left;cursor:pointer;
  font:500 12px/1.3 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink2);list-style:none;transition:background-color .15s ease,color .15s ease}
.cell:hover{background:var(--tint);color:var(--ink)}   /* chrome cells take the frame's L and ink on hover */
.cell:focus-visible{outline:2px solid var(--ink);outline-offset:-4px}
.cell.go{background:var(--acting);color:#fff}   /* the acting cell (proposal.html: APPROVE) — the one cell that is an action */
.cell.go:hover{background:var(--ink);color:#fff}   /* the acting cell's hover: ink, like the chrome's primary */
.cell.card{justify-content:space-between;cursor:default}   /* the card on file (Kickoff 8): a fact and its one action — the row is not a link; REMOVE is the Keep pattern, a POST cell. FLAGGED: undesigned */
.cell.card:hover{background:transparent;color:var(--ink2)}
.cell.card form{display:contents}
.cell.card button{font:inherit;letter-spacing:inherit;text-transform:inherit;background:none;border:0;padding:0;margin:0;color:var(--acting);cursor:pointer}
.cell.card button:hover{color:var(--ink)}

/* ── the flash, in the hub's voice (undesigned — FLAGGED) ── */
.hub .flash{padding:12px var(--content-edge);background:var(--highlight);color:var(--ink);font-size:14px}
.hub .flash--error{background:var(--late);color:#fff}   /* ONE RED (Sep 8) */

/* ── the foot: the bar's twin — the same height (--bar over its line: 74, the bar's 74), the same voice, the same last cell. The studio's facts as equal centred cells with rules between; we are the last, quietest thing. ── */
.hub-foot{display:grid;grid-template-columns:minmax(0,1fr) var(--side);min-height:var(--bar);border-top:var(--line);background:var(--tint);font:500 12px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink2)}
.hub-foot .cells{display:flex;min-width:0}
.hub-foot .cells span{flex:1 1 0;min-width:0;display:block;padding:0 16px;min-height:var(--bar);line-height:var(--bar);text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}   /* equal cells that ELLIPSIZE (a flex container never draws the ellipsis, so these are blocks with the row's line-height — the app footer's rule) */
.hub-foot .cells span+span,.hub-foot .cells .reach{border-left:var(--line)}   /* the cell after draws the rule between (the border rule) */
/* THE REACH IS ICONS, AND ONE CELL (ruled Sep 18): one 48-tall box, each icon a 48-wide link inside it, NO line between them — the lines are the foot's cells, and the socials are one fact. The network's mark masked over the link's own color — the studio's tone, ink on hover; the handle is the link's name and title, never text */
.hub-foot .reach{display:flex;align-items:center;flex:none}
.hub-foot .ico{display:grid;place-items:center;flex:0 0 var(--fh);width:var(--fh);height:var(--fh)}
.hub-foot .ico::before{content:"";width:20px;height:20px;background:currentColor;-webkit-mask:var(--ico) center/contain no-repeat;mask:var(--ico) center/contain no-repeat}
.hub-foot .ico.instagram{--ico:url(/images/instagram.svg)}
.hub-foot .ico.threads{--ico:url(/images/threads.svg)}
.hub-foot .ico.bluesky{--ico:url(/images/bluesky.svg)}
.hub-foot .ico.linkedin{--ico:url(/images/linkedin.svg)}
.hub-foot b{font-weight:500;color:var(--ink)}
.hub-foot b a:hover,.hub-foot .us a:hover{color:var(--ink)}
.hub-foot .us{display:flex;align-items:center;justify-content:center;gap:0 .5ch;min-height:var(--bar);border-left:var(--line);color:var(--ink3)}   /* exactly the column's width, centred; its rule is ITS left border, like the column's, so it's one line */
.hub-foot .us a{color:var(--acting)}

/* ── framed: on screen the paper sits inside the studio's frame, and the frame IS the letterhead — the logo slot is suppressed here and
   returns in print and in the studio's Preview, where the document stands alone. One document, one stylesheet per medium. ── */
.room .paper .p-logo{display:none}   /* every theme: the frame carries the mark */
.room .paper:is(.invoice.structure,.invoice.classic,.document) .p-from{border-left:0;padding-left:0}   /* the two themes with Structure's head, and the document that borrows it: no rule with nothing beside it */
.room .sheet{min-width:0}   /* the paper's column — and the paper's query container (paper.css: `:has(> .paper)`), so the paper answers to its own width, never the room's */
/* Minimalist here: the address alone on the head row — its studio block is the flex row with the logo hidden (paper.css carries the
   address in the head for every theme since Sep 16; the pay page's own Minimalist map of the morning is retired, it said the same). */

@media print{
  html,body.hub{background:#fff}   /* a printed page past the content is paper, never the frame's ground (12c) */
  .room .paper .p-logo{display:block}
  .room .paper:is(.invoice.structure,.invoice.classic,.document) .p-from{border-left:var(--pl);padding-left:16px}
}

/* ── the one breakpoint: the column goes above the paper (the client came here to pay) ── */
@media (max-width:855px){
  .hub-bar{grid-template-columns:auto minmax(max-content,1fr) minmax(min-content,auto)}   /* the crumb keeps its word; the session cell's email gives way (ellipsis) — the Kickoff 18 shoot at 600 clipped PAYMENTS; the cell's floor is its WORD (min-content — the .who grid above; Sep 17), never 0 */
  .crumb>:not(.back){display:none}.crumb .back{display:block}   /* stacked, the crumb is THE WAY BACK, one up — "‹ Invoices" on an invoice, "‹ Big Medium" on a list, NOTHING on the home or on a lapsed link (no parent to go up to) — Kickoff 24, ruled Sep 10 */
  .hub-foot{grid-template-columns:1fr}
  .hub-foot .cells{display:block}
  .hub-foot .cells span{display:flex;align-items:center;justify-content:center;line-height:inherit;white-space:normal}
  .hub-foot .cells span+span{border-left:0;border-top:var(--line);white-space:normal}
  .hub-foot .cells .reach{justify-content:center;border-left:0;border-top:var(--line)}   /* stacked, the icons stay ONE cell at 48, centred like the words */
  .hub-foot .us{border-left:0;border-top:var(--line)}
  .hub-foot .cells span,.hub-foot .us{min-height:var(--fh)}   /* stacked, the cells keep 48 */
  .room{grid-template-columns:minmax(0,1fr)}
  .side{order:-1;border-left:0;padding-bottom:0}   /* stacked, the column comes first; its last cell draws the line before the paper (the run rule — a bottom here would double it); no air below either — it ends on the paper, not the footer */
  .room .paper{min-height:0}
}
/* ── THE PHONE (Sep 16 — the app masthead's rule of Sep 15, studio.css, at the hub's OWN number): the bar folds at 528, the widest one-row sum — the lockup box 288 (240 + its 24s) + the way back 116 ("‹ Big Medium" + its 24) + LOG OUT 124 (the word, its 24s, the gap) = 528; a cell change moves the fold with it. Narrower, the row cannot hold the three (at 375 a wide logo pushed LOG OUT off the frame), so THE LOCKUP TAKES ITS OWN ROW, centred as the app's is, and the crumb and the session cell are the row below; the frame's first row follows the bar's height. The app's 480 is the app's. ── */
@media (max-width:528px){
  body.hub,body.hub.profile{grid-template-rows:auto auto 1fr auto}
  .hub-bar,body.profile .hub-bar{grid-template-columns:minmax(0,max-content) minmax(min-content,1fr);grid-template-rows:var(--bar) calc(var(--bar) + 2px);height:auto}   /* Sep 17: the session cell is floored on its word and takes the rest; the way back keeps its word while it fits beside that word and gives way (its ellipsis) only past it — nothing on this row can push a cell's word off the frame */
  .hub-bar .session{justify-content:flex-end}   /* LOG IN at the frame's edge, where LOG OUT is */
  /* the map, two rows: the lockup's (72), then the cells' — 72 plus the line they draw above themselves */
  .hub-bar .studio{grid-column:1 / -1;justify-content:center;contain:inline-size}   /* inline-size containment: the row is sized by the two cells under it, never by the lockup spanning them — a spanning item's width (up to 288) fed the crumb's max-content column and pushed LOG OUT off the frame (measured at 375) */
  .crumb,.hub-bar .who,.hub-bar .session{border-top:var(--line)}   /* the second row draws the line over itself (the run rule: the lockup's row closes with a rule) */
  .crumb{border-left:0}   /* first on its row: the frame's edge is its line */
}

/* ── the styleguide: the hub's components render in the studio shell; give them the ground they expect ── */
.styleguide-sample .side,.styleguide-sample .status,.styleguide-sample .how,.styleguide-sample .pick,.styleguide-sample .cell:not(.go),.styleguide-sample .kv{background:var(--panel)}   /* the acting cell keeps its fill */
.styleguide-sample .how.open{padding-bottom:20px}

/* ═══ THE PROFILE — `/` on a studio host, the public face of the hub (Kickoff 17, Sep 10; prototypes/profile.html, ruled Sep 6–7).
   Built on the hub's chrome: the bar, the foot, the tokens. Sections appear as the studio fills them; nothing is a mode.
   (The profile's frame rows and its bar — the session cell, LOG IN — sit with the bar's base rules above, AHEAD of the breakpoints: see there.) ═══ */

/* ── the hero: who · what, beside the cover. Two columns on the grid; the cover is the studio's, the pattern is its zero state. ── */
.hero{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);border-bottom:var(--line);background:var(--tint)}
.hero .who{padding:calc(10*var(--u)) var(--content-edge) calc(8*var(--u));display:flex;flex-direction:column;justify-content:center;min-width:0}
.hero h1{font:400 clamp(44px,6vw,72px)/1 var(--display);margin:0 0 16px;letter-spacing:-.01em;overflow-wrap:anywhere}
.hero .line{font:500 18px/1.4 var(--ui);color:var(--ink2);margin:0}
.hero .cover{border-left:var(--line);min-height:420px;background:var(--panel);position:relative;overflow:hidden}
.hero .cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* THE ZERO STATE: the legacy icon pattern (the old app's cover-pattern.svg — its `div.cover` masked the brand color through the same file), in the ACTING color on the panel ground. The SVG's shapes are 50% white, so the mask paints the pattern at half strength — the legacy look, by the same construction. */
.hero .cover.zero::before{content:"";position:absolute;inset:0;background:var(--acting);-webkit-mask:url(/images/cover-pattern.svg) center/cover no-repeat;mask:url(/images/cover-pattern.svg) center/cover no-repeat}

/* ── the card: the facts as a LIST in two columns — the app's list grammar (48 rows; caps label at a fixed inset, value beside it; every row draws
   its top, the second column its left — lines connect by construction, labels align by construction). What made the card tidy was sameness:
   every row the same shape (ruled Sep 7). `.fact`, not `.f` — .f is the form field, with its own margins; `.profile-card`, not `.card` — studio.css owns .card and the styleguide loads both sheets. ── */
.profile-card{border-bottom:var(--line);background:var(--tint)}
.profile-card .cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
.profile-card .fact{display:grid;grid-template-columns:104px 1fr;align-items:center;height:var(--fh);padding:0 24px;border-top:var(--line);min-width:0}
.profile-card .fact:nth-child(-n+2){border-top:0}
.profile-card .fact:nth-child(2n){border-left:var(--line)}
.profile-card .fact:last-child:nth-child(odd){grid-column:1/-1}   /* an odd count: the last fact runs full width (ruled Sep 7) */
.profile-card .fact:hover{background:var(--panel)}   /* the chrome's ease: a row you can rest on */
.profile-card .l{font:500 11px var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink3)}
.profile-card .v{font-size:15px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.profile-card .v a{color:var(--acting);text-decoration:underline;text-underline-offset:3px}
.profile-card .v.mono{font-family:var(--mono);font-size:14px}
@media (max-width:760px){.profile-card .cols{grid-template-columns:minmax(0,1fr)}.profile-card .fact:nth-child(-n+2){border-top:var(--line)}.profile-card .fact:first-child{border-top:0}.profile-card .fact:nth-child(2n){border-left:0}}

/* ── about: the paragraph in the UI face (Chris, Sep 10); the drop cap by the PLATFORM — `initial-letter: 3` sinks the first letter three lines, its
   top on the first line's cap height, its bottom on the third baseline, the browser doing the metrics (no hand-tuned size/line-height/margin) — in
   the display face and the LINE's color. Where unsupported, the letter simply stays in the run (graceful). Ascender alignment would want
   `initial-letter-align` (Safari only) — not worth the fragility (ruled Sep 7). ── */
.about{padding:calc(8*var(--u)) var(--content-edge);max-width:calc(var(--content) * .72 + 2*var(--content-edge));font:400 17px/1.6 var(--ui);color:var(--ink)}
.about p{margin:0 0 1em}
.about p:last-child{margin-bottom:0}
/* the drop cap is all-or-nothing: a browser without initial-letter (Firefox) gets the plain paragraph, never a small muted F — the whole rule sits behind the @supports; the -webkit- prefix is Safari's */
@supports (initial-letter:2) or (-webkit-initial-letter:2){
.about p:first-child::first-letter{initial-letter:3;-webkit-initial-letter:3;font-family:var(--display);color:var(--field);margin-right:14px}
}

/* ── work: images only, on the lines; NO heading — the work speaks (ruled Sep 7; the line above is the section's). one = a wide cover; many = a grid.
   The 2px between images IS the line: one color, one width — each figure's 1px edge, meeting. ── */
.work{border-top:var(--line)}
.work .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr))}
.work .grid figure{margin:0;background:var(--panel);aspect-ratio:4/3;overflow:hidden;border:1px solid var(--field)}   /* each figure draws a 1px edge: neighbours meet at 2px — the line — and a partial last row leaves the ground, not a slab of line color (the prototype's grid background bled across empty tracks; seen in the shoot with four images)*/
.work .grid figure img{width:100%;height:100%;object-fit:cover;display:block}
.work .grid figure.wide{grid-column:1/-1;aspect-ratio:21/9}
.hero+.work,.profile-card+.work{border-top:0}   /* the hero and the card draw their own bottom: nothing is drawn twice */

/* the foot is the bar's twin (client.css's .hub-foot); the mailto there is the reach — the studio's facts are links in the acting color */
.hub-foot .cells a{color:var(--acting)}
.hub-foot .cells a:hover{color:var(--ink)}

@media (max-width:900px){.hero{grid-template-columns:1fr}.hero .cover{border-left:0;border-top:var(--line);min-height:260px;order:-1}}

/* ── THE DOOR's two states (Kickoff 17, Sep 10 — prototypes/hub.html ▸ the doors): the domain hint as <code> on the line; the wrong-person sentence in ink ── */
.door code{font:14px/1 var(--mono);padding:3px 6px;border:var(--line);color:var(--ink)}
.door .whoami{margin:0 0 20px;color:var(--ink)}
.door .whoami b{font-weight:500}
.door p b{font-weight:500;color:var(--ink)}
/* THE WHICH-ONE PAGE: the client names as room cells — the home's .room-cell (below), on the line. Plain, FLAGGED: not drawn. */
.door .rooms{border-top:var(--line);margin-top:20px}

/* ═══ THE CLIENT'S HOME — `/{client}` (Kickoff 18, Sep 10; prototypes/hub.html, ruled Sep 10, its #proto layer promoted here). The pay
   page's two regions with the home's content: THE WIDE on the lighter ground, THE COLUMN on the deeper — tables in the wide, runs of
   rooms in the column. ═══ */
.wide{padding:calc(8*var(--u)) var(--content-edge);background:var(--tint);min-width:0}   /* the wide region: the lighter ground, the column the deeper — the pay page's pair */
.wide h1{margin:0;font:400 34px/1.1 var(--display);color:var(--ink)}
.wide .sub{margin-top:10px;color:var(--ink2)}
.wide .sub p{margin:0 0 12px}   /* the welcome note's paragraphs — the door's block's own markup and rule (Kickoff 46: one renderer, no pre-line) */
.wide .sub p:last-child{margin-bottom:0}
.hub-table{margin-top:var(--break);border:var(--line)}   /* a list is a table: the box draws the outer rule, each row its own top (hub.html's `.wide section`, named so the styleguide can show it alone) */
.hub-table .head{display:flex;align-items:center;justify-content:space-between;min-height:var(--fh);padding:0 24px;background:var(--panel);font:500 11px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink3)}
.hub-table .head a{color:var(--acting)}
.hub-table .head a::after{content:"\a0▸"}
.hub-table .head a:hover{color:var(--ink)}
/* a row: the thing · its room · the amount (right) · the status — minmax(0,…) so every row resolves the same tracks; NOT client.css's .cell (the pay picker's button) */
.hrow{display:grid;grid-template-columns:minmax(0,4fr) minmax(0,3fr) minmax(0,2fr) minmax(0,3fr);column-gap:calc(4*var(--u));align-items:center;min-height:var(--fh);padding:0 24px;border-top:var(--line);color:var(--ink)}
a.hrow:hover{background:var(--panel)}   /* a row is a link: hover swaps to the other ground — two tints, never a third */
.hrow .n{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hrow .tag{justify-self:start;display:inline-flex;align-items:center;height:24px;padding:0 8px;border:var(--line);font:500 11px/1 var(--ui);letter-spacing:.08em;text-transform:uppercase;color:var(--ink2);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}   /* the room as a small SQUARE cell of the line — the system has one shape */
.hrow .amt{justify-self:end;font-family:var(--mono);font-size:14px;color:var(--ink)}
.hrow .st{font-size:14px;color:var(--ink3);white-space:nowrap}   /* the status: quiet when it's history, ink when it's now */
.hrow.due .st{color:var(--ink);justify-self:start;background:var(--highlight);padding:3px 6px;margin-left:-6px;box-decoration-break:clone;-webkit-box-decoration-break:clone}   /* the "now" states as a mark in the studio's highlight — under the words only */
.hrow.late .st{color:var(--late)}   /* overdue: the one color that isn't the brand's */
.hub-table .empty{padding:16px 24px;border-top:var(--line);color:var(--ink3)}
/* the payment center's rows in a table box (Kickoff 18): the picker's head in the table's voice, the panel closed by its own air */
.hub-table .pick{border-top:0}
.hub-table .how{padding-bottom:20px}
.hub-table .how.open{display:block}
/* the column: the sum, then runs of rooms — client.css's .status and .side .head; the room cell (name over what's inside) */
.room-cell{display:block;padding:10px 24px;border-bottom:var(--line);color:var(--ink)}
a.room-cell:hover{background:var(--tint)}   /* a cell with a page is a link and hovers; a room with no page yet (the projects — RAISED) sits still */
.room-cell b{font-weight:500}
.room-cell span{display:block;font-size:13px;color:var(--ink3);margin-top:2px}
.side>.head:first-child{padding-top:calc(8*var(--u))}   /* a column that opens on a run (no sum to see — a by-project member): the same 8u the status would have given it */
.side .status+.head{border-top:0}
.side .room-cell+.head,.side .run+.head{padding-top:calc(8*var(--u))}   /* two breaks between runs — the column's one rhythm (the card on file's run counts as one) */
.side .status+.run .head{padding-top:0}
@media (max-width:855px){
  .room{grid-template-rows:auto 1fr}   /* stacked: the column's row is its content, the wide takes the frame's rest — auto rows had been sharing the free space, leaving air under the column's last cell (measured 17px in the shoot) */
  .hrow{grid-template-columns:1fr;row-gap:4px;padding:12px 24px}
  .hrow .amt{justify-self:start}
  .side{border-bottom:var(--line);padding-bottom:0}   /* the column comes first (the frame's rule above); it closes on the line before the wide */
}
/* THE DOOR'S WELCOME (hub.html ▸ the doors): the studio's note to this client, when one is set — the light ground, not white (white would
   read as a field). Promoted with the layer; nothing renders it yet: where the note lives is RAISED (Kickoff 18). */
.door .welcome{padding:24px;border:var(--line);background:var(--tint);margin-bottom:var(--break)}
.door .welcome p{color:var(--ink);margin:0 0 12px}
.door .welcome p:last-child{margin-bottom:0}
