/* =====================================================================
   P6R-T0.6 APP BASELINE. PicksYou app.css, the signed in workspace.

   design/app.css is copied byte for byte to public/css/app.css. There is
   no build step: what is written here is what a browser reads. The copy
   is made after every edit and the two files are compared by sha256 at
   every gate, the same discipline site.css and admin.css keep.

   REWRITTEN FROM SCRATCH FOR PHASE 6R (design/rules/phase6r-skin.md, the
   owner's order of 2026-09-05: "the same clean as Opinly has, just
   different colors and positioning of items, but fonts, sizes and etc
   same"). The design source is design/mockups/phase6r/app-overview.html,
   app-wizard-step2.html, app-account.html and app-billing.html, whose
   four inline sheets are one file built into each; every width, height,
   radius and gap below is lifted from it and translated onto the shipped
   class names. Where that file is silent, phase6r-skin.md decides.

   THE APP LOADS BOTH SHEETS, STILL (CSS ruling C1 of phase5o.md).
   components/app/layout.blade.php links site.css first and this file
   second. site.css is where the identity lives: the two vendored faces,
   the token block, base type, links, buttons, form fields and the shared
   card, table, tally and chip vocabulary. Loading it here means a view
   nobody touched this phase inherits the skin rather than losing its
   styling, which is the class contract law. What is in THIS file is what
   the workspace adds on top: the top bar, the sidebar, the frame, the
   answers table, the rail and the app-only class contract.

   TOKEN NAMES, NOT TOKEN VALUES. Every colour, face, size, space,
   radius, shadow and ease below is asked for by NAME, and no rule here
   depends on what a name currently holds: site.css was rewritten in the
   same wave as this file and the values moved with it.

   THE CHROME MEASUREMENTS ARE THE EXCEPTION and they are literal on
   purpose: 46 for the bar, 174 and 56 for the sidebar, 30 for a nav row,
   26 for the frame's padding, 1280 for its cap, 36 for a table row. The
   ruling names those numbers as the chrome of this product and the
   mockup draws them, so a chrome measurement that drifted with a shared
   token would be a measurement nobody could check against the ruling.

   NO ACCENT ON A BUTTON, ANYWHERE IN HERE. Raspberry is spent on links,
   the active tab underline, the named pill, the marked name inside an
   answer, the current week column, the interval band, the activation
   card's progress dots and the callout dots. A button is filled ink, a
   quiet card on hairline, or a ghost.

   ONE FILLED BUTTON PER SCREEN. Nothing in this sheet fills a second
   one; where a card head carries an action it is quiet or ghost.
   ===================================================================== */

/* =====================================================================
   1. THE DOCUMENT
   ===================================================================== */

/* THE APP IS ITS OWN DOCUMENT (ruling A2, kept). It is not the public
   layout with one row swapped, so site.css's page regions never match in
   here and the three rules below decide the page instead.

   IT SETS ITS OWN BODY TYPE. site.css owns the faces and the scale, and
   after the phase 6R rewrite its body IS 14/1.55; this restates the two
   values on the app's own body so the workspace reads at the ruling's
   density whatever order the two sheets land in. Nothing else here
   restates a base rule. */
body.app {
  display: block;
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: 1.55;
  text-wrap: pretty;
}

/* THE MEASURED FACE, ON EVERY FIGURE (ruling section 1). Counts,
   timestamps, money, ids, axis labels and verbatim answers are set in
   the mono with tabular figures and a slashed zero. The views already
   carry .mono and .num; both mean the same thing here. */
.app .mono,
.app .num,
.app .fig {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "zero" 1;
}

/* AND A FIGURE INSIDE A SENTENCE KEEPS THE SENTENCE'S SPACING (mockup
   NOTES decision 3). The mono's word space is more than twice the
   spoken face's, so "27 of 90" set inside a headline read as double
   spaced. Verbatim answers are excluded: they are the vendor's own text
   and nothing about them is adjusted. */
.app h1 .mono,
.app .interval .mono,
.app .head-meta .mono,
.app .side-foot .mono,
.app .tally .mono { word-spacing: -0.28em; }

/* =====================================================================
   2. THE TOP BAR, 46px (ruling section 2)
   ===================================================================== */

/* Left: the mark, a hairline slash, and the business crumb, which is the
   name with its town in secondary. Right: the account menu trigger, a
   26px avatar of initials on panel ground. Nothing else is in this bar:
   the destinations are the sidebar's and the money is the sidebar's. */
.app-top {
  position: sticky;
  top: 0;
  z-index: var(--z-bar);
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 16px 0 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.app-top .top-left,
.app-top .top-right { display: flex; align-items: center; gap: 10px; min-width: 0; }
.app-top .brand { display: inline-flex; align-items: center; text-decoration: none; flex: none; }
.app-top .brand .mark { display: block; height: 22px; width: auto; }
.app-top .slash { color: var(--line); font-size: 18px; line-height: 1; flex: none; }

/* THE CRUMB: the business, then its town, quieter and lighter. It is one
   line and it truncates rather than wrapping, because the bar is 46px
   and a second line would push the page down. */
.app-top .crumb {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-top .crumb:hover { text-decoration: none; }
.app-top .crumb .biz-name { overflow: hidden; text-overflow: ellipsis; }
.app-top .crumb .town { color: var(--ink-2); font-weight: 400; }

/* THE SWITCHER, AND ONLY WITH MORE THAN ONE BUSINESS (S1.2). A menu with
   one row in it is furniture, so the chevron is drawn by the summary and
   the plain crumb is an anchor with no affordance at all. */
.app-top .switcher { min-width: 0; position: relative; }

.app-top .switcher > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  height: 30px;
  padding: 0 6px;
  border-radius: 8px;
}

.app-top .switcher > summary::-webkit-details-marker { display: none; }
.app-top .switcher > summary:hover { background: var(--band); }
.app-top .switcher .chevron { color: var(--ink-2); flex: none; }

/* THE ACCOUNT MENU TRIGGER: a 26px round of initials on panel ground.
   It is a native details, so the menu opens with no script, a middle
   click on a row still opens a tab, and JavaScript off changes nothing. */
.app-top .acct { position: relative; }

.app-top .acct > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
}

.app-top .acct > summary::-webkit-details-marker { display: none; }

.app-top .avatar {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-pill);
  background: var(--band);
  border: 1px solid var(--line);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
  flex: none;
}

.app-top .acct > summary:hover .avatar { background: var(--surface); border-color: var(--line-2); }

/* THE MENU ITSELF: a popover at 312px with 40px rows (ruling section 4).
   It is the one place in the workspace a shadow is spent. */
.app-top .sheet {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: var(--z-menu);
  width: 312px;
  max-width: calc(100vw - 24px);
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow-2);
}

.app-top .sheet .who {
  font-size: var(--fs-meta);
  color: var(--ink-2);
  margin: 4px 8px 6px;
  overflow-wrap: anywhere;
}

.app-top .sheet a {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 40px;
  padding: 0 8px;
  border-radius: 8px;
  font-size: var(--fs-body);
  color: var(--ink);
  text-decoration: none;
}

.app-top .sheet a:hover { background: var(--band); text-decoration: none; }
.app-top .sheet a[aria-current="page"] { background: var(--band); font-weight: 500; }
.app-top .sheet a .ico { flex: none; color: var(--ink-2); }
.app-top .sheet a.two { height: auto; flex-direction: column; align-items: flex-start; gap: 0; padding: 8px; }
.app-top .sheet a.two .label { display: flex; align-items: center; gap: 10px; }
.app-top .sheet a.two .meta { font-size: var(--fs-meta); font-weight: 400; color: var(--ink-2); padding-left: 30px; }
.app-top .sheet a .meta { font-size: var(--fs-meta); font-weight: 400; color: var(--ink-2); }

.app-top .sheet a.rule,
.app-top .sheet .rule {
  border-top: 1px solid var(--line);
  border-radius: 0 0 8px 8px;
  margin-top: 5px;
  padding-top: 5px;
}

.app-top .sheet form { margin: 0; }
.app-top .sheet .btn-link { display: flex; align-items: center; height: 40px; padding: 0 8px; font-size: var(--fs-body); }

/* =====================================================================
   3. THE SHELL AND THE SIDEBAR (ruling section 2)
   ===================================================================== */

/* 174px of panel ground with a hairline down its right edge, and the
   surface taking the rest. minmax(0, 1fr) on the second track, because a
   grid track defaults to min-content and the answers table would push
   the page wide instead of scrolling in its own box.

   THE HAIRLINE IS PAINTED BY THE GRID, not by a border on the aside, so
   it runs the full height of a short page as well as a long one. */
.app-shell {
  display: grid;
  grid-template-columns: 174px minmax(0, 1fr);
  min-height: calc(100vh - 46px);
  background: linear-gradient(to right, var(--band) 0 173px, var(--line) 173px 174px, transparent 174px);
}

.app-side {
  position: sticky;
  top: 46px;
  height: calc(100vh - 46px);
  display: flex;
  flex-direction: column;
  padding: 12px 8px 14px;
  overflow: hidden;
  min-width: 0;
}

/* THE COLLAPSE, WITH A WIDTH TWEEN (ruling section 2). The column goes to
   56px, the labels go, the icons stay centred, and the transition is on
   the grid track so the surface widens with it rather than after it.

   THE HANDLE IS NOT THERE WHEN IT CANNOT WORK. It ships display:none and
   Alpine takes it off on init, the same idiom x-app.reveal uses, so a
   reader with no JavaScript gets an ordinary sidebar and never a control
   that does nothing. */
.app-shell.is-narrow { grid-template-columns: 56px minmax(0, 1fr); background: linear-gradient(to right, var(--band) 0 55px, var(--line) 55px 56px, transparent 56px); }
.app-shell.is-narrow .app-nav a,
.app-shell.is-narrow .app-nav summary { padding-left: 0; justify-content: center; gap: 0; }
.app-shell.is-narrow .app-nav a span,
.app-shell.is-narrow .app-nav summary span,
.app-shell.is-narrow .app-nav .chevron,
.app-shell.is-narrow .app-nav .sub,
.app-shell.is-narrow .side-foot { display: none; }
.app-shell.is-narrow .side-collapse { justify-content: center; }

.side-collapse {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 30px;
  padding: 0 8px;
  margin: 4px 0 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-2);
  font-family: var(--font);
  font-size: var(--fs-meta);
  line-height: 1;
  cursor: pointer;
}

.side-collapse:hover { background: rgba(28, 38, 48, 0.05); color: var(--ink); }
.side-collapse .ico { flex: none; }

/* THE NAV. Rows of 30px, the icon 16px sitting at 26px from the column's
   left edge, the label at 46 (mockup NOTES decision 2: lucide style
   glyphs are inset about 2px and "Named beside you" needs every pixel of
   a 174px column). The current row is a white pill at weight 500. */
.app-shell .app-nav { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.app-shell .app-nav a,
.app-shell .app-nav summary {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 30px;
  padding: 0 2px 0 18px;
  border-radius: 6px;
  color: var(--ink);
  font-size: var(--fs-body);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}

.app-shell .app-nav .ico { flex: none; width: 16px; height: 16px; color: var(--ink-2); }
.app-shell .app-nav a:hover,
.app-shell .app-nav summary:hover { background: rgba(28, 38, 48, 0.04); text-decoration: none; }

/* THE ACTIVE PILL: white, weight 500, and aria-current says the same
   thing, so colour and weight are never the only cue. */
.app-shell .app-nav a[aria-current="page"] {
  background: var(--surface);
  font-weight: 500;
  box-shadow: var(--shadow-1);
}

/* THE REPORTS DISCLOSURE, WITH ITS VERTICAL GUIDE. A native details:
   Weeks and Exports sit under it behind a hairline that runs down the
   group, so the two rows read as belonging to the row above them.
   Months arrives with the monthly report stage and slots in here. */
.app-shell .app-nav details > summary { list-style: none; cursor: pointer; }
.app-shell .app-nav details > summary::-webkit-details-marker { display: none; }
.app-shell .app-nav details > summary .chevron { margin-left: auto; color: var(--ink-2); flex: none; transition: transform var(--dur-1) var(--ease); }
.app-shell .app-nav details[open] > summary .chevron { transform: rotate(180deg); }

.app-shell .app-nav .sub {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 2px 0 2px 26px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
}

.app-shell .app-nav .sub a { height: 27px; padding-left: 8px; font-size: var(--fs-meta); }

/* THE BOTTOM BLOCK (ruling section 2): the compact checklist count, the
   plan and money line with the cancel door in it, and Sign out. Three
   short paragraphs, all at 13px on secondary ink, behind one hairline. */
.app-side .side-foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 10px 0;
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--ink-2);
}

.app-side .side-foot p { margin: 0; }
.app-side .side-foot a { color: var(--ink); }
.app-side .side-foot strong { color: var(--ink); font-weight: 500; }
.app-side .side-foot form { margin: 0; }
.app-side .side-foot .btn-link { font-size: var(--fs-meta); color: var(--ink); }
.app-side .todo-sum { margin: 0; }
.app-side .plan-line { margin: 0; }
.app-side .plan-line strong { color: var(--ink); font-weight: 500; }

/* THE WAY OUT OF A FAILED PAYMENT (phase 5p R1, flaw F6). The line above
   names the business whose charge failed; this is the press that opens
   it. It is a link and not a button because the block is a note, and it
   keeps a 24px target of its own so the smallest control in the sidebar
   still meets WCAG 2.5.8. */
.app-side .plan-fix { font-size: var(--fs-meta); margin: 0; }
.app-side .plan-fix a { display: inline-block; min-height: 24px; padding: 2px 0; font-weight: 500; }

/* THE ACCOUNT WITH NOTHING ON IT YET (owner order, 2026-09-03). No desk
   and no money, so the bottom block holds one thing: the way out. Left
   to grow it pinned Sign out to the foot of a column with nothing above
   it, so here it sits straight under the destinations. */
.app-side.no-work .side-foot { margin-top: 0; }

/* =====================================================================
   4. THE FRAME AND THE RHYTHM (ruling section 2)
   ===================================================================== */

/* 26px of padding, no max width below 1440, and from 1440 the frame caps
   at 1280 and centres. That is how the owner's measure law is met on a
   1920 screen: the frame itself carries the symmetrical margin, so no
   block on the page is left standing beside a hole. */
.app-main {
  padding: 26px;
  min-width: 0;
  width: 100%;
}

@media (min-width: 1440px) {
  .app-main { max-width: 1280px; margin: 0 auto; }
}

/* The app views were written against .wrap and .wrap.panel, which in
   site.css is the public site's centred container. Inside the shell the
   frame is the container, so the wrap gives up its width and its gutter
   and becomes the page's vertical rhythm instead: the 24px card stack.
   This one rule is what lets thirty untouched views sit in the frame. */
.app-main .wrap { max-width: none; margin: 0; padding: 0; }
.app-main .wrap.panel { display: flex; flex-direction: column; gap: 24px; }
/* A NARROW PAGE IS ONE CENTRED COLUMN, NOT A COLUMN WITH A HOLE BESIDE IT
   (the owner's measure law of 2026-08-07). Twenty views ask for .narrow:
   the account sections, the billing page, the security doors. Left aligned
   inside a 1280 frame that is 1200 wide at 1400, a 720px column stood
   against 480px of nothing, which is the defect the law names by name.
   The leftover becomes symmetrical margin instead, which is the law's
   second honest answer, and it is the same repair phase 5p made for the
   first session's column. */
.app-main .wrap.narrow { max-width: 720px; margin-inline: auto; }

/* THE PAGE TITLE ROW: an eyebrow, a 20px title, an optional action on
   the title's own line, and the lede under them. Not a card, not a band. */
.title-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px var(--gap);
  align-items: end;
  margin: 0 0 4px;
}

.title-row .eyebrow { grid-column: 1 / -1; font-size: var(--fs-meta); color: var(--ink-2); margin: 0; }

.title-row h1,
.title-row h2 {
  grid-column: 1;
  font-size: var(--fs-title);
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: 0;
  margin: 0;
}

.title-row .title-actions { grid-column: 2; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.title-row .lede { grid-column: 1 / -1; max-width: 70ch; margin: 2px 0 0; font-size: var(--fs-meta); color: var(--ink-2); }

/* A SECTION HEADING inside the stack: the 15px title over its 13px line,
   30px above the cards it names. */
.section-head { margin: 6px 0 0; }
.section-head h2 { font-size: var(--fs-h3); line-height: 1.35; font-weight: 600; margin: 0; }
.section-sub { font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); margin: 2px 0 0; max-width: 70ch; }

/* THE TABS, WITH THEIR DESCRIPTION ON THE ACTIVE AND HOVERED ONE (ruling
   section 4). The account row wraps rather than scrolling on a desktop
   (carry-list 1, api-keys-ui.md K1): seven sections do not fit one line
   and cutting the current tab off the screen costs the reader the answer
   to "where am I". At 390 it scrolls in its own box with an edge fade,
   which the phone block below draws. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  margin: 0 0 24px;
  position: relative;
}

.tabs a {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  min-height: 40px;
  padding: 10px 12px;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
}

.tabs a .ico { align-self: center; flex: none; }
.tabs a:hover { color: var(--ink); text-decoration: none; }
.tabs a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }
.tabs a .desc { display: none; font-size: var(--fs-meta); font-weight: 400; color: var(--ink-2); }
.tabs a[aria-current="page"] .desc,
.tabs a:hover .desc { display: inline; }

/* =====================================================================
   5. THE HEAD OF A BUSINESS PAGE (ruling section 6)
   ===================================================================== */

/* The receipt sentence with its count, never a greeting; the usual range
   under it; the six week strip in the right column with the range
   control above it. Two real columns, so a 1920 screen has content on
   both sides of the row. */
.app-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--gap) 40px;
  align-items: end;
  margin: 0;
}

.app-head .head-copy { max-width: 70ch; min-width: 0; }
.app-head .eyebrow { font-size: var(--fs-meta); color: var(--ink-2); margin: 0 0 6px; }

.app-head h1 {
  font-size: var(--fs-count);
  line-height: 1.2;
  letter-spacing: -0.01em;
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
}

/* THE COUNT IS THE ONLY MEASURED THING IN THE SENTENCE, so it is the
   only part in the mono face, at the sentence's own weight: a heavier
   figure than its sentence would be a second headline. */
.app-head h1 .mono { font-weight: 600; }
.app-head .interval { font-size: var(--fs-body); color: var(--ink-2); margin: 8px 0 0; max-width: 70ch; }
.app-head .interval .mono { color: var(--ink); }
.app-head .head-meta { font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); margin: 6px 0 0; }
.app-head .head-act { margin: 12px 0 0; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.app-head .head-act .exits { font-size: var(--fs-meta); color: var(--ink-2); }

/* A RUN IN FLIGHT: a determinate line with the count that is in, never a
   spinner with no number on it. The bar is aria-hidden and the sentence
   under it carries the same figure, so nothing is announced twice. */
.progress { margin: 12px 0 0; max-width: 44ch; }
.progress .track { height: 6px; border-radius: 3px; background: var(--band); overflow: hidden; }
.progress .fill { height: 100%; width: calc(var(--fill, 0) * 100%); background: var(--ink); border-radius: 3px; }

/* THE SIX WEEK STRIP, in HTML columns. Every label is a real text node
   at a real size: nothing here is scaled SVG text, which is what made
   the old chart collide with its own axis.

   THE DRAWING SLICE CAPS, THE RECORD DOES NOT (the VM1 lesson). The
   strip draws the last six sampled weeks; the weeks page keeps every
   week there has ever been. A check of a different size is never a
   column here: mixed n on one axis is quiet dishonesty. */
.weeks { width: 100%; min-width: 0; justify-self: end; }
.app-head .weeks { max-width: 360px; }

.weeks-cols {
  position: relative;
  display: flex;
  gap: 6px;
  align-items: flex-end;
  height: 84px;
}

/* The usual range, as a tinted band across the columns rather than as a
   pair of dotted edges only a chart reader can decode. The sentence in
   the key under it says the same thing in words. */
.weeks-band {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--low, 0) * 100%);
  height: calc(var(--band, 0) * 100%);
  background: var(--accent-soft);
  border-radius: 2px;
}

.wcol {
  position: relative;
  flex: 1;
  min-width: 0;
  /* A FRACTION, MULTIPLIED OUT HERE. The page prints no percent sign
     anywhere: the one thing this product must never look like it is
     publishing is a rate. */
  height: calc(var(--h, 0) * 100%);
  background: var(--ink);
  border-radius: 2px 2px 0 0;
}

.wcol.now { background: var(--accent); }
.weeks-x { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.2; color: var(--ink-2); margin-top: 6px; }
.weeks-key { font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.4; color: var(--ink-2); margin: 4px 0 0; }

.weeks-key .swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--accent-soft);
  vertical-align: -1px;
  margin-right: 6px;
}

/* THE MINI STRIP, for a row of a table (the per assistant matrix). The
   ruling says 24px; the accepted mockup ships 32 (design/mockups/
   phase6r/NOTES.md decision 4: at 24 a 6 of 30 column draws five pixels
   tall and the row cannot be read). 32 is what is drawn. */
.weeks.weeks-sm { max-width: 180px; }
.weeks.weeks-sm .weeks-cols { height: 32px; gap: 3px; }
.weeks.weeks-sm .wcol { min-height: 2px; }

/* =====================================================================
   6. CARDS, AND WHAT THIS SHEET DOES NOT RESTATE
   ===================================================================== */

/* THE CARD ITSELF IS site.css's (CSS ruling C1). The 6R baseline draws it
   at the ruling's numbers already: white on a hairline at the 10px card
   radius, no shadow, a 15px title over its 13px description, and a foot
   that bleeds to the card's edges. So the workspace adds NOTHING to
   .card, .card-head, .card-title, .card-actions, .card-body or
   .card-foot, and the two panels cannot drift apart. The same goes for
   .btn, .input, .field, .table, .tally, .pill, .chip, .seg, .scrim,
   .dialog, .pop and .hatch: every one of them is one object in this
   product, drawn once, in the sheet that owns the identity.

   WHAT IS BELOW IS ONLY WHAT THE WORKSPACE ADDS: the bleeding table and
   row list inside a card, the two chip kinds the app names differently,
   the zebra on a working table, and the app's own empty state. */

/* A WIDE TABLE SCROLLS IN ITS OWN BOX, NEVER THE PAGE, and it bleeds to
   the card's edges so the first column sits on the card's inner edge,
   level with the title above it. The bleed is the card's own padding,
   negated, which is exactly how site.css bleeds the foot. */
.card-table {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-left: calc(-1 * var(--s5));
  margin-right: calc(-1 * var(--s5));
}

.card-table .table { margin: 0; }
.card-table .table th:first-child,
.card-table .table td:first-child { padding-left: var(--s5); }
.card-table .table th:last-child,
.card-table .table td:last-child { padding-right: var(--s5); }
.card > .card-table:last-child { margin-bottom: calc(-1 * var(--s5)); }
.card-table + .card-foot { margin-top: 0; }

/* ZEBRA ON A WORKING TABLE (ruling section 2: rows on the panel ground at
   half). site.css offers it as an opt-in class because a marketing table
   of three rows does not want it; every table in the workspace is a
   record of many rows and does. */
.app-main .table tbody tr:nth-child(even) td { background: var(--zebra); }
.app-main .table .fig { font-family: var(--font-mono); font-size: var(--fs-meta); font-variant-numeric: tabular-nums; font-feature-settings: "zero" 1; white-space: nowrap; }

/* A ROW LIST INSIDE A CARD (the findings desk, the receipts steps): the
   same bleed, so a row's hover tint reaches the card's border. */
.card-rows { border-top: 1px solid var(--line); margin-left: calc(-1 * var(--s5)); margin-right: calc(-1 * var(--s5)); }
.card-rows ol,
.card-rows ul { list-style: none; margin: 0; padding: 0; }
.card-rows li { border-bottom: 1px solid var(--line); }
.card-rows li:last-child { border-bottom: 0; }
.card > .card-rows:last-child { margin-bottom: calc(-1 * var(--s5)); }
.card-rows + .card-foot { margin-top: 0; }
.desk-group { margin: 0; padding: var(--s2) var(--s5); background: var(--band); font-size: var(--fs-mini); font-weight: 500; letter-spacing: 0.02em; color: var(--ink-2); }

/* THE TWO CHIP KINDS THE APP NAMES DIFFERENTLY. site.css draws ok, warn,
   bad, named, quiet and src; the billing page's status cell and the
   answer verdicts were written against `picked` and `possible` before
   the chip had kinds, and a class contract is kept by giving the shipped
   name a home, not by rewriting five views. */
.chip.picked { color: var(--ok); background: var(--ok-bg); }
.chip.possible { color: var(--warn); background: var(--warn-bg); }

/* THE READING CARD (ruling section 4): a title, its description, the
   count in mono, the usual range, the strip and one door. The head of a
   business page is the same grammar at the page's size; this is it
   inside a card, which is what the later stages' pages are built from. */
.reading { display: grid; gap: var(--s2); }
.reading .count { font-family: var(--font-mono); font-size: 24px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.reading .usual { font-size: var(--fs-meta); color: var(--ink-2); margin: 0; }

/* THE EMPTY STATE: a sentence, a mono hint line and one door, on the
   hatched panel. Never a control over a list that is not there. It is
   the app's own class name for site.css's .hatch and it is drawn the
   same way, because a reader meets both. */
.empty-state {
  padding: 28px var(--gap);
  text-align: center;
  color: var(--ink-2);
  border: 1px dashed var(--line);
  border-radius: var(--radius-s);
  background: repeating-linear-gradient(45deg, rgba(28, 38, 48, 0.06) 0 1px, transparent 1px 8px);
}

.empty-state h2 { font-size: 18px; line-height: 1.3; font-weight: 600; color: var(--ink); margin: 0 0 var(--s2); }
.empty-state p { margin: 0 0 var(--s2); }
.empty-state p:last-child { margin-bottom: 0; }
.empty-state .hint { font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.5; }

/* =====================================================================
   7. THE ACTIVATION CARD, THE ONE WARM SURFACE (ruling section 3)
   ===================================================================== */

/* Panel ground, a 2px ink rule at its head, raspberry progress dots, a
   16px radius, and it collapses as a native details. No gradient.
   Nothing on the list publishes anything anywhere, which the card's own
   subtitle says out loud (the approval gate). */
.activation {
  background: var(--band);
  border: 1px solid var(--line);
  border-top: 2px solid var(--ink);
  border-radius: 16px;
  padding: var(--s4) var(--s5) 18px;
}

.activation > summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; }
.activation > summary::-webkit-details-marker { display: none; }
.activation .act-title { font-size: var(--fs-h3); font-weight: 600; margin: 0; }
.activation .act-sub { font-size: var(--fs-meta); color: var(--ink-2); margin: 2px 0 0; }
.activation .actions { display: flex; align-items: center; gap: 8px; flex: none; }

.dots { display: inline-flex; gap: 5px; vertical-align: middle; margin-left: 8px; }
.dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--line); }
.dots i.done { background: var(--accent); }

.act-chev,
.activation > summary .chevron { color: var(--ink-2); flex: none; transition: transform var(--dur-1) var(--ease); }
.activation[open] .act-chev,
.activation[open] > summary .chevron { transform: rotate(180deg); }

.act-group { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); margin-top: 14px; }
.act-group .todo { list-style: none; margin: 0; padding: 0; }
.act-label { font-size: var(--fs-meta); color: var(--ink-2); margin: 14px 0 0; }
.act-label form { margin: 0; }

.act-row {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) auto auto;
  gap: 12px;
  align-items: center;
  min-height: 41px;
  padding: 6px 14px;
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.act-row:first-child { border-top: 0; }
.act-row .ring { width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--line); display: grid; place-items: center; }
.act-row.done .ring { background: var(--ink); border-color: var(--ink); color: #ffffff; }
.act-row .min { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink-2); white-space: nowrap; }
.act-row .door { font-size: var(--fs-meta); font-weight: 500; white-space: nowrap; }
.act-row.done .name { color: var(--ink-2); }

/* =====================================================================
   8. THE ZERO ROW CARD (ruling section 6)
   ===================================================================== */

/* Every business the assistants named this week, per assistant, with the
   count of answers that named each. It is a list of names and counts and
   nothing in it is a place in a list: no ordinal column, no arrow, no
   league table. The reader's own row is the one in the accent, and the
   closing row says the count the head restates. */
.zero {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: 8px 16px;
  align-items: baseline;
  padding: 9px 0;
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.zero:first-child { border-top: 0; }
.zero .who { font-weight: 500; }
.zero .who small { display: block; font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.4; color: var(--ink-2); }
.zero ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 14px; }
.zero li { white-space: nowrap; }
.zero li .mono { font-size: var(--fs-meta); color: var(--ink-2); }
.zero li.you { color: var(--accent); font-weight: 500; }
.zero li.you .mono { color: var(--accent); }

.zero-you {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
  padding-top: 12px;
  border-top: 2px solid var(--ink);
  font-weight: 600;
}

/* =====================================================================
   9. THE ANSWERS TABLE (ruling A3, kept; ruling 6R section 6)
   ===================================================================== */

/* ONE TABLE, GROUPED BY ASSISTANT, EVERY ROW A NATIVE DETAILS. No script
   anywhere in this component: a row opens because the browser opens it,
   which is why it works with JavaScript off, prints, and can be opened
   by a fragment.

   THE CARD IS THE TABLE'S BOX, so the card's padding goes and every row
   carries the card's own inset instead: that is what lets a row's hover tint,
   the assistant's group head and the sum row reach the card's border. */
.answers { padding: 0; }
.answers > .card-head,
.answers .answers-open > summary.card-head { margin: 0; padding: var(--s5) var(--s5) var(--s4); }

/* THE COLUMN STRIP, at the table head's own size. */
.answers .cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px 84px 24px;
  gap: 12px;
  align-items: center;
  height: 36px;
  padding: 0 var(--s5);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink-2);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.answers .cols span { min-width: 0; }
.answers .cols .num { text-align: right; }

/* THE ASSISTANT'S GROUP HEAD: the plain name on panel ground, its ten
   cell dot strip and its count with its own n. The engine name is never
   translated. */
.answers .grp {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: 36px;
  padding: 6px var(--s5);
  background: var(--band);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body);
}

.answers .grp .g-name { font-weight: 600; color: var(--ink); min-width: 0; }
.answers .grp .g-model { font-family: var(--font-mono); font-size: var(--fs-mini); font-weight: 400; color: var(--ink-2); }
.answers .grp .g-sum { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink-2); }

/* THE ASSISTANT'S FOLD, AND THE GROUP HEAD IS ITS SUMMARY (V1 finding
   4). Closed, the row is exactly what it always was: the name, the ten
   cell strip and the count with its own n. A Growth week of 216 answers
   folded is about a tenth of the page it was at 390. */
.answers details.grp-fold > summary.grp { list-style: none; cursor: pointer; }
.answers details.grp-fold > summary.grp::-webkit-details-marker { display: none; }
.answers details.grp-fold > summary.grp:hover { background: #e7edf3; }
.answers details.grp-fold > summary .caret { transition: transform var(--dur-1) var(--ease); }
.answers details.grp-fold[open] > summary .caret { transform: rotate(180deg); }
.answers details.grp-fold:last-of-type > details.row:last-of-type { border-bottom: 0; }

/* TEN CELLS, AND THE FIGURE BESIDE THEM IS THE TRUTH. The strip is a
   drawing at a fixed ten cells whatever n is, so it is aria-hidden and
   the mono count next to it carries the count and its n. */
.g-bar { display: inline-flex; gap: 2px; }
.g-bar i { width: 8px; height: 8px; border-radius: 2px; background: var(--line); }
.g-bar i.hit { background: var(--accent); }

/* THE ROW: 36px, opening in place to the answer word for word. */
details.row { border-bottom: 1px solid var(--line); }
details.row:last-of-type { border-bottom: 0; }

details.row > summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px 84px 24px;
  gap: 12px;
  align-items: center;
  min-height: 36px;
  padding: 6px var(--s5);
  font-size: var(--fs-body);
}

details.row > summary::-webkit-details-marker { display: none; }
details.row > summary:hover,
details.row[open] > summary { background: var(--zebra); }
details.row .q { min-width: 0; font-size: var(--fs-body); }
details.row .q .rep { color: var(--ink-2); font-size: var(--fs-meta); white-space: nowrap; }
details.row .when { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink-2); text-align: right; white-space: nowrap; }

.answers .caret {
  color: var(--ink-2);
  font-size: var(--fs-meta);
  text-align: center;
  justify-self: end;
  transition: transform var(--dur-1) var(--ease);
}

details.row[open] .caret { transform: rotate(180deg); }

/* THE VERBATIM ANSWER, opened in place, in the measured face at 14/1.6.
   It is the vendor's words, stored as they came back; the business name
   is marked with the soft accent fill and a 2px accent rule, which is
   the one place inside an answer any colour is spent. */
.answers .answer {
  margin: 0 var(--s5) 14px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.answers .answer .a-head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  font-size: var(--fs-meta);
  color: var(--ink-2);
  margin: 0 0 8px;
}

.answers .answer .a-body {
  font-family: var(--font-mono);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--ink);
  overflow-wrap: anywhere;
  margin: 0;
}

.answers .answer .a-foot { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; font-size: var(--fs-meta); color: var(--ink-2); margin: 10px 0 0; }

/* THE MARKED NAME (ruling section 3): the soft raspberry fill with a 2px
   raspberry rule under it. AnswerText puts the one tag back; nothing
   else about the vendor's text is touched. */
.answers mark,
.app-main mark {
  background: var(--mark);
  color: var(--mark-ink);
  box-shadow: inset 0 -2px 0 var(--accent);
  padding: 1px 2px;
  border-radius: 2px;
}

/* THE SUM ROW, UNDER A 2px INK RULE. This row is the headline's source:
   the h1 above restates this number and nothing else states it a third
   time. */
.answers .sum {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 8px var(--s5);
  border-top: 2px solid var(--ink);
  background: var(--surface);
  font-weight: 600;
}

.answers .sum .mono { font-weight: 600; }

/* The page's own footnote: what has been sampled, since when, and the
   way to the method. It sits under the stack, not inside a card. */
.app-foot {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.app-foot .nb { white-space: nowrap; }

/* =====================================================================
   10. THE RAIL: REAL CONTENT BESIDE EVERY TABLE (ruling section 4)
   ===================================================================== */

/* The owner's measure law says a capped block owes an answer to "what is
   beside it?", and the best answer is real content. x-app.rail is that
   content: the facts the table's count is made of, the same rows read
   the other way, and the doors that take the table off this page.

   265px, because that is the width the rail's longest label and its
   figure need without wrapping, and it leaves the table its own measure.
   Under 900 the rail falls under the table it belongs to. */
.with-rail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 265px;
  gap: var(--gap);
  align-items: start;
}

.rail { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }
.rail .card { padding: var(--s4); }
.rail .card-head { margin: 0 0 10px; }
.rail .card-foot { margin: var(--s4) calc(-1 * var(--s4)) calc(-1 * var(--s4)); padding: 10px var(--s4); }
.rail .kv { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; font-size: var(--fs-meta); border-top: 1px solid var(--line); }
.rail .kv:first-child { border-top: 0; padding-top: 0; }
.rail .kv span { min-width: 0; }
.rail .kv b { font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* =====================================================================
   11. THE DESK ROWS AND THE BANNER
   ===================================================================== */

/* THE DESK ROW. The whole row is one target, the status is a word and
   never a colour alone, and what the row buys is an observed fact under
   its title. Nothing on this list publishes anything: every row is
   advice until the reader acts on it. */
.desk-row {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto 16px;
  gap: 12px;
  align-items: baseline;
  padding: 10px var(--s5);
  color: inherit;
  text-decoration: none;
  font-size: var(--fs-body);
}

.desk-row:hover { background: var(--zebra); text-decoration: none; }
.desk-row .t { font-weight: 500; }
.desk-row .mins { font-family: var(--font-mono); font-weight: 400; color: var(--ink-2); font-size: var(--fs-meta); }
.desk-row .buys { grid-column: 2; font-size: var(--fs-meta); color: var(--ink-2); }
.desk-row .state { font-size: var(--fs-meta); color: var(--ink-2); white-space: nowrap; }
.desk-row .state.bad { color: var(--bad); }
.desk-row .state.fixed { color: var(--ok); }
.desk-row .go { color: var(--ink-2); align-self: center; }
.desk-row .ico { align-self: center; color: var(--ink-2); }
li.done .desk-row .t { color: var(--ink-2); }

/* A disclosure row on the desk: the finding opens what we read on this
   business and what to do about it, in place. */
.task > summary { list-style: none; cursor: pointer; }
.task > summary::-webkit-details-marker { display: none; }
.task[open] > summary { background: var(--zebra); }
.task > div { padding: 0 var(--s5) var(--s4); }

/* THE FIRST EVENING, NOT THE WHOLE BACKLOG (phase 5p R2, finding F6).
   The findings desk shows three rows and folds the rest behind one line
   that prices them. The row that carries the fold is a row of the same
   list, so the card's hairlines still run edge to edge. */
.card-rows li.more-rows { color: inherit; }

.card-rows .rest-fold > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 8px var(--s5);
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.card-rows .rest-fold > summary::-webkit-details-marker { display: none; }
.card-rows .rest-fold > summary:hover { background: var(--zebra); color: var(--ink); }
.card-rows .rest-fold > ol { border-top: 1px solid var(--line); }

/* THE BANNER, one to a page, above the head. It carries its own ground:
   a card with a banner inside it would be two boxes for one sentence.
   The heading names the state, so colour is never the only cue. */
.banner {
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-2);
  border-radius: 8px;
  background: var(--surface);
  padding: 12px 16px;
}

.banner h2 { font-size: var(--fs-h3); font-weight: 600; line-height: 1.35; margin: 0 0 2px; }
.banner p { margin: 0; font-size: var(--fs-meta); color: var(--ink-2); }
.banner form { margin: 8px 0 0; }
.banner.ok { border-left-color: var(--ok); background: var(--ok-bg); }
.banner.bad { border-left-color: var(--bad); background: var(--bad-bg); }
.banner.bad h2 { color: var(--bad); }

/* THE SEGMENTED CONTROL. site.css draws it: 34px, a 28px cell each, the
   active cell filled ink, and the ruling excludes that cell from the one
   filled button a screen is allowed. Two things are the app's own.

   THE APP'S SEGMENTS SAY aria-current="true", not "page". They are views
   of one page (all answers, the ones that named you, the ones that did
   not), and "page" would be a lie about where the reader is; the shipped
   component has said "true" since it was written and five views carry
   it, so the state gets a home rather than the markup a rewrite.

   AND IT SCROLLS IN ITS OWN BOX RATHER THAN CUTTING A SEGMENT OFF (phase
   5p R1, flaw F3). At 390 the week page's filter read "All 216 | Named
   you, 74 | Did not name yo", with the one control that answers "show me
   where I was NOT named" cut mid word. overflow-y stays hidden so the
   radius still clips the cell fills, and a box with a non-visible
   overflow is a scroll container, whose automatic minimum size is zero:
   that is what lets it shrink inside the toolbar's flex row instead of
   pushing the page wide. The bar itself is hidden, because a classic
   scrollbar drawn inside a 34px control adds its own height to a row
   that is one line of the page. */
.seg { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; scrollbar-width: none; max-width: 100%; }
.seg::-webkit-scrollbar { display: none; }
.seg a[aria-current="true"] { background: var(--ink); color: var(--button-ink); }

/* =====================================================================
   12. THE LEDGERS: THE TALLY, THE FACTS AND THE RECEIPTS ROWS
   ===================================================================== */

/* A LEDGER ROW IS A FACT AT THE SIZE OF A FACT (ruling A2: no stat card
   row anywhere in the app). site.css owns .tally whole, the prose value
   and the sum row included, and NOTHING here restates it: a rule of the
   same weight in the second sheet silently wins, which is how the cancel
   page's plan sentence lost its wrap and ran 10px off a 390 viewport
   (measured on /app/{business}/billing/cancel, scrollWidth 400). What is
   below is only the rows the workspace adds.

   THE READER'S OWN ROW is in the accent the current week column wears. */
.tally li.you { color: var(--accent); font-weight: 500; }
.tally li.you > span:last-child { color: var(--accent); }
.tally li.more-row { color: var(--ink-2); }

/* A TALLY VALUE THAT IS A SENTENCE MUST WRAP. A figure that wraps is
   unreadable, which is why the last cell is held on one line; three rows
   in this panel put a sentence there instead (the checkout's payment
   line, a reading whose value is a word, and the week zero row that
   reads out the three crawler names). A sentence is spoken type. */
.tally li.method > span:last-child,
.tally li.tile .n.word,
.tally li.crawlers > span:last-child {
  white-space: normal;
  text-align: right;
  font-family: var(--font);
  font-variant-numeric: normal;
}

.tally li.method > span:last-child { color: var(--ink-2); }

/* A MENU PATH WRAPS AND KEEPS THE MEASURED FACE. "Application Management,
   Access Logs" is a literal a reader retypes into somebody else's control
   panel, so it stays in the mono face and wraps rather than running off a
   390 viewport. */
.tally li.path > span:last-child { white-space: normal; text-align: right; }

/* THE READING ROW (the row that used to be a tile): the label with what
   the value is made of under it, and the value itself. A value that is a
   word rather than a figure is set in the spoken face, because a
   sentence in the measured face is a sentence dressed as a total. */
.tally li.tile .k { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tally li.tile .k .d { font-size: var(--fs-meta); color: var(--ink-2); }
.tally li.tile .n small { font-family: var(--font); font-variant-numeric: normal; font-size: var(--fs-meta); color: var(--ink-2); }
.tally li.tile .n.word { color: var(--ink-2); }
.crawlers { align-items: start; }

/* THE PLAN FACTS STRIP, on panel ground at the foot of a card (the
   billing page's next charge). Three facts, hairline between them. */
.facts { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; margin: 0; }
.facts > div { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.facts dt { color: var(--ink-2); font-size: var(--fs-meta); margin: 0; }
.facts dd { margin: 0; font-size: var(--fs-body); }

@media (min-width: 700px) {
  .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 32px; }
}

/* The businesses ledger on /app and /app/account/businesses. */
.biz-list { list-style: none; margin: 0; padding: 0; }
.biz-list > li { border-bottom: 1px solid var(--line); padding: 14px 0; }
.biz-list > li:last-child { border-bottom: 0; }
.biz-list .name { font-weight: 500; }
.biz-list .meta { color: var(--ink-2); font-size: var(--fs-meta); }
.biz-edit { margin: 12px 0 0; }

/* =====================================================================
   13. THE TOAST AND THE SKELETON ROWS
   ===================================================================== */

/* THE POPOVER, THE CONFIRM DIALOG AND ITS FROSTED SCRIM ARE site.css's
   (.pop at 312 with 40px rows, .dialog, .scrim). The account menu in the
   top bar above is the workspace's one popover and it is drawn there, on
   its own summary, because it hangs off a 26px avatar rather than off a
   quiet button. These two are what the workspace adds.

   THE TOAST NAMES WHAT WAS SAVED (ruling section 5). It is the one thing
   this product animates on a state change, it carries no action, and it
   is one of the three objects allowed a shadow. */
.toast {
  position: fixed;
  left: 50%;
  bottom: var(--s5);
  z-index: var(--z-menu);
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(100vw - 2 * var(--s4));
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-2);
  font-size: var(--fs-meta);
  color: var(--ink);
}

/* COLUMN MATCHED SKELETON ROWS (ruling section 4). A skeleton is the
   shape of the row it replaces and nothing else: no spinner, and no bar
   that claims progress it does not have. The shimmer is inside the
   no-preference query at the foot of this sheet. */
.skeleton-row { display: grid; grid-template-columns: minmax(0, 1fr) 132px 84px 24px; gap: 12px; align-items: center; min-height: 36px; padding: 6px var(--s5); border-bottom: 1px solid var(--line); }
.skeleton-row span { display: block; height: 10px; border-radius: 3px; background: var(--band); }

/* =====================================================================
   14. THE GUIDANCE RAIL AND THE FAQ (ruling section 4)
   ===================================================================== */

/* Two honest columns, one boxed IMPORTANT line, and an FAQ that leads
   with the scariest question. It is the wizard's rail in the mockup and
   the shape any page's guidance takes; native details throughout, so it
   opens with no script. */
.guide h3 { font-size: var(--fs-meta); font-weight: 600; margin: 0 0 6px; }
.guide ul { padding-left: 14px; margin: 0; font-size: var(--fs-meta); color: var(--ink-2); display: flex; flex-direction: column; gap: 4px; }
.guide .two { display: grid; gap: 14px; }
.guide .important { margin-top: 16px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.guide .important .mini { font-family: var(--font-mono); font-size: var(--fs-mini); font-weight: 500; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-2); }
.guide .important p { font-size: var(--fs-meta); color: var(--ink); margin: 4px 0 0; }

.faq { margin-top: 16px; }
.faq details { border-top: 1px solid var(--line); }
.faq details:first-of-type { border-top: 0; }
.faq summary { list-style: none; cursor: pointer; display: flex; gap: 8px; align-items: flex-start; padding: 8px 0; font-size: var(--fs-meta); font-weight: 500; }
.faq summary::-webkit-details-marker { display: none; }
.faq p { font-size: var(--fs-meta); color: var(--ink-2); margin: 0; padding: 0 0 10px 22px; }

/* =====================================================================
   15. THE FORMS THE APP ADDS
   ===================================================================== */

/* THE FORM GRID OF THE CARRY-LIST (item 1, design/rules/api-keys-ui.md):
   .field .radioline .checkline, the row of a radio or a checkbox with
   its label and the sentence under it.

   IT NEEDS THIS HOME BECAUSE OF A SPECIFICITY LOSS. site.css declares
   `.checkline { display: grid; }` and, above it, `.field label { display:
   block; }`, which is one class more specific; every one of these rows IS
   a label inside a field, so the block rule wins and the two column grid
   never applies. Measured on /app/account/api at 1400. */
.field .radioline .checkline,
.field .checkline {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  margin: 0;
  font-weight: 400;
}

.field .checkline .meta { display: block; color: var(--ink-2); font-size: var(--fs-meta); }

/* A PAIR OF FIELDS THAT BELONG TOGETHER SHARES A ROW (rulebook R3), and
   width is a format hint: a postcode is five to eight characters and a
   VAT number eleven to fourteen, so a box as wide as a street address
   invites a street address. */
.form .two { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4) var(--gap); }

@media (min-width: 640px) {
  .form .two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.form .field.w-postcode .input { max-width: 12ch; }
.form .field.w-vat .input { max-width: 18ch; }

/* THE TWO ROWS OF ACTIONS THE APP WRITES. site.css sizes every control
   from --control, so nothing here restates a height. */
.app-main .submit { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; margin-top: var(--s4); }
.confirm { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; }

/* THE SHOW CONTROL, INSIDE THE FIELD IT ACTS ON (phase 5p R1, flaw F17).
   The word sits at the field's right edge, in secondary ink rather than
   the accent: three raspberry words down the security card would
   out-shout the one filled button that is the page's task. */
.pw { position: relative; }
.pw .input { padding-right: 5.5em; }

.pw-toggle {
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  padding: 0 12px;
  min-height: 24px;
  font-size: var(--fs-meta);
  border-radius: 0 8px 8px 0;
  color: var(--ink-2);
}

.pw-toggle:hover { color: var(--ink); }

/* THE KEY SHOWN ONCE (carry-list 1, design/rules/api-keys-ui.md). The
   plaintext token is printed exactly once, in the measured face, in a
   box that says so; the status line under it is the copy button's live
   region and is empty until the reader presses it. */
.key-box {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: var(--band);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow-wrap: anywhere;
}

.key-box .mono { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink); }
.key-status { font-size: var(--fs-meta); color: var(--ok); margin: 8px 0 0; min-height: 1.4em; }

/* THE CHECKOUT ROW: the form, with the summary beside it (phase 5p R1,
   flaws F19 and F20). The columns are placed by hand, so the document
   can carry the summary first, which is what a phone reads top to
   bottom, while a wide screen puts the form on the left. */
.app-main .auth { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); align-items: start; }
.app-main .auth > aside { display: grid; gap: var(--gap); min-width: 0; align-content: start; }

@media (min-width: 960px) {
  .app-main .auth { grid-template-columns: minmax(0, 1fr) 340px; }
  .app-main .auth > .card { grid-column: 1; grid-row: 1; }
  .app-main .auth > aside { grid-column: 2; grid-row: 1; }
}

/* =====================================================================
   16. THE INHERITED APP VOCABULARY

   The classes the thirty views under resources/views/app still ask for,
   restyled to the 6R skin under their shipped names (the class contract
   law of phase5o.md, which this phase keeps). Nothing is renamed: every
   selector below is a name a shipped view already carries, so no markup
   had to move to get its styling back, and a view that is rebuilt in a
   later stage simply stops matching.
   ===================================================================== */

/* ---- the week page's tools row ---- */
.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--s4);
  flex-wrap: wrap;
  margin: 0 0 var(--s4);
}

.toolbar .left { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; min-width: 0; }
.toolbar .right { color: var(--ink-2); font-size: var(--fs-meta); }
.toolbar .right p { margin: 0; }

/* ---- the week page's question cards ---- */

/* ONE QUESTION IS ONE CARD AND ITS BODY IS THE ANSWERS TABLE (rulings A3
   and A6). The card wears `.answers` as well as `.answers-group`, so the
   table region above styles the column strip, the group heads, the
   answer rows and the sum row here without a second copy of any of it.
   What is left for this block is the disclosure: the card head IS the
   summary. */
.answers-group > .answers-open { border-bottom: 0; }
.answers-open > summary { list-style: none; cursor: pointer; }
.answers-open > summary::-webkit-details-marker { display: none; }
.answers-open > summary:hover { background: var(--zebra); }
.answers-open > summary .card-actions { color: var(--ink-2); font-size: var(--fs-meta); font-weight: 500; white-space: nowrap; }
.answers-open .when-open { display: none; }
.answers-open[open] .when-closed { display: none; }
.answers-open[open] .when-open { display: inline; }

/* ONE LABEL PER STATE, FOR EVERY DISCLOSURE IN THE PANEL (phase 5p R1,
   flaw F4, rulebook R15). The scope is the summary and not the subtree:
   `details[open] .when-closed` would reach into a closed disclosure
   nested inside an open one and hide its label too. */
details > summary .when-open { display: none; }
details[open] > summary .when-closed { display: none; }
details[open] > summary .when-open { display: inline; }
.answers-group .sum:last-child { border-bottom-left-radius: var(--radius); border-bottom-right-radius: var(--radius); }
.tally.sample-sum { margin-top: var(--s4); }

/* ---- the names beside you, as bars ---- */

/* THE ROW IS A NAME, A BAR AND A COUNT. The bar is a drawing of the
   count beside it and the block is aria-hidden, so nothing is announced
   twice; the table under it is the record. The reader's own row is the
   one in the accent, which is the accent the current week column wears. */
.named-bars { display: flex; flex-direction: column; gap: var(--s3); margin: 0 0 var(--s5); }
.named-bars .nb-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s1) var(--s3); align-items: baseline; }
.nb-name { min-width: 0; }
.nb-count { font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--ink-2); font-size: var(--fs-meta); }
.nb-track { grid-column: 1 / -1; grid-row: 2; height: 6px; border-radius: 3px; background: var(--band); overflow: hidden; }
.nb-bar { display: block; height: 100%; width: calc(var(--f, 0) * 100%); background: #c6d0da; border-radius: 3px; }
.named-bars .nb-row.you .nb-bar { background: var(--accent); }
.named-bars .nb-row.you .nb-name { font-weight: 500; }
.named-table .table td.plan-cell { white-space: normal; }

/* ---- the receipts page's thirty days ---- */
.day-cols { color: #c6d0da; }
.day-cols .dc-plot svg { width: 100%; height: 80px; display: block; }
.day-cols .dc { color: #c6d0da; }
.day-cols .dc-base { color: var(--line); }
.day-cols .dc-x { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: var(--fs-mini); color: var(--ink-2); margin-top: var(--s1); }
.day-cols .dc-total { font-size: var(--fs-meta); color: var(--ink-2); margin: var(--s2) 0 0; }

/* ---- the notification switches ---- */
.toggle-list { list-style: none; margin: 0; padding: 0; }
.toggle-list > li { border-bottom: 1px solid var(--line); }
.toggle-list > li:last-child { border-bottom: 0; }

.toggle-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s1) var(--s4);
  padding: var(--s3) 0;
}

.toggle-row label,
.toggle-row .name { font-weight: 500; margin: 0; }
.toggle-row .cadence { grid-column: 1; font-size: var(--fs-meta); color: var(--ink-2); }
.toggle-row .ctl { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; gap: var(--s3); }
.toggle-row .saved { font-size: var(--fs-meta); color: var(--ok); }

/* A 34px track in ink when it is on (ruling section 4). accent-color is
   what paints a native checkbox, and the ink is what the ruling asks
   for: a switch is not a link and never wears the raspberry. */
.toggle-row .switch { width: 44px; height: 24px; accent-color: var(--ink); }

/* THE STATE, IN WORDS (phase 5p R1, flaw F18, rulebook R10). Beside a
   live switch the word is the second cue, so colour is never the only
   one; a locked row has no switch at all and the word IS the control's
   place, so it is set in ink at the weight of a value. */
.toggle-row .state {
  font-size: var(--fs-meta);
  color: var(--ink-2);
  min-width: 4.5em;
  text-align: right;
  white-space: nowrap;
}

.toggle-row .state.locked { color: var(--ink); font-weight: 500; margin-right: calc(44px + var(--s3)); }

/* ---- the security pages ---- */
.codes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2) var(--s4); margin: 0 0 var(--s4); padding: 0 0 0 var(--s5); }
.codes li { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.qr-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); align-items: start; }
.qr { background: var(--surface); padding: var(--s3); border: 1px solid var(--line); border-radius: var(--radius-s); width: max-content; }
.qr svg { display: block; width: 168px; height: 168px; }

@media (min-width: 700px) {
  .qr-row { grid-template-columns: auto minmax(0, 1fr); gap: var(--s5); }
}

/* A literal a reader copies: the snippet, the TOTP key, an API prefix. */
.code {
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  background: var(--band);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: var(--s3);
  margin: 0 0 var(--s3);
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---- the small shared shapes ---- */
.two-up { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); }
.pair { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
.trio { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); }

@media (min-width: 700px) {
  .two-up { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .trio { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.summary { display: grid; gap: var(--s2); margin: 0 0 var(--s4); }
.prose > * + * { margin-top: var(--s3); }
.more-row { color: var(--ink-2); }

/* .stack is a TABLE that stacks on a phone, not a column of cards: the
   app's card stack is .wrap.panel. Two shipped views write
   `<table class="table stack">` and this is the width they mean. */
.stack { width: 100%; }

/* ---- the sample week's own blocks (the public rendering of this view) ---- */
.sample-head { display: flex; gap: var(--s5); align-items: flex-end; justify-content: space-between; flex-wrap: wrap; }
.sample-head-copy { max-width: 70ch; min-width: 0; }
.sample-instrument { min-width: 260px; }
.si-label { font-size: var(--fs-meta); color: var(--ink-2); margin: 0 0 var(--s2); }
.ruler-key { font-size: var(--fs-meta); color: var(--ink-2); margin: var(--s2) 0 0; }
.sample-sum { margin-top: var(--s4); }
.sample-buy { display: grid; gap: var(--s4); }
.buy-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); align-items: start; }

@media (min-width: 860px) {
  .buy-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.buy-plans { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
.buy-plan { border: 1px solid var(--line); border-radius: var(--radius); padding: var(--s4); }
.buy-plan .bp-name { font-weight: 500; }
.buy-plan .bp-line { font-size: var(--fs-meta); color: var(--ink-2); }
.buy-trust { font-size: var(--fs-meta); color: var(--ink-2); }

@media (min-width: 760px) {
  .buy-plans { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The Monday mail, drawn as an object on the sample. It floats, so it is
   one of the three things allowed the one shadow. */
.mail-stage { display: grid; gap: var(--s3); }
.mail-object { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); padding: var(--s4); box-shadow: var(--shadow-2); }
.mo-head { font-size: var(--fs-meta); color: var(--ink-2); margin: 0 0 var(--s2); }
.mo-subject { font-weight: 600; margin: 0 0 var(--s2); }
.mo-line { margin: 0 0 var(--s2); }
.mo-quote { font-family: var(--font-mono); font-size: var(--fs-meta); line-height: 1.6; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--s3); margin: 0 0 var(--s2); }
.mo-pre { font-size: var(--fs-meta); color: var(--ink-2); margin: 0 0 var(--s2); }
.mo-from { font-size: var(--fs-meta); color: var(--ink-2); }
.mo-foot { font-size: var(--fs-meta); color: var(--ink-2); margin: var(--s2) 0 0; }
.mo-btn { margin: var(--s3) 0; }
.mail-caption { font-size: var(--fs-meta); color: var(--ink-2); }

/* The mini weekly loop chart the sample draws. Every label is HTML text
   outside the drawing; the svg carries no text at all. */
.chart-mini { display: grid; gap: var(--s2); }

/* THE PLOT IS A TWO COLUMN GRID AND EVERY CELL IS PLACED BY HAND. Auto
   placement put the week row into the first column, whose `auto` track
   then sized to those labels and left the drawing a track of zero width,
   so the svg rendered at its intrinsic 1000px and the page scrolled
   sideways at 390. */
.chart-plot { position: relative; display: grid; grid-template-columns: 46px minmax(0, 1fr); gap: var(--s2); }
.chart-y { grid-column: 1; grid-row: 1; display: flex; flex-direction: column; justify-content: space-between; font-family: var(--font-mono); font-size: var(--fs-mini); color: var(--ink-2); }
.chart-area { grid-column: 2; grid-row: 1; position: relative; min-width: 0; color: #c6d0da; }
.chart-area svg { width: 100%; height: 180px; display: block; }
.chart-grid { color: var(--line); }
.chart-base { color: var(--line); }
.chart-band { color: var(--accent); }
.chart-band-edge { color: var(--accent); }
.chart-band-label { display: block; font-size: var(--fs-meta); color: var(--ink-2); margin-top: var(--s1); }
.chart-x { grid-column: 2; grid-row: 2; display: grid; grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)); font-family: var(--font-mono); font-size: var(--fs-mini); color: var(--ink-2); }
.chart-x span { text-align: center; }
.chart-x .kind { display: block; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.chart-x .now { color: var(--ink); font-weight: 500; }

/* The card that closes the week page, and the ledger aside beside it. */
.ledger-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); padding: var(--s4) var(--s5); }
.closing { display: flex; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; font-size: var(--fs-meta); color: var(--ink-2); }
.closing-links { display: flex; gap: var(--s4); flex-wrap: wrap; }

/* THE WEEKS LEDGER (phase 5p R1, flaw F8). One row a sampled week: the
   day, the count with its n, the usual range in words and what moved. */
.weeks-table .table td.num,
.weeks-table .table th.num { white-space: nowrap; }
.weeks-table .table .range { color: var(--ink-2); font-size: var(--fs-meta); }

/* THE HEAD IS TWO FACTS AND A FOLD (phase 5p R1, flaw F12). The
   comparison line was six honest sentences in one paragraph and every
   one of them was skipped. What stands is the rolling count and what
   moved; the weeks behind them open on one press. */
.head-fold { margin: var(--s2) 0 0; }
.head-fold > summary { list-style: none; cursor: pointer; font-size: var(--fs-meta); color: var(--ink-2); display: inline-flex; align-items: center; gap: var(--s2); min-height: 24px; }
.head-fold > summary::-webkit-details-marker { display: none; }
.head-fold > summary:hover { color: var(--ink); }
.head-fold > summary .caret { transition: transform var(--dur-1) var(--ease); }
.head-fold[open] > summary .caret { transform: rotate(180deg); }
.head-fold .head-meta:first-of-type { margin-top: var(--s1); }

/* THE FIRST SESSION'S COLUMN IS CENTRED IN THE WHOLE FRAME (the owner's
   measure law: a capped measure owes an answer to "what is beside it?",
   and the only honest answers are content, symmetrical margin, or one
   centred column). This page is one column of two cards with nothing to
   stand beside it, so the frame gives up its cap for it and the column
   takes the middle. */
.app-main:has(> .wrap.first-run) { max-width: none; }
.app-main .wrap.first-run { max-width: 720px; margin-inline: auto; }

@media (min-width: 700px) {
  .wrap.first-run .tally li.tile .k { flex: 1 1 0; }
}

/* =====================================================================
   17. MOTION (ruling section 5)
   ===================================================================== */

/* 160ms fade and rise on a card that entered after a state change, rows
   staggered 40ms, and 150ms colour and transform on a control. Nothing
   loops, nothing pings, nothing shines, and nothing moves that the
   reader did not touch. The nav, the tabs and the table rows are used
   tens of times a day and are not animated at all. */
@media (prefers-reduced-motion: no-preference) {
  .enter { animation: app-rise var(--dur-2) var(--ease) both; }
  .enter .act-row { animation: app-rise var(--dur-2) var(--ease) both; }
  .enter .act-row:nth-child(2) { animation-delay: 40ms; }
  .enter .act-row:nth-child(3) { animation-delay: 80ms; }
  .enter .act-row:nth-child(4) { animation-delay: 120ms; }
  .toast { animation: app-rise var(--dur-2) var(--ease) both; }

  @keyframes app-rise {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: none; }
  }

  /* The skeleton's shimmer, and it is the one loop in the product: it
     runs only while a row has no data in it, and it stops the moment
     the row arrives. */
  .skeleton-row span { background: linear-gradient(90deg, var(--band) 0 40%, var(--bg) 50%, var(--band) 60% 100%); background-size: 300% 100%; animation: app-shimmer 1.4s linear infinite; }

  @keyframes app-shimmer {
    from { background-position: 100% 0; }
    to { background-position: -100% 0; }
  }
}

@media (prefers-reduced-motion: reduce) {
  .answers .caret,
  .head-fold > summary .caret,
  .act-chev,
  .app-shell,
  .app-shell .app-nav details > summary .chevron { transition: none; }
}

/* =====================================================================
   18. THE PHONE, 390 (ruling section 2)
   ===================================================================== */

/* THE FLOATING TAB BAR. No sidebar at all under 900: five destinations
   in a 56px bar inset 16px at a 14px radius, which floats, so it takes
   the one shadow. The page above it keeps 96px of room so the last row
   of a table is never under the bar. */
.tabbar { display: none; }

@media (max-width: 900px) {
  .app-shell { grid-template-columns: minmax(0, 1fr); background: none; min-height: 0; }
  .app-side { display: none; }
  .app-main { padding: var(--s4) var(--s4) 96px; }

  .tabbar {
    position: fixed;
    z-index: var(--z-menu);
    left: var(--s4);
    right: var(--s4);
    bottom: var(--s4);
    height: 56px;
    display: flex;
    justify-content: space-around;
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 14px;
    box-shadow: var(--shadow-2);
  }

  .tabbar a { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; flex: 1 1 0; min-width: 0; height: 100%; font-size: var(--fs-mini); line-height: 1.1; color: var(--ink-2); text-decoration: none; }
  .tabbar a:hover { text-decoration: none; }
  .tabbar a[aria-current="page"] { color: var(--ink); font-weight: 600; }
  .tabbar .ico { flex: none; }

  /* THE TOP BAR KEEPS THE MARK, THE CRUMB AND THE AVATAR, and nothing
     else: at 390 anything more overflows, which is one of the defects
     the mockup's research table refuses by name. */
  .app-top { padding: 0 var(--s3) 0 var(--s4); }
  .app-top .extra { display: none; }

  /* THE HEAD STACKS AND THE STRIP TAKES THE WIDTH. */
  .app-head { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .app-head h1 { font-size: 26px; }
  .app-head .weeks { max-width: none; justify-self: stretch; }
  .weeks-cols { height: 64px; }

  /* THE RAIL FALLS UNDER THE TABLE IT BELONGS TO. */
  .with-rail { grid-template-columns: minmax(0, 1fr); }

  /* A TABLE SCROLLS IN ITS OWN BOX WITH AN EDGE FADE, and never wraps a
     cell: a table that scrolls has said it is wider than the screen, and
     a wrapped cell in a scrolling box is unreadable twice over. */
  .card-table { position: relative; }
  .card-table .table { min-width: 560px; }
  .card-table .table td,
  .card-table .table th { white-space: nowrap; }

  .card-table::after,
  .tabs::after {
    content: "";
    position: sticky;
    top: 0;
    right: 0;
    float: right;
    width: 28px;
    height: 100%;
    margin-left: -28px;
    background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--surface));
    pointer-events: none;
  }

  /* THE TAB STRIP SCROLLS TOO, with the same fade on the paper ground. */
  .tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs::after { background: linear-gradient(to right, rgba(250, 252, 254, 0), var(--bg)); }

  /* THE ANSWERS TABLE STACKS RATHER THAN SCROLLING THE PAGE: the column
     strip goes, the question takes the full width and the verdict sits
     under it. Three cells of 132 and 84 cannot share 390 with a
     question. */
  .answers .cols { display: none; }

  details.row > summary {
    grid-template-columns: minmax(0, 1fr) 24px;
    grid-template-rows: auto auto;
    row-gap: 2px;
  }

  details.row .q { grid-column: 1; }
  details.row .pill { grid-column: 1; }
  details.row .when { display: none; }
  .answers .caret { grid-row: 1 / span 2; grid-column: 2; align-self: center; }

  /* THE TIME AFTER THE PILL IS THE WEEK PAGE'S, AND ONLY ITS (phase 5p
     T3b). The overview retired its When column; the week page's question
     cards wear `.answers-group`, and there the time is the fact that
     tells three repeats of one question apart. */
  .answers-group details.row summary .pill::after { content: " " attr(data-when); color: var(--ink-2); font-weight: 400; }

  .answers .answer { margin-left: var(--s3); margin-right: var(--s3); }
  .answers .grp { flex-wrap: wrap; }

  .answers .grp,
  .answers .sum,
  .answers .cols,
  details.row > summary,
  .card-rows .rest-fold > summary,
  .desk-row,
  .desk-group,
  .skeleton-row { padding-left: var(--s4); padding-right: var(--s4); }

  .card-table .table th:first-child,
  .card-table .table td:first-child { padding-left: var(--s4); }
  .card-table .table th:last-child,
  .card-table .table td:last-child { padding-right: var(--s4); }
  .task > div { padding-left: var(--s4); padding-right: var(--s4); }

  /* THE TITLE ROW AND THE ZERO ROW STACK. A card head's actions wrap
     under the title rather than squeezing it. */
  .title-row { grid-template-columns: minmax(0, 1fr); }
  .title-row .title-actions { grid-column: 1; }
  .zero { grid-template-columns: minmax(0, 1fr); gap: var(--s1); }
  .act-row { grid-template-columns: 22px minmax(0, 1fr); grid-template-rows: auto auto; }
  .act-row .ring { grid-row: 1 / span 2; }
  .act-row .min,
  .act-row .door { grid-column: 2; }
  .codes { grid-template-columns: minmax(0, 1fr); }
  .guide .two { grid-template-columns: minmax(0, 1fr); }
}

/* =====================================================================
   ==== P6R-S1 ====

   STAGE 1, THE CHECK TO PLAN MACHINE (T1.8). The three setup screens,
   the add-a-business page, the checkout's dated block and its plan facts
   strip, the post payment sequence and the overview's setup ribbon.

   IT IS APPENDED AS ITS OWN REGION, not edited into the baseline above:
   sections 1 to 18 are the T0.6 skin and a later stage adds to them
   rather than rewriting them. Two baseline behaviours ARE overruled here
   and both are named where they happen: the workspace's zebra, which
   struck the estimate ledger's total row, and site.css's half opacity on
   a disabled button, which left a white label on mid grey.

   THE DESIGN SOURCE is design/mockups/phase6r/s1/parts/s1.css and the D0
   sheet it sits on (parts/skin.css, which is app-wizard-step2.html's
   style block byte for byte), translated onto the shipped class names.
   Colour, face, size, space, radius and ease are asked for by TOKEN
   NAME, as everything above is.

   THE CARD MODEL IS THE SHIPPED ONE, AND THAT IS THE ONE TRANSLATION
   EVERY BLOCK HERE MAKES. The mockup's card carries no padding and each
   strip inside it pads itself; site.css's card pads --s5 and a strip
   bleeds back out with negative margins, which is exactly how .card-foot
   and .card-table already reach the card's edges. So .prov-status, .qrow,
   .qadd, .price-line, .lines and .facts.six bleed by the card's own
   inset and none of them restates it.
   ===================================================================== */

/* ---------------------------------------------------------------------
   S1.1 THE WIZARD'S CHROME: the place marker, the two columns, the row
   of actions under them.
   --------------------------------------------------------------------- */

/* x-app.steps. A three segment track and the count beside it. The track
   is decoration and is aria-hidden in the component; the sentence next
   to it carries the fact, in the measured face, as a COUNT and never as
   a percentage. */
.steps { display: flex; align-items: center; gap: var(--s3); margin: 0 0 var(--gap); }
.steps .track { display: flex; flex: 1; gap: 4px; max-width: 360px; }
.steps .track i { flex: 1; height: 4px; border-radius: 2px; background: var(--line); }
.steps .track i.done { background: var(--ink); }

.steps .lbl {
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink-2);
  white-space: nowrap;
}

/* THE WIZARD IS TWO REAL COLUMNS and that is this screen's answer to the
   owner's measure law: the work on the left, and beside it the alias
   panel, the facts ledger, the calendar or the facts card, none of which
   is filler. The rail holds at 280 until the main column needs the width
   and only then falls under it (the phone block at the foot of this
   region), because a rail that collapses early leaves the hole the law
   names. */
.wiz {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: var(--gap);
  align-items: start;
}

.wiz > div { min-width: 0; }

/* The work column's rhythm: one grid gap between the blocks, and the
   lede keeps its own 6px under the title. */
.wiz > div > * + * { margin-top: var(--gap); }
.wiz h1 { font-size: 24px; line-height: 1.2; font-weight: 600; letter-spacing: -0.01em; margin: 0; text-wrap: balance; }
.wiz .lede { font-size: var(--fs-body); line-height: var(--lh); color: var(--ink-2); margin: 6px 0 0; max-width: 70ch; }

/* THE ACTION ROW UNDER THE CARD: the second choice on the left, the one
   filled button on the right. It wraps, because the add page puts the
   dedupe sentence where the second choice would be. */
.wiz-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s3);
  margin-top: var(--gap);
}

.wiz-actions .meta { min-width: 0; margin: 0; }
.wiz-actions form { margin: 0; }

/* A BUTTON THAT CANNOT BE PRESSED SAYS SO IN ITS OWN INK. site.css draws
   every disabled control at half opacity, which on a filled ink button
   is a white label on mid grey. Step 3 disables its primary once today's
   baseline exists, and a button that still looks pressable is worse than
   one that says it cannot be pressed, so in the workspace the state is a
   quiet button that has lost its hover. */
.app-main .btn[disabled] {
  opacity: 1;
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink-2);
  cursor: default;
}

.app-main .btn[disabled]:active { transform: none; }

@media (hover: hover) and (pointer: fine) {
  .app-main .btn[disabled]:hover { background: var(--surface); border-color: var(--line); }
}

/* ---------------------------------------------------------------------
   S1.2 STEP 1 AND THE ADD PAGE: the field grid, the provenance line, the
   fixed fact and the state row.
   --------------------------------------------------------------------- */

/* Name and town on one row, category and country on the next, the typed
   line of business, the website and the language each on their own. */
.fgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4) var(--gap); }
.fgrid .span2 { grid-column: 1 / -1; }

/* x-app.provenance: where a value came from, under the field it came
   into. It is the difference between a form filled in FOR somebody and a
   form filled in behind their back. */
.prov { font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); margin: 6px 0 0; }

/* A FACT DRAWN WHERE AN INPUT WOULD BE (the language). SCOPED TO .field,
   NEVER WRITTEN BARE (fix batch F1): section 11 above already spells
   `.desk-row .state.fixed` as the blockers desk drawing the word "Fixed",
   and a bare rule would put a 34px bordered box around that word on every
   desk row. Both screens that use this put it inside a .field and the
   desk's span never is. */
.field .fixed {
  display: flex;
  align-items: center;
  min-height: var(--control);
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--band);
  font-size: var(--fs-body);
  color: var(--ink);
}

/* THE STATE ROW AT THE HEAD OF A CARD: the chip and its sentence, on the
   card's own top edge. It is .prov-status and NOT .status, for the same
   reason (fix batch F1): site.css spells .status as the blockers desk's
   status WORD, mono and uppercase and letterspaced and tinted by state,
   and step 1 inherited every bit of it, which set the whole sentence in
   small capitals and pushed the card to 412px at a 390 viewport. */
.prov-status {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  margin: calc(-1 * var(--s5)) calc(-1 * var(--s5)) 0;
  padding: var(--s3) var(--s5);
  border-bottom: 1px solid var(--line);
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--bg);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--ink-2);
}

.prov-status .chip { flex: none; margin-top: 1px; }
.prov-status + .fgrid { margin-top: var(--s5); }

/* ---------------------------------------------------------------------
   S1.3 THE ALIAS PANEL (x-app.alias-chips). What an answer has to carry
   to count as naming this business, edited as chips beside the name.
   --------------------------------------------------------------------- */
.aliases { display: flex; flex-wrap: wrap; gap: var(--s2); }

.alias {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: 28px;
  padding: 0 4px 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface);
  font-size: var(--fs-meta);
  line-height: 1.3;
  white-space: nowrap;
}

/* The name itself: it lives in the field on the left and is changed
   there, so the chip is on the panel ground and has no remove button. */
.alias.lock { padding-right: 10px; background: var(--band); }

/* The remove submit. It carries a character and not an icon (the icon set
   never puts a mark on a button), so it is centred by the grid rather
   than by a line height that depends on the face. */
.alias .x {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-2);
  font-family: inherit;
  font-size: var(--fs-body);
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}

.alias .x:hover { background: var(--band); color: var(--ink); }
.alias-add { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s2); margin-top: var(--s3); }

/* WHAT THE READING PROPOSED, AS SOMETHING TO KEEP OR DROP (fix batch F1).
   The website reading writes nothing to businesses.aliases; each proposal
   is a real ticked checkbox inside the chip and the save keeps the ones
   still ticked. Panel ground, so a proposal reads lighter than a name
   that is already ours. */
.alias.offer { gap: 6px; padding: 0 10px; background: var(--band); font-weight: 400; cursor: pointer; }
.alias.offer input[type="checkbox"] { width: 14px; height: 14px; margin: 0; flex: none; accent-color: var(--ink); }

/* ---------------------------------------------------------------------
   S1.4 STEP 2: the question rows, the add-your-own row, the rest of the
   pack and the facts ledger's total.
   --------------------------------------------------------------------- */

/* ONE QUESTION IS ONE ROW: the tick, the sentence the sampler will send,
   and which question it is. The rows bleed to the card's edges so a
   hairline runs the full width of the card, as the answers table's do.

   THE NAME IS TAKEN ONCE MORE, IN THE OTHER SHEET: site.css spells .qrow
   as the /how page's numbered prompt list (two columns, 24px for the
   number). No page loads both objects, the workspace's sheet is read
   second, and the meaning here is the wizard's. */
.qrow {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr) auto;
  gap: var(--s3);
  align-items: start;
  margin: 0 calc(-1 * var(--s5));
  padding: 10px var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.qrow:first-child { border-top: 0; }
.prov-status + .qrow { border-top: 0; }
.qrow > input[type="checkbox"] { width: 16px; height: 16px; margin: 3px 0 0; accent-color: var(--ink); }

/* THE PLACEHOLDER WHERE A ROW HAS NO TICK. A question past the plan's cut
   and a question waiting for a person have nothing to decide, so they
   have no box; the column stays open so every question on the screen
   keeps one left edge. */
.qrow .qmark { width: 16px; height: 16px; }
.qrow .qt { display: block; font-size: var(--fs-body); font-weight: 400; line-height: var(--lh); margin: 0; }
label.qt { cursor: pointer; }
.qrow .qm { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) 10px; margin-top: 2px; }
.qrow details { font-size: var(--fs-meta); color: var(--ink-2); }
.qrow details > summary { display: inline; list-style: none; cursor: pointer; font-size: var(--fs-meta); color: var(--accent); }
.qrow details > summary::-webkit-details-marker { display: none; }
.qrow details p { margin: 4px 0 0; max-width: 70ch; }

.qrow .pos {
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  line-height: 1.4;
  color: var(--ink-2);
  white-space: nowrap;
  margin-top: 3px;
}

/* A ROW THE PLAN DOES NOT REACH is a fact, not a control: reduced
   contrast in the secondary ink rather than in opacity, so the text stays
   readable, and nothing about it invites a press. */
.qrow.beyond .qt,
.qrow.beyond .qm,
.qrow.beyond .pos { color: var(--ink-2); }

/* THE ADD-YOUR-OWN ROW at the foot of the card. Its sentence and its
   refusal each take the whole row. */
.qadd {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s2);
  align-items: center;
  margin: 0 calc(-1 * var(--s5)) calc(-1 * var(--s5));
  padding: 14px var(--s5) var(--s4);
  border-top: 1px solid var(--line);
}

.qadd .meta,
.qadd .error-text { grid-column: 1 / -1; margin: 0; }

/* THE REST OF THE PACK, under the form: panel ground rather than white,
   so it reads as reference beside the white card that is the decision. */
.beyond-card { background: var(--band); }

/* The facts ledger's last row wears the sum row's 2px ink rule. */
.rail .kv.total { border-top: 2px solid var(--ink); font-weight: 600; }

/* ---------------------------------------------------------------------
   S1.5 STEP 3: the estimate ledger, its price row, the quiet block, the
   choice note and the FAQ in the main column.
   --------------------------------------------------------------------- */

/* ONE TABLE SPELLING IN THE WORKSPACE, and it is the shipped one: the
   ledger is a .card-table holding a .table, whose scroll box, card bleed,
   phone rules and edge fade section 18 already draws. .tbl is admin.css's
   name for the same object and the app never loads that sheet. The sum
   row is site.css's `tr.total`, which already carries the 2px ink rule at
   weight 600; the ONE thing it did not carry is the card ground, because
   the workspace zebra above strikes every even row and the total row is
   the row the whole screen exists for. */
.app-main .table tbody tr.total td { background: var(--surface); }

/* THE PRICE IS ITS OWN ROW UNDER THE TABLE, at body size and not in the
   13px foot: it is the second fact this screen exists to state and it is
   not a footnote. It closes the card, so it bleeds the way a foot does. */
.price-line {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s3);
  margin: 0 calc(-1 * var(--s5)) calc(-1 * var(--s5));
  padding: var(--s3) var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.price-line strong { font-weight: 600; }
.price-line a { font-size: var(--fs-meta); }

/* THE QUIET BLOCK, ON FREE ONLY: each paid week as a fact beside its
   price. Facts, never an offer, and no card is asked for on this page. */
.lines { margin: 0 calc(-1 * var(--s5)); padding: var(--s3) var(--s5) 14px; border-top: 1px solid var(--line); }
.lines + .card-foot { margin-top: 0; }
.line { display: flex; justify-content: space-between; gap: var(--s3); padding: 5px 0; font-size: var(--fs-body); }
.line .amt { font-family: var(--font-mono); font-size: var(--fs-meta); font-variant-numeric: tabular-nums; white-space: nowrap; }
.line .unit { color: var(--ink-2); }
.line.total { margin-top: 6px; padding-top: 10px; border-top: 1px solid var(--line); font-weight: 600; }
.line.total .amt { font-weight: 600; }

/* WHAT EACH BUTTON DOES, IN WORDS, DIRECTLY ABOVE THE PAIR. The two verbs
   are quoted inside one translated sentence, so the markup cannot mark
   them up without splitting a string a translator owns; the air above the
   paragraph is what ties it to the buttons under it. */
.choice-note { margin-top: var(--gap); max-width: 70ch; }

/* THE FAQ LIVES IN THE MAIN COLUMN ON STEP 3, not in the rail: in the
   rail the main column ended 230px above the rail's foot, which is the
   void the measure law forbids. As a card it carries the disclosures'
   own padding rather than the card's. */
.card.faq { padding: var(--s1) var(--s5) 6px; }

/* THE RAIL'S SECOND CARD IS GUIDANCE AND NOT A FACT, and the panel ground
   is what says so. */
.card.guide { background: var(--band); }

/* ---------------------------------------------------------------------
   S1.6 THE DATED LIST. One object, two positions: the calendar block in
   step 3's rail and the checkout's timeline in a full width card.
   --------------------------------------------------------------------- */
.when { list-style: none; margin: 0; padding: 0; }

/* THE DATE COLUMN HOLDS THE DATE (F2 fix batch, finding 3). It was a flat
   128px with a nowrap date inside it, and the shipped checkout prints a
   longer date than the mockup did: "7 Sep 2026, from 06:00 UTC" measures
   203px, so at 1400 and 1920 the Monday cell ran 75px past its track and
   printed 63px into the sentence beside it.

   WHY A WIDER TRACK AND NOT max-content. Every row is its own grid (each li
   is the grid container), so a max-content track is sized per row: the two
   short dates kept a 128px column and the long one took 203px, which left
   the sentences on a ragged left edge. One width for the list keeps the
   mockup's aligned pair of columns.

   AND THE DATE MAY WRAP IF IT EVER OUTGROWS IT. The weekday above it keeps
   nowrap; the date under it does not, so a longer date takes a second line
   inside its own cell instead of running over the sentence again. Under 900
   and inside a rail the rows are one column already, below. */
.when li {
  display: grid;
  grid-template-columns: 210px minmax(0, 1fr);
  gap: var(--s3);
  padding: 10px 0;
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.when li:first-child { border-top: 0; padding-top: 0; }
.when li:last-child { padding-bottom: 0; }

.when .d {
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  font-weight: 500;
  line-height: 1.6;
  color: var(--ink);
  white-space: nowrap;
}

/* EVERY `small` IN THIS REGION STATES ITS SIZE. The browser default is
   0.833em, which on 14px body type is 11.7px and under the ruling's 12px
   floor; the mockup harness caught exactly that on a date. */
.when .d small { display: block; font-size: var(--fs-meta); font-weight: 400; line-height: 1.5; color: var(--ink-2); white-space: normal; }
.when .t { min-width: 0; }
.when .t small { display: block; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* IN A 280px RAIL THE DATE SITS ON ONE LINE ABOVE ITS SENTENCE. The two
   column form collided at 13px in the measured face inside the rail, so
   the same markup takes one column there. */
.rail .when li { grid-template-columns: minmax(0, 1fr); gap: 2px; }
.rail .when .d small { display: inline; margin-left: var(--s2); }

/* ---------------------------------------------------------------------
   S1.7 THE ADD PAGE'S FACTS CARD.
   --------------------------------------------------------------------- */
.facts-list { list-style: none; margin: 0; padding: 0; }
.facts-list li { padding: var(--s2) 0; border-top: 1px solid var(--line); font-size: var(--fs-body); }
.facts-list li:first-child { border-top: 0; padding-top: 0; }
.facts-list li:last-child { padding-bottom: 0; }
.facts-list li small { display: block; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S1.8 THE CHECKOUT'S PLAN FACTS STRIP.
   --------------------------------------------------------------------- */

/* WHAT THE MONEY BUYS, AS A FOOT TO THE PRICE and not as another price
   row: panel ground, a hairline over it, and it closes the card.

   IT STAYS ONE COLUMN AT EVERY WIDTH. The mockup reads the strip as two
   rows of three, but the mockup's bill card is the wide left column of a
   `1fr 360px` head; the shipped checkout puts the price card in the 340px
   aside (section 15 above), and three cells of about 100px cannot hold
   "ChatGPT, Perplexity, Google AI Overviews" without cutting it. So the
   label sits over its value, one fact a row, hairline between, which is
   the strip's grammar at the width this page actually gives it. */
.facts.six {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  margin: var(--s4) calc(-1 * var(--s5)) calc(-1 * var(--s5));
  padding: var(--s2) var(--s5) var(--s3);
  border-top: 1px solid var(--line);
  border-radius: 0 0 9px 9px;
  background: var(--band);
}

.facts.six > div { display: block; padding: var(--s2) 0; border-top: 1px solid var(--line); }
.facts.six > div:first-child { border-top: 0; }
.facts.six dt { font-size: var(--fs-meta); color: var(--ink-2); }
.facts.six dd { font-size: var(--fs-meta); font-weight: 500; line-height: 1.5; }

/* ---------------------------------------------------------------------
   S1.9 THE POST PAYMENT SEQUENCE.
   --------------------------------------------------------------------- */

/* THREE DETERMINATE LINES THAT RESOLVE IN PLACE. The count is the motion:
   no spinner, nothing that pings, and the only animated property on the
   screen is the bar's width. Server rendered state is already correct
   with scripts off, so nothing here depends on the poll.

   THE RING IS THE ACTIVATION CARD'S RING (section 7: 22px, 1.5px
   hairline). One product draws one done mark, and the CSS request's 16px
   would have been a second size for the same object. */
.seq { list-style: none; margin: 0; padding: 0; }

.seq li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) auto;
  gap: var(--s3);
  align-items: start;
  padding: 10px 0;
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.seq li:first-child { border-top: 0; padding-top: 0; }
.seq li:last-child { padding-bottom: 0; }
.seq .ring { width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--line); display: grid; place-items: center; }
.seq li.done .ring { background: var(--ink); border-color: var(--ink); color: #ffffff; }
.seq li.now .ring { border-color: var(--ink); }
.seq li.now .ring::after { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--ink); }
.seq .t { min-width: 0; font-weight: 500; }
.seq .t small { display: block; font-size: var(--fs-meta); font-weight: 400; line-height: 1.5; color: var(--ink-2); }

/* A line that has not resolved reads as a fact about what comes next, not
   as a thing that is happening. */
.seq li.wait .t { color: var(--ink-2); font-weight: 400; }

/* The time the line resolved at. It is EMPTY on a line that has not
   resolved, so the column must not collapse the row when it is. */
.seq .at {
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  line-height: 1.55;
  color: var(--ink-2);
  text-align: right;
  white-space: nowrap;
}

/* The shipped determinate bar (section 5), under the third line's
   sentence rather than beside the ring. */
.seq .progress { grid-column: 2; margin-top: var(--s2); max-width: 100%; }

/* THE RECEIPT'S DATED PAIR IS A SENTENCE, NOT A FIGURE. site.css holds a
   .tally value on one line, because a figure that wraps is unreadable;
   the return page's "what happens next" rows put a whole sentence there
   and the longest of the three drew a 856px page against a 390 viewport
   (measured on /app/{business}/billing/return/{payment}). `.dated` is the
   same row kind section 12 already gives .method, .crawlers and .path:
   spoken type, wrapping, in the spoken face. */
.tally.dated li > span:last-child {
  white-space: normal;
  text-align: right;
  font-family: var(--font);
  font-variant-numeric: normal;
}

/* ---------------------------------------------------------------------
   S1.10 x-app.choice-cards: a native radio group drawn as cards.

   Same names and same values as the copy in site.css, because the
   component is one file used on both sides of the product: the claim card
   renders inside x-site.layout and the wizard renders inside the app
   shell. Nothing here is a script; the label wraps the input, so the card
   is the hit area, and the arrow keys, the focus ring and the required
   state stay the browser's.
   --------------------------------------------------------------------- */
fieldset.choice-set { display: grid; gap: var(--s2); border: 0; margin: 0; padding: 0; min-width: 0; }
.choice-set > legend { display: block; padding: 0; margin: 0 0 var(--s2); font-size: var(--fs-h3); font-weight: 600; line-height: 1.35; }
.choices { display: grid; gap: 10px; }
.choices.two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.choices.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.choice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: var(--s3) 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}

/* The radio is DRAWN AND NEVER HIDDEN. */
.choice input { width: 16px; height: 16px; margin-top: 2px; flex: none; accent-color: var(--ink); }

/* THE TICKED CARD IS A WEIGHT CHANGE, NOT A COLOUR WASH, so a card that
   is chosen and a card that is hovered can never be confused. */
.choice:has(input:checked) { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
.choice .t { display: block; font-size: var(--fs-body); font-weight: 500; line-height: 1.4; }
.choice .m { display: block; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); margin-top: 2px; }
.choice-set > .error-text { margin: 0; }

/* ---------------------------------------------------------------------
   S1.11 THE SETUP RIBBON ON THE OVERVIEW (x-app.setup-ribbon).
   --------------------------------------------------------------------- */

/* A plain white strip: the sentence, the wizard's own bar, one door. It
   is NOT the warm surface; on the overview that is still the activation
   card, and two warm objects in one column would be two things claiming
   to be the page's task. */
.ribbon {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s3) var(--s5);
  align-items: center;
  padding: var(--s3) var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* The strip's own gap spaces the bar; the bar's margin would space it
   twice. */
.ribbon .steps { margin: 0; }
.ribbon .r-label { min-width: 0; font-size: var(--fs-body); font-weight: 500; line-height: 1.4; }
.ribbon .r-label b { font-weight: 500; white-space: nowrap; }
.ribbon .r-label small { display: block; font-size: var(--fs-meta); font-weight: 400; line-height: 1.5; color: var(--ink-2); }
.ribbon form { margin: 0; }

/* ---------------------------------------------------------------------
   S1.12 THE PHONE, 390.
   --------------------------------------------------------------------- */
@media (max-width: 900px) {
  /* The rail falls under the work it belongs to, and the field grid
     becomes one column. */
  .wiz { grid-template-columns: minmax(0, 1fr); }
  .fgrid { grid-template-columns: minmax(0, 1fr); }
  .choices.two,
  .choices.three { grid-template-columns: minmax(0, 1fr); }
  .when li { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .when .d small { display: inline; margin-left: var(--s2); }
  .prov-status { flex-wrap: wrap; }

  /* The position moves UNDER the question rather than squeezing it: three
     columns cannot share 390 with a sentence. */
  .qrow { grid-template-columns: 16px minmax(0, 1fr); }
  .qrow .pos { grid-column: 2; margin-top: 0; }

  /* THE RIBBON IS ONE COLUMN AND THE DOOR GOES TO THE START OF IT. It
     must never be the reason the overview scrolls sideways. */
  .ribbon { grid-template-columns: minmax(0, 1fr); }
  .ribbon .btn,
  .ribbon form { justify-self: start; }

  /* THE WIZARD'S PRIMARY IS FIXED TO THE BOTTOM, and the wizard shows its
     own bar instead of the floating tab bar: two bars stacked at the foot
     of a 390 viewport is one of them covering the other. The frame keeps
     room for it, in place of the room it keeps for the tab bar. */
  .wiz-actions {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--z-menu);
    margin: 0;
    padding: var(--s3) var(--s4);
    background: var(--surface);
    border-top: 1px solid var(--line);
  }

  .wiz-actions .btn { flex: 1 1 auto; }
  .wiz-actions .meta { flex: 1 1 100%; }
  body:has(.wiz-actions) .tabbar { display: none; }
  body:has(.wiz-actions) .app-main { padding-bottom: 88px; }

  /* The bleeding strips are NOT re-inset here. site.css pads a card at
     --s5 at every width, so a strip that bleeds by the card's own inset
     bleeds by the same 24 on a phone as on a desk; re-insetting them to
     16 would have left an 8px shelf inside the card's border. */
  .steps .track { max-width: none; }
}

/* =====================================================================
   ==== P6R-S3 ====

   STAGE 3, THE ARTEFACT AN AGENCY FORWARDS (T3.7). The months archive,
   one month, and the reports and alerts settings page. The proof link
   page is public and its region is in design/tokens.css; the paper is
   inline in resources/views/reports/month.blade.php, because dompdf runs
   with remote fetching off and a linked stylesheet would produce an
   unstyled document.

   IT IS APPENDED AS ITS OWN REGION, not edited into the baseline above:
   sections 1 to 18 are the T0.6 skin, P6R-S1 is stage 1, and a later
   stage adds to them rather than rewriting them.

   THE DESIGN SOURCE is design/mockups/phase6r/s3/parts/s3.css and the D0
   sheet it sits on, translated onto the shipped class names, with every
   request in design/css-requests-phase6r-s3.md landed. Colour, face,
   size, space, radius and ease are asked for by TOKEN NAME, as
   everything above is.

   THE CARD MODEL IS THE SHIPPED ONE (the S1 translation, kept). The
   mockup's card carries no padding and each strip inside it pads itself;
   site.css pads a card at --s5 and a strip bleeds back out with negative
   margins, exactly as .card-foot and .card-table already do. So .cad,
   .rcpt-add, .cap, .budget and the alerts list bleed by the card's own
   inset, none of them restates it, and none of them is re-inset at 390:
   the card pads 24 at every width and a 16px bleed would leave an 8px
   shelf inside its border.
   ===================================================================== */

/* ---------------------------------------------------------------------
   S3.1 THE CARD COLUMN BESIDE A RAIL, AND A SECTION HEAD OVER CARDS.
   --------------------------------------------------------------------- */

/* .stack IS TAKEN TWICE AND BOTH SPELLINGS SHIP. Section 16 owns the bare
   name for a TABLE that stacks on a phone (`<table class="table stack">`),
   which is a width and nothing else. The two month pages write
   `<div class="stack">` inside .with-rail for the card column, which is
   the D0 sheet's own meaning of the word, so the column is drawn as the
   CHILD of the rail row it is: a table can never be one. */
.with-rail > .stack { display: flex; flex-direction: column; gap: var(--s5); min-width: 0; }

/* A SECTION TITLE OVER A GROUP OF CARDS THAT IS NOT ITSELF A CARD. The
   card title's size and its description, because a heading over two
   cards is not louder than the cards under it. Inside a 24px stack the
   negative bottom pulls the head onto the cards it names. */
.sec-head { margin: 30px 0 12px; }
.sec-head h2 { font-size: var(--fs-h3); font-weight: 600; line-height: 1.35; margin: 0; }
.sec-head p { font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); margin: 2px 0 0; }
.stack > .sec-head { margin: 6px 0 -12px; }

/* ---------------------------------------------------------------------
   S3.2 THE MONTHS ARCHIVE: one row a month, the dot strip of its
   Mondays, and the files that take the record away.
   --------------------------------------------------------------------- */

/* The scroll box is the containing block for its own sr-only text, so the
   hidden sentence that reads the dot strip out cannot widen the page past
   the box that clips the table (the mockup caught this at 390). */
.card-table.months { position: relative; }
.card-table.months .table td { height: 44px; }

/* THE MONTH CELL IS THE ROW'S IDENTITY and the only one that wraps: the
   month as the door, its state under it in the description size. The
   link keeps the shipped link colour rather than the mockup's ink,
   because every other row link in this workspace is drawn in the accent
   and a link that is ink with no underline is a link nothing but colour
   announces. */
.card-table.months td.mon a { font-weight: 500; }
.card-table.months td.mon small { display: block; font-size: var(--fs-meta); color: var(--ink-2); }

/* THE DOORS CELL: two quiet 30px buttons on one line, right aligned, and
   at 390 they wrap under each other rather than clipping. */
.card-table.months td.doors { text-align: right; }
.card-table.months td.doors .btn { margin-left: var(--s2); }

/* THE MONTH THAT HAS NOT CLOSED IS A DIFFERENT KIND OF ROW, not a state:
   the panel ground under it and no colour anywhere, because an open
   month is not a warning. It outruns the workspace zebra on purpose; a
   tinted row that is also striped reads as neither. */
.app-main .card-table.months tr.open td { background: var(--band); }

/* THE DOT STRIP OF A MONTH'S MONDAYS. One dot a Monday: filled when it
   was sampled, a ring when it is still to come, and a dash for a Monday
   that passed with no run of ours, because a gap is not a zero and must
   not read as one. The current week's Monday is the accent, and that is
   the only accent on the page. */
.wdots { display: inline-flex; gap: 5px; align-items: center; vertical-align: middle; }
.wdots i { width: 10px; height: 10px; border-radius: 50%; background: var(--ink); }
.wdots i.todo { background: transparent; border: 1.5px solid var(--line); }
.wdots i.miss { width: 10px; height: 2px; border-radius: 1px; background: var(--line); }
.wdots i.now { background: var(--accent); border-color: var(--accent); }

/* THE DOWNLOADS RAIL: a file a row, the one sentence that says what one
   of its rows is under it. The mockup's leading file glyph is not drawn
   (design rule 3.4, and the shipped icon set has no such mark), so the
   name is the door and the grain is the fact. */
.file-row { display: grid; gap: 2px; padding: 10px 0; border-top: 1px solid var(--line); font-size: var(--fs-body); }
.file-row:first-child { border-top: 0; padding-top: 0; }
.file-row:last-child { padding-bottom: 0; }
.file-row .fname { font-weight: 500; overflow-wrap: anywhere; }

/* A FILE WHOSE ROUTE DOES NOT EXIST YET IS A FACT, NOT A DOOR: the
   secondary ink and no underline, so nothing on it invites a press. */
.file-row .fname.sec { color: var(--ink-2); text-decoration: none; }
.file-row .grain { font-size: var(--fs-meta); line-height: 1.45; color: var(--ink-2); }
.file-row .grain .mono { color: var(--ink); }

/* THE PROOF LINK'S PREFIX, on the archive and on the settings page: the
   first characters in the measured face, and the rest of the token drawn
   as the hairline it is, because it is shown once and never again. */
.token {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}

.token .dim { color: var(--ink-2); }

/* WHAT A LIVE PROOF LINK SHOWS. The mockup draws a tick in front of each
   item; the shipped set has no such mark and design rule 3.4 keeps marks
   out of a list, so an item is its sentence.

   AN ITEM THIS LINK DOES NOT SHOW IS STRUCK THROUGH as well as dropped
   to the secondary ink (the second cue the request asks T3.7 for): the
   state has to survive colour being taken away, and a rule through the
   words is the one cue that needs no glyph and no second sentence. */
.scope { list-style: none; margin: var(--s2) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: var(--fs-meta); color: var(--ink); }
.scope li { display: inline-flex; align-items: baseline; gap: 6px; margin: 0; }
.scope li.off { color: var(--ink-2); text-decoration: line-through; text-decoration-thickness: 1px; }

/* ---------------------------------------------------------------------
   S3.3 ONE MONTH: the head with its frozen card, the amendment, the
   quoted answers, the blockers and the method.
   --------------------------------------------------------------------- */

/* THE HEAD WITH A CARD IN ITS RIGHT COLUMN INSTEAD OF THE STRIP. The
   same two columns, aligned to the START rather than the end because the
   card is taller than the sentence, and the receipt sentence at 26 and
   not 30 (D3 decision 4: it names three assistants and the business and
   runs to three lines at 30 in a 1fr column beside a 360px card). */
.app-head.month-head { align-items: start; }
.app-head.month-head h1 { font-size: 26px; line-height: 1.25; }
.app-head.month-head .interval { margin-top: 10px; font-size: var(--fs-h3); }

/* The right column: the action row over the frozen card, 12px apart,
   ending flush with the card's edge. Under 900 it falls under the copy,
   in document order. */
.app-head .head-right { display: flex; flex-direction: column; gap: 12px; align-items: stretch; min-width: 0; }
.app-head .head-right .head-act { margin: 0; justify-content: flex-end; }

/* THE LEDGER ROW, IN WHATEVER CARD IT IS WRITTEN. Section 10 draws .kv
   inside .rail, and this region drew it twice more, for the frozen card
   and for the method's facts column. A .kv in a plain card had no rules
   at all: the archive's "who gets the report" printed its label and its
   value with no space between them ("s3@harness.localowner", measured at
   1400), and so did the proof card on the settings page, the approvals
   ledger and the peers facts. One object is drawn once, so the row is
   written here for every card in the workspace and the scoped copies
   above stand only where they change something.

   THE SUM ROW COMES WITH IT: `.kv.total` is the 2px ink rule the sheet
   puts over any total, which section 16 had inside the rail alone. */
.app-main .kv { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; font-size: var(--fs-meta); border-top: 1px solid var(--line); }
.app-main .kv:first-child { border-top: 0; padding-top: 0; }
.app-main .kv span { min-width: 0; }
.app-main .kv b { font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }
.app-main .kv.total { border-top: 2px solid var(--ink); font-weight: 600; }

/* THE FROZEN FIGURES CARD reads as a ledger and not as a second page
   section, so it takes the rail's own sizing and the rail's own rows.
   Section 10 writes those rows as `.rail .kv` and they have to reach
   this card too. */
.card.frozen { padding: var(--s4); }
.card.frozen .card-head { margin: 0 0 10px; }
.card.frozen .kv { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; font-size: var(--fs-meta); border-top: 1px solid var(--line); }
.card.frozen .card-head + .kv { border-top: 0; padding-top: 0; }
.card.frozen .kv span { min-width: 0; }
.card.frozen .kv b { font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The sha prefix under the rows: it is an identifier, so it never wraps
   and never leaves the measured face. */
.card.frozen .stamp { font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.5; color: var(--ink-2); margin: var(--s2) 0 0; }

/* THE AMENDMENT, IN THE OPEN. Panel ground under a 2px ink rule, the
   warn chip on the first line, and the figures as first closed readable
   in place. It is the changelog entry's grammar with a fixed lead in,
   which is what an amendment is. */
.amend {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s1) 14px;
  align-items: start;
  padding: var(--s3) var(--s4) 14px;
  background: var(--band);
  border: 1px solid var(--line);
  border-top: 2px solid var(--ink);
  border-radius: var(--radius);
  font-size: var(--fs-body);
}

.amend > p { margin: 0; }
.amend > p + p { margin-top: var(--s2); grid-column: 2; }
.amend .chip { margin-top: 2px; }
.amend .was { grid-column: 2; font-size: var(--fs-meta); color: var(--ink-2); }
.amend .was .mono { color: var(--ink); }

/* AN AMENDED CELL KEEPS THE FIGURE IT WAS FIRST CLOSED WITH, under the
   current one, in the measured face at the mini size. */
.app-main .table td .was { display: block; font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.4; color: var(--ink-2); white-space: nowrap; }

/* THE TWO QUOTED ANSWERS SIT INSIDE THE MAIN COLUMN, beside the rail, so
   the two columns of the page end together (D3 decision 3). Each answer
   is already inside a card here, so it drops the side margins section 9
   gives it inside the answers table. */
.excerpts .answer { margin: 0; }

/* And one answer takes the whole row, for the same reason: a month with a
   single sampled Monday has one excerpt to show, and half a row of nothing
   beside it is the hole the owner's measure law names. */
.excerpts:has(> :only-child) { grid-template-columns: minmax(0, 1fr); }

/* ONE BLOCKER, ONE ROW: the title with the date right aligned, the
   evidence and its minutes under them across both columns. The state is
   .done and never .fixed, which S1 owns for the fixed fact drawn where
   an input would be. */
.blk-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 12px;
  align-items: baseline;
  padding: 10px var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.blk-row:first-child { border-top: 0; }
.blk-row .t { font-weight: 500; }
.blk-row .ev { grid-column: 1; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }
.blk-row .ev .mono { color: var(--ink); }
.blk-row .when { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink-2); white-space: nowrap; }
.blk-row.done .t { color: var(--ink-2); font-weight: 400; }
.blk-row.done .when { color: var(--ok); }

/* THE METHOD, AS TWO COLUMNS: the sentences at a reading measure with
   the fixed facts of the reading beside them, so the prose is never
   alone at 1920. The ledger's rows are the rail's, here too. */
.method { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--gap) 40px; align-items: start; }
.method p { font-size: var(--fs-body); margin: 0; }
.method p + p { margin-top: 12px; }
.method .mono { color: var(--ink); }
.method .kv { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; font-size: var(--fs-meta); border-top: 1px solid var(--line); }
.method .kv:first-child { border-top: 0; padding-top: 0; }
.method .kv span { min-width: 0; }
.method .kv b { font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------------------------------------------------------------------
   S3.4 REPORTS AND ALERTS: cadence, recipients, the proof link, the
   alert kinds with their budget, and the brand card.
   --------------------------------------------------------------------- */

/* A CADENCE ROW IS A CONTROL BESIDE ITS SENTENCE, twice: the segmented
   pair beside the next date, then Send now beside its note. */
.cad {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s2) var(--gap);
  align-items: center;
  margin: 0 calc(-1 * var(--s5));
  padding: var(--s4) var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.cad form { margin: 0; }
.cad .next { margin: 0; font-size: var(--fs-meta); color: var(--ink-2); }
.cad .next .mono { color: var(--ink); }

/* ONE ADDRESS, ONE ROW: the address in the measured face with the day it
   was added under it, its state, and its doors. The mockup's leading
   account glyph is not drawn (design rule 3.4); the owner's row says
   "owner" in a chip and "the account owner, always" under the address,
   which is the fact the lock was standing in for. */
.rcpt {
  display: grid;
  /* THE ADDRESS KEEPS A FLOOR AND THE STATE GIVES WAY. An auto track takes
     its max-content while the 1fr beside it shrinks, so a state chip
     carrying a whole sentence squeezed the address column to nothing and
     an address that may break anywhere broke one character to a line (a
     700px column of letters, measured on the settings page at 1400).
     minmax(0, auto) lets the chip wrap instead. */
  grid-template-columns: minmax(14ch, 1fr) minmax(0, auto) auto;
  gap: 2px 12px;
  align-items: center;
  min-height: 44px;
  padding: var(--s2) var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.rcpt:first-child { border-top: 0; }

.rcpt .addr { font-family: var(--font-mono); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink); overflow-wrap: anywhere; }
.rcpt .addr small { display: block; font-family: var(--font); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }
/* THE STATE CHIP WRAPS ON THIS ROW, and only on this row. Every other chip
   in the product is two or three words and site.css holds it on one line;
   the pending state here names the address it is waiting on, which measured
   480px inside a 390 viewport and pushed the page to 521 (caught by the
   harness at 390 and again at 1400). A chip carrying a sentence is a
   sentence. */
.rcpt .st { min-width: 0; }
.rcpt .st .chip { white-space: normal; overflow-wrap: anywhere; align-items: baseline; }
.rcpt .st .chip::before { margin-top: 5px; }

/* The doors are two small FORMS side by side, not two links: an address
   removed by a GET is a list edited by a browser prefetch. */
.rcpt .do { display: flex; gap: var(--s2); white-space: nowrap; }
.rcpt .do form { margin: 0; }
.rcpt .do .btn { height: 28px; padding: 0 10px; }

/* THE ADD FORM: the field and its verb on one line, the button level
   with the input's own bottom edge. */
.rcpt-add {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s2);
  align-items: end;
  margin: 0 calc(-1 * var(--s5));
  padding: 14px var(--s5) 6px;
  border-top: 1px solid var(--line);
}

.rcpt-add .field { min-width: 0; margin: 0; }

/* The cap and the double opt in promise under the form. It carries no
   rule of its own, so it does not bleed: a strip with nothing to draw to
   the card's edge would land in exactly the same place either way. */
.cap { margin: var(--s2) 0 0; font-size: var(--fs-meta); color: var(--ink-2); }
.cap .mono { color: var(--ink); }

/* THE ALERT KINDS ON THE SHIPPED TOGGLE LIST, bleeding to the card's
   edges so its hairlines line up with the budget row under it. It is
   scoped to the list INSIDE A FORM: the account's notifications page
   draws the same list with a switch that saves on its own, outside any
   form, and that page keeps section 16's inset list. */
.app-main form > .toggle-list { margin: 0 calc(-1 * var(--s5)); padding: 0 var(--s5); border-top: 1px solid var(--line); }
.app-main form > .toggle-list > li:last-child { border-bottom: 0; }

/* THE BUDGET ROW: the control between the two halves of its sentence,
   with what the month has spent beside it. */
.budget {
  display: flex;
  gap: var(--s4);
  align-items: center;
  flex-wrap: wrap;
  margin: 0 calc(-1 * var(--s5));
  padding: 14px var(--s5) var(--s4);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.budget .used { font-size: var(--fs-meta); color: var(--ink-2); }
.budget .used .mono { color: var(--ink); }

/* THE THIRD KIND OF SEGMENT CELL. site.css draws `.seg a` and
   `.seg button`; the budget is four RADIOS in one form, because it is
   saved with the switches beside it in one press and a link would be a
   GET that changes something. The input is taken out of the picture and
   the label carries the ring, so the control is still reachable and
   still visible with a keyboard. */
.seg label {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 10px;
  border-radius: var(--radius-xs);
  color: var(--ink-2);
  font-size: var(--fs-meta);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
}

.seg label input[type="radio"] { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.seg label:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }
.seg label .mono { font-size: var(--fs-meta); }

@media (hover: hover) and (pointer: fine) {
  .seg label:hover { color: var(--ink); }
}

/* THE BRAND CARD. The logo slot is the hatch at 64px, empty until an
   owner uploads one; with a file stored the border goes solid and the
   ground becomes the panel, so the two states differ in more than a
   word. */
.logo-slot { display: flex; align-items: center; gap: 14px; }

.logo-slot .well {
  width: 64px;
  height: 64px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: repeating-linear-gradient(45deg, rgba(28, 38, 48, 0.06) 0 1px, transparent 1px 8px);
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1;
  text-align: center;
  color: var(--ink-2);
}

.logo-slot .well.has { border-style: solid; background: var(--band); font-size: var(--fs-meta); color: var(--ink); }
.logo-slot > div { min-width: 0; }

/* The accent field. The well beside the box is not drawn until the value
   has been validated on the server, so today the rule holds the capped
   input and nothing else. */
.swatch { display: flex; align-items: center; gap: 10px; }
.swatch .input { max-width: 140px; }

/* A SELECT IN THE MEASURED FACE CARRIES ITS OPTIONS WITH IT: a list of
   figures drawn in the spoken face under a measured box is the same
   number in two faces. */
.input.mono option { font-family: var(--font-mono); }

/* WHAT NO PLAN REMOVES, PRINTED WHERE THE FORM IS FILLED IN. */
.rule-box {
  margin: 0 0 2px;
  padding: var(--s3) 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--band);
  font-size: var(--fs-meta);
  color: var(--ink);
}

.rule-box .mini { margin: 0 0 var(--s1); }
.rule-box p { margin: 0; }

/* ---------------------------------------------------------------------
   S3.5 THE PHONE, 390. Every grid in this region goes to one column;
   nothing is re-inset, because the card pads 24 at every width.
   --------------------------------------------------------------------- */
@media (max-width: 900px) {
  .app-head.month-head h1 { font-size: 22px; }
  .app-head .head-right { align-items: stretch; }
  .app-head .head-right .head-act { justify-content: flex-start; }

  .amend { grid-template-columns: minmax(0, 1fr); }
  .amend > p + p,
  .amend .was { grid-column: 1; }

  .blk-row { grid-template-columns: minmax(0, 1fr); }
  .method { grid-template-columns: minmax(0, 1fr); }

  .cad { grid-template-columns: minmax(0, 1fr); }
  .rcpt { grid-template-columns: minmax(0, 1fr); }
  .rcpt .st,
  .rcpt .do { grid-column: 1; justify-self: start; }
  .rcpt-add { grid-template-columns: minmax(0, 1fr); }
  .card-table.months td.doors .btn { margin-left: var(--s1); }
}

/* ---------------------------------------------------------------------
   S3.6 A STACKED TABLE IS NOT A SCROLLING ONE (caught by the harness at
   390 on the archive and on the sampled weeks ledger).

   site.css turns `table.table.stack` into label and value rows under 640,
   and section 18 of this sheet holds every table in a 560px scroll box
   with its cells on one line from 900 down. Both rules were right on
   their own and wrong together: the stacked rows were laid out inside a
   560px table in a 342px box, so every value sat off the right edge of
   the card and the archive read as a column of labels with nothing
   beside them. A table that has said it stacks has said it does not
   scroll.
   --------------------------------------------------------------------- */
@media (max-width: 639px) {
  .card-table .table.stack { min-width: 0; }

  .card-table .table.stack td,
  .card-table .table.stack th { white-space: normal; }

  /* And with nothing to scroll to, the edge fade has nothing to fade. */
  .card-table:has(> .table.stack)::after { content: none; }

  /* The month cell is the row's identity: the month over its state,
     rather than the two of them at opposite ends of the row. */
  .card-table.months .table.stack td.mon { display: block; }
}

/* =====================================================================
   ==== P6R-S2A ====

   T2.12, the App reference sheet's four corrections. The measured source
   is design/mockups/phase6r/s7/APP-SHEET.md, and the corrections come
   from the owner's review of 2026-09-05 (docs/REWORK-PLAN.md section 12
   items 5, 6 and 7). Four things and nothing else:

     1. Sentences and verbatim answer prose are Satoshi at 14/1.55. Geist
        keeps the headings, the nav, the buttons, the table heads and every
        control label; Geist Mono is spent on figures, ids, timestamps,
        model strings and code, and on nothing else.
     2. The input carries its own boundary again. The rule itself is in
        site.css, where every sheet reads it; this region only says what
        the workspace adds on top of it.
     3. One filled button per DECISION REGION, not one per page. The lint
        counts per [data-region]; the views mark their regions.
     4. Motion is stated as normal motion first and the reduced-motion
        alternative second.

   IT IS APPENDED AS ITS OWN REGION and edits nothing above it. T2.11 owns
   the Stage 2 objects (the metric cards, the next action card, the setup
   disclosure) and lands after this in its own banner.
   ===================================================================== */

/* ---------------------------------------------------------------------
   S2A.1 THE FACE SPLIT (APP-SHEET.md section 1)

   WHY A SENTENCE CHANGES FACE. The review measured the reference's
   workspace and found Satoshi doing the descriptive copy while a grotesk
   does the chrome, and it named our own "all verbatim prose in Geist
   Mono" as the defect worth fixing: "Mono helps identifiers and aligned
   numbers; long customer-facing prose deserves a readability comparison."
   A vendor's answer is three or four lines of ordinary English, and set
   in a monospace it reads as a log entry rather than as what somebody was
   told about this business.

   THE DEFAULT FLIPS AND THE CHROME IS THE EXCEPTION LIST, because there
   are far more sentences in the workspace than there are labels, and a
   sentence that was missed reads better in Satoshi than a label that was
   missed reads in Geist.
   --------------------------------------------------------------------- */
body.app { font-family: var(--font-pub); }

/* Geist: everything that is a label, a heading, a control or a column
   head. A word here is read at a glance and never as prose. */
body.app :is(
  h1, h2, h3,
  .btn, .btn-link,
  .app-nav a, .app-nav summary,
  .table th,
  .seg, .seg a, .seg button,
  .tabs a, .tabbar a,
  .crumb, .chip, .pill,
  .field label, .form legend,
  .card-title h1, .card-title h2, .card-title h3,
  .title-row h1, .title-row h2,
  .empty-state h2,
  .act-title, .act-row .name,
  .grp .g-name, .row .q,
  .desk-row .t, .joined-row .t,
  .zero .who,
  .side-foot strong
) { font-family: var(--font); }

/* THE VERBATIM ANSWER IS SATOSHI NOW, at 14/1.55 (APP-SHEET.md section 1,
   "Verbatim answer prose ... Was Geist Mono 14 / 1.6"). Nothing else about
   it moves: it is not tabular, it is not edited, and the one tag inside it
   is still <mark>. */
.answers .answer .a-body {
  font-family: var(--font-pub);
  line-height: 1.55;
}

/* The measured face keeps what it is for. This restates the rule rather
   than trusting the default, because the default is Satoshi now and a
   figure that fell back to it would line up with nothing. */
body.app :is(.mono, .num, .fig, code, kbd, samp, pre) { font-family: var(--font-mono); }

/* ---------------------------------------------------------------------
   S2A.2 THE INPUT BOUNDARY (APP-SHEET.md section 3)

   The boundary itself is site.css's, which every sheet reads: 1px on
   --edge (#7c8998, 3.6:1 on the white field), --edge-hover on hover, the
   2px ink ring on focus. What the workspace adds is the face on the
   value, because a value a person typed is a sentence and not a label,
   and the two states the acceptance matrix asks about.
   --------------------------------------------------------------------- */
body.app .input, body.app select.input, body.app textarea.input {
  font-family: var(--font-pub);
}

/* A field carrying a measured value (an id, a code, an amount) says so
   with .mono and keeps it. */
body.app .input.mono { font-family: var(--font-mono); }

/* ---------------------------------------------------------------------
   S2A.3 ONE FILLED BUTTON PER DECISION REGION

   THE OLD RULE WAS ONE PER PAGE, and the review names it as a PicksYou
   choice mistaken for an integrity requirement: "Repeating the same
   primary action after a long explanation is compatible with transparent
   billing." A page is not a decision. A decision region is: the page
   head, the next action card, a dialog, a card that asks for one thing,
   the closing block after the whole explanation.

   IT IS MARKUP, NOT CSS, so there is nothing to declare here beyond the
   note: a view marks its regions with data-region and
   design/harness/lint.mjs counts filled primaries inside each one, failing
   any region that carries two. Regions marked so far: the workspace page
   head (x-app.title-row), the activation card (x-app.activation), and the
   public landing's own sections. A surface with no data-region is counted
   as one region, which is the old rule, so nothing regressed while the
   views are marked one at a time.
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   S2A.4 MOTION, STATED AS NORMAL MOTION AND THEN THE ALTERNATIVE

   NORMAL MOTION (APP-SHEET.md section 9), written first and as a positive
   statement: 150ms colour and transform on a control, a 160ms fade and
   rise on a card that enters after a state change, rows staggered 40ms, a
   150ms turn on a chevron, the skeleton shimmer, the toast's rise.

   REDUCED MOTION is the alternative and not the specification: no rise,
   no stagger, no shimmer (a static panel bar), chevrons drawn already
   turned, and colour still changing instantly on hover and focus, because
   a control that does not answer a pointer is a control a reader cannot
   tell is one.

   The declarations for both already stand in sections 13 and 17 of this
   sheet and in site.css's motion block; the review asked for the WORDING
   to be rewritten, not the CSS, and design/rules/phase6r-skin.md section 5
   now carries it in that order. Refused under both: pinging dots, shine on
   a primary, nudging arrows, marquees.
   --------------------------------------------------------------------- */

/* =====================================================================
   ==== P6R-S2 ====

   STAGE 2, THE OVERVIEW WITH EVERY NUMBER CARRYING ITS DENOMINATOR
   (T2.11). Every line of design/css-requests-phase6r-s2.md landed, plus
   the two items of design/css-requests-phase6r-s3.md that reached a
   Stage 2 page.

   THE SOURCE. design/mockups/phase6r/s2/*.html with s2/NOTES.md, the
   dashboard head of design/mockups/phase6r/s7/app-overview-v2.html, and
   design/mockups/phase6r/s7/APP-SHEET.md, which is the binding sheet for
   everything in here. Where the request and the mockup disagreed, the
   difference is written down in the ruling's "Built as, Stage 2".

   IT IS AN APPENDED REGION, NOT AN EDIT OF THE BASELINE. Sections 1 to
   18 above are the T0.6 skin and nothing in them was rewritten; three
   shipped rules are OVERTURNED here and each says so where it stands,
   with its reason, because a correction hidden in a diff is a correction
   nobody can find.

   THE LAWS THIS REGION KEEPS. Raspberry is never a button and never a
   band: it is on links, the named pill, the marked name, the current
   week column, the interval band and the reader's own dot. One filled
   button per DECISION REGION, not per page (the ruling's section 3 as
   T2.12 amended it), and nothing in here fills one. No percent sign
   anywhere: every position and every width arrives as a fraction on the
   element and this sheet multiplies it out.
   ===================================================================== */

/* ---------------------------------------------------------------------
   S2.1 THE HEAD'S RIGHT COLUMN, THE SINCE LINE, THE ROW SIZED STRIP
   (request section T2.1)
   --------------------------------------------------------------------- */

/* The head's second column holds TWO children now, the range control
   over the six week drawing, so it is a column of its own rather than a
   third grid item that would have wrapped the strip onto its own row.
   It is capped at the 360 the sheet already gives .app-head .weeks and
   it sits on the frame's right edge. */
.app-head .head-right {
  justify-self: end;
  width: 100%;
  max-width: 360px;
  gap: 10px;
}

/* The control sits above the drawing and aligns to its right edge. The
   shipped .seg carries the box, the height and the pressed state, so
   nothing here touches a segment. */
.app-head .head-right .seg { align-self: flex-end; }

/* WHAT HAPPENED WHILE THE READER WAS AWAY. Two hairlines and a
   paragraph: it is a sentence about the page, not a card on it, so it
   has no ground, no border and no radius beyond the two rules. */
.since {
  margin: var(--s4) 0 0;
  padding: 12px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body);
  color: var(--ink);
}

.since .lead { font-weight: 500; color: var(--ink); }

/* THE ROW SIZED STRIP TAKES ITS HEIGHT FROM THE CALLER (the Stage 2
   order restores the ruling's section 6 number). x-app.week-strip sets
   --weeks-h on .weeks only when a caller passes a height, so the head's
   own strip is byte for byte the markup it shipped with and the fallback
   below is the 32px the accepted mockup drew. The assistant matrix and
   the questions page ask for 24; the 2px floor on a column keeps a 1 of
   9 visible at that height. */
.weeks.weeks-sm .weeks-cols { height: var(--weeks-h, 32px); }

/* Nothing visual: the span the poll writes the count sentence into. It
   is named here so nobody removes it as a stray. */
.progress .count { font-variant-numeric: tabular-nums; }

/* THE DENOMINATOR UNDER A COUNT IS MEASURED (APP-SHEET.md section 5, the
   metric card: "the count at 40 / 700 mono with its denominator as a small
   mono line beside it"). Section 12 put that `small` back into the spoken
   face, which was written before the owner's review settled the metric
   card's anatomy; the size, the weight and the secondary ink stay exactly
   where section 12 left them and only the family moves, so "of 75" under
   a 28 reads as one figure with its n and not as a count with a caption. */
.tally li.tile .n small { font-family: var(--font-mono); }

/* AND THE FOOTNOTE'S DATE KEEPS ITS SENTENCE'S SPACING. Section 1 gives a
   measured figure inside a headline, an interval, a meta line, the
   sidebar and a tally the mono's narrower word space; the page's own
   footnote was not on that list, and the date it carries moved into the
   measured face with the rest of the timestamps (T2.11). `.nb` is the
   no-break wrapper and says nothing about a face, which is why the
   markup wears both. */
.app-foot .mono { word-spacing: -0.28em; }

/* ---------------------------------------------------------------------
   S2.1b THE METRIC CARD ROW (APP-SHEET.md section 5, and section 8's
   order: the compact result header, then the metric cards, then the next
   action card)
   --------------------------------------------------------------------- */

/* PHASE 5o RULING A2 SAID "no stat card row exists anywhere in the app",
   and the owner's review of 2026-09-05 overturned it for exactly one
   object: "a metric card can use a large number and a concise
   denominator". What A2 refused is still refused here and the two are
   not the same thing. A2 killed four figures at the HEADLINE'S OWN SIZE
   beside the one number the page is about; this row is four facts the
   head has already stated, at a size below the receipt sentence, each
   one carrying the n it was counted out of on its own line.

   WHAT THE SHEET REFUSES ON THIS CARD, in its own words: no delta, no
   arrow, no green, no number out of a hundred. There is no colour rule
   in this whole block. */
.metrics { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); }

@media (min-width: 700px) {
  .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* FOUR ACROSS FROM 1024, which is where the sheet puts three at 229 each
   and where a fourth still clears the 12px floor on its denominator. */
@media (min-width: 1024px) {
  .metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.metric {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s4) var(--s5) 14px;
  min-width: 0;
}

.metric .m-name { font-size: var(--fs-h3); font-weight: 600; line-height: 1.35; margin: 0; }

/* THE COUNT AND ITS DENOMINATOR, BASELINE ALIGNED AND ALLOWED TO WRAP.
   A card narrow enough to break "of 90 answers" onto its own line is a
   card that reads perfectly well that way; a card that clips it is not. */
.metric .m-count { display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 10px; margin: 0; min-width: 0; }
.metric .m-fig { font-size: var(--fs-count-big); font-weight: 700; line-height: 1; }
.metric .m-den { font-size: var(--fs-meta); line-height: 1.4; color: var(--ink-2); }

/* AND A READING THAT IS A SENTENCE IS NOT SET AS A FIGURE. "Too few
   answers here to read yet" at 40px in the measured face would look like
   a number nobody could read; it is the body size in the spoken face,
   which is what the tally's own `.n.word` rule already does one section
   up for the same reason. */
.metric .m-word { font-family: var(--font); font-size: var(--fs-body); font-weight: 500; line-height: 1.35; color: var(--ink); }
.metric .m-sub { font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); margin: 0; }

/* ---------------------------------------------------------------------
   S2.2 THE ANSWERS TABLE REGROUPED, AND THE ANSWER CARD'S SOURCES
   (request section T2.4)
   --------------------------------------------------------------------- */

/* THE SECOND AND THIRD REPEAT OF A QUESTION: one line that opens the
   answer in place. A DASHED hairline, on no ground at all, so a folded
   repeat cannot be mistaken for the opened answer above it, which is
   the one thing on this row that carries a ground. */
details.repeat {
  margin: 0 var(--s5) 10px;
  padding: 8px 14px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

details.repeat > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

details.repeat > summary::-webkit-details-marker { display: none; }
details.repeat > summary .caret { color: var(--ink-2); flex: none; transition: transform var(--dur-1) var(--ease); }
details.repeat[open] > summary .caret { transform: rotate(180deg); }

/* The answer inside a repeat sits flush: the repeat is already inset by
   the card's own margin, so a second one would step the page in twice. */
details.repeat > .answer { margin: 10px 0 0; }

/* THE MODEL STAMP beside the assistant on an opened answer: which
   machine answered and the day we pinned that id. It never wraps and its
   figures are measured. */
.a-head .pin { white-space: nowrap; color: var(--ink-2); }
.a-head .pin .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* THE CITED HOSTS OF ONE ANSWER, in the order the assistant leaned on
   them. A wrapping row, because it is a list of names and not a table,
   and a host with its class chip never breaks apart. */
.cites {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.cites li { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.cites .lbl { font-weight: 500; color: var(--ink-2); }

.cite {
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  color: var(--ink);
  text-decoration: none;
}

.cite:hover { text-decoration: underline; }

/* THE SOURCE CLASS CHIP, on the shipped .chip base and with no dot: it
   says what a host is, and a dot is the state register. NOTHING HERE
   GRADES A HOST. A competitor is not a warning, so the chip is one
   colour whatever it says, and a host nobody has classified renders no
   chip at all rather than a dashed guess. */
.chip-src { color: var(--ink-2); background: var(--band); }
.chip-src::before { display: none; }

/* THE NINTH LINK AND THE REST, FOLDED IN PLACE. display:contents on the
   inner list drops the folded links into the same wrapping row as the
   first eight, so opening the fold reads as the row growing rather than
   as a second list appearing under it. */
.cites-more { display: inline; }
.cites-more > summary { list-style: none; display: inline; cursor: pointer; color: var(--accent); font-size: var(--fs-meta); }
.cites-more > summary::-webkit-details-marker { display: none; }
.cites-more[open] > summary { display: none; }
.cites-more > ul { display: contents; }

/* THE WEEK PAGE'S ROW IS AN ASSISTANT, so its second cell holds the ten
   cell strip AND the count sentence, and 132px holds neither
   comfortably: at 1400 the strip alone is 98px. The overview's row is a
   question with a two word pill in that cell and keeps the shipped
   width. This is why the widening is on .answers-group and not on
   details.row. */
.answers-group .cols,
.answers-group details.row > summary { grid-template-columns: minmax(0, 1fr) 190px 84px 24px; }

/* ---------------------------------------------------------------------
   S2.3 THE ACTIVATION CARD'S NEXT ACTION AND ITS FOLD (request section
   T2.5)
   --------------------------------------------------------------------- */

/* THE ONE RECOMMENDED NEXT ACTION, between the summary and the first
   group. No ground and no border of its own: the card is already the
   one warm surface in the workspace and a box inside a box is two.
   The button is the shipped .btn and it is inline, so it is its own
   width. The card is a decision region (data-region="activation"), which
   is what lets it carry a filled button beside the page head's. */
.act-next { margin: 14px 0 0; }
.act-next .btn { display: inline-flex; }

/* WHAT NOBODY OWES US, FOLDED: the done rows and the optional ones,
   counted by one line that opens in place with no script. */
.act-more { margin-top: 14px; }

.act-more > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.act-more > summary::-webkit-details-marker { display: none; }
.act-more > summary:hover { color: var(--ink); }
.act-more > summary .chevron { flex: none; color: var(--ink-2); transition: transform var(--dur-1) var(--ease); }
.act-more[open] > summary .chevron { transform: rotate(180deg); }
.act-more .act-group { margin-top: 10px; }

/* ---------------------------------------------------------------------
   S2.4 THE ZERO ROW: THE ACCENT ONLY WHERE THE READER WAS NAMED
   (T2.11's own note on T2.6)
   --------------------------------------------------------------------- */

/* SECTION 8 IS OVERTURNED FOR ONE RULE, and this is the whole of it.
   `.zero li.you` painted the reader's own figure raspberry on every row,
   including the rows that read "you: named in 0 of 30", so the accent
   said "here you are" where the card's whole point was that the reader
   was not there. The markup has always known the difference: the
   component writes `class="you named"` when the count is above zero and
   `class="you"` when it is not (resources/views/components/receipt/
   zero-row.blade.php). The accent now follows the fact. A row where the
   reader was not named keeps the ink it inherits, which is the honest
   drawing of nothing. */
.zero li.you { color: inherit; font-weight: 500; }
.zero li.you .mono { color: var(--ink-2); }
.zero li.you.named { color: var(--accent); }
.zero li.you.named .mono { color: var(--accent); }

/* The card's two doors (fix batch F9, item 2): the head's opens the same
   names over four weeks, the foot's opens the businesses we sample
   beside this one. Both are quiet; a card that claims nothing about
   anybody has no filled button in it. */
.zero-card > .card-head { align-items: flex-start; }
.zero-card > .card-foot { gap: 8px 16px; }

/* ---------------------------------------------------------------------
   S2.5 RECEIPTS, JOINED (request section T2.7)
   --------------------------------------------------------------------- */

/* THREE DATED ROWS, EACH A DOOR. It is a ledger, not an instrument:
   there is no icon per row, no figure at heading size, no motion, and a
   zero is a fact about what we could see rather than a warning, so
   nothing in here is coloured by its value. */
.joined { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.joined li { border-bottom: 1px solid var(--line); }
.joined li:last-child { border-bottom: 0; }

/* The row is an `a` where it has somewhere to go and a `span` where it
   has not, so both selectors have to reach it. */
.joined-row {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr) 16px;
  gap: 2px 12px;
  align-items: baseline;
  padding: 10px var(--s5);
  font-size: var(--fs-body);
  color: inherit;
  text-decoration: none;
}

/* ONLY THE ANCHOR LIGHTS UP. A row with no door that tinted under the
   pointer would be promising something it cannot do. */
a.joined-row:hover { background: var(--zebra); text-decoration: none; }
.joined-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.joined-row .d {
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
}

/* THE ONLY THING THAT MARKS A ZERO is the stamp dropping to secondary
   at 400. No ground, no chip, no colour: we did not see it, which is
   not the same as something being wrong. */
.joined-row.zero .d { color: var(--ink-2); font-weight: 400; }

.joined-row .t { min-width: 0; }
.joined-row .t small { display: block; font-size: var(--fs-meta); color: var(--ink-2); }
.joined-row .go { flex: none; align-self: center; color: var(--ink-2); }

/* THE ARRIVAL FROM THE CARD LANDS UNDER THE BAR OTHERWISE. The joined
   card's three rows open three cards on the receipts page, and a 46px
   sticky bar over a fragment jump eats the card head. #snippet and
   #upload are the two the desk already opened; #visits and #crawlers are
   the two this card added. */
#visits,
#crawlers,
#snippet,
#upload { scroll-margin-top: calc(var(--header) + var(--s4)); }

/* ---------------------------------------------------------------------
   S2.6 THE CALCULATOR (request section T2.8 and fix F5)
   --------------------------------------------------------------------- */

/* TWO EQUAL COLUMNS, and they are this page's answer to the measure law:
   the dials on the left and the reading on the right, both keeping their
   width at 1400 and 1920 rather than one capped block beside a hole. */
.calc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--gap);
  align-items: start;
}

/* THE COUNTS THE PAGE IS HELD AT, on paper ground between two hairlines,
   bleeding to the card's edges: it is the fixed point of every figure
   under it, not a sentence in the card body. */
.fixed-point {
  margin: 0;
  padding: 12px var(--s5);
  background: var(--bg);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body);
}

.fixed-point .fig { font-weight: 500; }

/* THE EXAMPLE THE READER PICKS, in the place the fixed point stands when
   there is something to fix (F5). Same band, because it answers the same
   question: what rate is this drawing held at. */
.calc-example {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 12px var(--s5);
  background: var(--bg);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.calc-example .lbl { font-size: var(--fs-meta); font-weight: 500; color: var(--ink-2); }
.calc-example .note { font-size: var(--fs-meta); color: var(--ink-2); }

/* AND A SEGMENT CELL WHOSE WHOLE LABEL IS A FIGURE IS MEASURED. The S2A
   region puts every control label in Geist through `body.app :is(...)`,
   which is right for "12 weeks" and wrong for "1 in 4": the example
   chooser's three cells are a pair of counts each and nothing else. The
   selector has to out-weigh that list, which is why it repeats body.app.
   The overview's range control is the other case and keeps Geist with a
   .mono span around its one number. */
body.app .seg a.mono,
body.app .seg button.mono { font-family: var(--font-mono); }

/* ONE DIAL. A hairline above it and none on the first, so the two dials
   read as a list rather than as two boxes. */
.dial { padding: var(--s4) var(--s5) 18px; border-top: 1px solid var(--line); }
.dial:first-of-type { border-top: 0; }
.dial .dial-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.dial .dial-head label { font-size: var(--fs-body); font-weight: 600; }
.dial .dial-head .val { font-size: 24px; font-weight: 600; line-height: 1; }
.dial p { margin: 2px 0 0; font-size: var(--fs-meta); color: var(--ink-2); }

/* THE SLIDER. The native appearance is dropped so the track and the
   thumb are the sheet's, and the only thing on this page that moves is
   the thumb under a finger. */
.dial input[type="range"] {
  width: 100%;
  height: 34px;
  margin: 10px 0 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.dial input[type="range"]::-webkit-slider-runnable-track { height: 2px; border-radius: 1px; background: var(--line); }
.dial input[type="range"]::-moz-range-track { height: 2px; border-radius: 1px; background: var(--line); }

.dial input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -7px;
  border-radius: 50%;
  background: var(--ink);
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px var(--ink);
  transition: transform var(--dur-1) var(--ease);
}

.dial input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--ink);
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px var(--ink);
  transition: transform var(--dur-1) var(--ease);
}

.dial input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.1); }
.dial input[type="range"]:active::-moz-range-thumb { transform: scale(1.1); }
.dial input[type="range"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* THE STOPS UNDER A SLIDER. First left, last right, the rest centred, so
   a label sits over the stop it names. */
.ticks {
  display: flex;
  justify-content: space-between;
  margin: 2px 0 0;
  font-size: var(--fs-mini);
  color: var(--ink-2);
}

.ticks span { flex: 1; text-align: center; }
.ticks span:first-child { text-align: left; }
.ticks span:last-child { text-align: right; }
.ticks .on { color: var(--ink); font-weight: 500; }

/* Who is asked and how many times, under the assistants dial. */
.roster { margin: var(--s2) 0 0; font-size: var(--fs-meta); color: var(--ink-2); }
.roster .fig,
.roster .mono { color: var(--ink); }

/* THE READING BLOCK. Two bands over one axis, and the tick is the same
   tick on both: what a bigger week buys is a narrower band around a rate
   that does not move, which is the whole honest case this page makes. */
.ibars { display: grid; gap: 14px; }
.ibar { display: grid; gap: var(--s1); }
.ibar .ib-label { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: var(--fs-meta); color: var(--ink-2); }
.ibar .ib-label b { color: var(--ink); font-weight: 500; }

.ibar .ib-track {
  position: relative;
  height: 22px;
  border-radius: 4px;
  background: var(--band);
}

/* THE TWO NUMBERS ARRIVE AS FRACTIONS ON THE ELEMENT and are multiplied
   out here, exactly as .wcol and .nb-bar take their sizes: the copy law
   keeps the percent sign out of every customer facing file, and a
   markup that writes "21.5%" is a markup with a rate in it. */
.ibar .ib-band {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--low, 0) * 100%);
  width: calc(var(--width, 0) * 100%);
  background: var(--accent-soft);
  border-left: 2px solid var(--accent);
  border-right: 2px solid var(--accent);
  border-radius: 3px;
}

/* THE MEASURED RATE, at the same place on both bands so the two line up
   and the reader can see the band narrow around a fixed point. */
.ibar .ib-tick {
  position: absolute;
  top: -3px;
  bottom: -3px;
  left: calc(var(--tick, 0) * 100%);
  width: 2px;
  margin-left: -1px;
  background: var(--ink);
}

.ibar .ib-reads { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: var(--fs-meta); }
.ibar .ib-reads .pts { color: var(--ink-2); }
.ibar .note { margin: var(--s1) 0 0; font-size: var(--fs-meta); color: var(--ink-2); }

/* THE READING WE DO NOT HAVE (F5). The label row and nothing else: no
   track, no band, no tick, and no height held open for a measurement
   nobody made. A drawing of an absent number is the invention the
   owner's review caught. */
.ibar.ib-empty .meta { margin: var(--s1) 0 0; }

/* The three words under the bands. Never a percentage: the axis is
   named, not numbered. */
.ib-axis { display: flex; justify-content: space-between; gap: 12px; margin: 2px 0 0; font-size: var(--fs-mini); color: var(--ink-2); }

/* THE ONE SENTENCE. A range set in the mono inside spoken text reads
   double spaced, so the figure keeps the sentence's word spacing, which
   is the rule section 1 already gives a figure inside a headline. */
.calc-sentence { margin: var(--s4) 0 0; font-size: var(--fs-lede); line-height: 1.5; }
.calc-sentence .fig { font-weight: 500; word-spacing: -0.28em; }

/* THE MODEL THE ILLUSTRATION RUNS UNDER (F5), stated once, above both
   cards. A reader cannot check arithmetic whose assumptions are not on
   the page. */
.assumptions {
  margin: 0 0 var(--s4);
  padding: 14px var(--s4);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: var(--fs-body);
}

.assumptions .lbl { margin: 0 0 var(--s2); font-size: var(--fs-meta); font-weight: 500; color: var(--ink-2); }
.assumptions ul { margin: 0; padding: 0 0 0 18px; display: flex; flex-direction: column; gap: var(--s1); }
.assumptions > p:last-child { margin: var(--s2) 0 0; font-size: var(--fs-meta); }

/* THE PLAN THE DIALS LAND ON, as a fact with its price and a quiet door.
   The price is a block of its own: run on, it reads as one sentence with
   a price inside it, which is a sentence that sells. */
.plan-fact {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 8px 12px;
  margin: var(--s4) 0 0;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: var(--fs-body);
}

.plan-fact small { display: block; font-size: var(--fs-meta); color: var(--ink-2); }
.plan-fact .fig { font-weight: 500; }

/* ---------------------------------------------------------------------
   S2.7 THE PEERS STRIP (request section T2.9, and the .dotstrip
   collision of design/css-requests-phase6r-s3.md)
   --------------------------------------------------------------------- */

/* THE COLLISION, AND HOW IT IS FIXED. `.dotstrip` was already drawn in
   design/tokens.css as the free receipt's inline row of 8px squares, and
   the peers strip is a DIFFERENT OBJECT with lanes, a track, positioned
   dots, an axis and a key inside it. The public rule laid it out inline
   and its axis printed as a sentence. One object is spelled once (the
   Stage 1 Built-as, decision 3), so the customer strip took its own name
   in the markup, `.peer-dots`, beside the `.peer-` names the rest of
   that page already uses; the free receipt keeps `.dotstrip` and its
   rule is untouched. */
.peer-dots { display: block; margin: 20px 0 16px; }
.peer-dots .ds-row { position: relative; height: 28px; margin-top: 10px; }
.peer-dots .ds-row:first-child { margin-top: 0; }

/* THE READER'S OWN LANE STANDS ABOVE THE OTHERS, shorter, with room for
   its label. */
.peer-dots .ds-row.you { height: 22px; }

.peer-dots .ds-track {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: var(--line);
}

/* ONE BUSINESS, ONE DOT, AND THE POSITION ARRIVES AS A FRACTION. Sixty
   per cent opacity so a cluster reads as a cluster; a dot knows where it
   sits and nothing else, which is the whole privacy promise of this page
   drawn rather than written. */
.peer-dots .dot {
  position: absolute;
  top: 50%;
  left: calc(var(--f, 0) * 100%);
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--ink);
  opacity: 0.6;
}

.peer-dots .dot.you {
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  background: var(--accent);
  opacity: 1;
  box-shadow: 0 0 0 2px var(--surface);
}

.peer-dots .ds-label {
  position: absolute;
  top: 0;
  left: calc(var(--f, 0) * 100%);
  margin-left: 10px;
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  line-height: 1.2;
  color: var(--accent);
  white-space: nowrap;
}

.peer-dots .ds-x {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  color: var(--ink-2);
}

.peer-dots .ds-key { margin: var(--s2) 0 0; font-size: var(--fs-mini); color: var(--ink-2); }
.peer-dots .ds-key i { display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: var(--ink); vertical-align: 0; }
.peer-dots .ds-key i.you { background: var(--accent); }

/* THE CLAIM, and it is the largest type on the page: the sentence a
   reader came for, with the rest of the arithmetic under it. */
.peer-sentence { margin: 0; font-size: 18px; line-height: 1.4; color: var(--ink); }
.peer-sentence .mono { word-spacing: -0.28em; }
.peer-sub { margin: 6px 0 0; font-size: var(--fs-body); color: var(--ink-2); }
.peer-sub .mono { color: var(--ink); word-spacing: -0.28em; }

.peer-spread { margin-top: 20px; }
.peer-spread h3 { margin: 0 0 var(--s2); font-size: var(--fs-body); font-weight: 600; }

/* The band table fits its half of the row at 390 as the two column table
   it is, so it drops the scroll box's minimum width. */
.peer-bands { min-width: 0; }

/* THE READER'S OWN BAND: the panel ground and a heavier first cell.
   NO COLOUR BEYOND THE GROUND. A band is where a count falls, not a
   state, and a tinted row would grade it. */
.peer-bands tr.you td { background: var(--band); }
.peer-bands tr.you td:first-child { font-weight: 500; }

/* THE LIST BESIDE THE TABLE: what is never on this page. Its .kv rows
   are single celled, a label with no figure, which the shipped rule
   already draws now that .kv reaches every card of the workspace (the
   Stage 3 Built-as, decision 3). */
.peer-never .kv { display: block; }

/* ---------------------------------------------------------------------
   S2.8 THE NAMED PAGE'S QUEUE AND ITS TRACKED COLUMN (request section
   T2.6)
   --------------------------------------------------------------------- */

/* ONE SUGGESTED NAME AND ITS TWO DOORS. It sits inside the card's own
   padding, so it has no side padding of its own, and it carries no
   filled button: accepting a name is the reader's reading of their own
   answers and nothing about it is published anywhere. */
.queue-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 16px;
  align-items: center;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}

.queue-row:first-of-type { border-top: 0; }
.queue-row .qname { font-size: var(--fs-body); font-weight: 500; color: var(--ink); }
.queue-row .qmeta { margin: 0; font-size: var(--fs-meta); color: var(--ink-2); }
.queue-row .qmeta .mono { color: var(--ink); }
.queue-row .qdoors { display: flex; flex: none; flex-wrap: wrap; gap: var(--s2); }
.queue-row .qdoors form { margin: 0; }

/* WHAT THIS READER HAS TOLD US ABOUT A NAME. No chip, no badge and no
   colour: a tracked name is not a warning and a rejected one is not an
   error. */
td.tracked { font-size: var(--fs-meta); color: var(--ink-2); white-space: nowrap; }
td.tracked .mono { color: var(--ink); }

/* ---------------------------------------------------------------------
   S2.9 THE HELP RAIL, THE FAQ CARET, THE DANGER LEAD (request section
   T2.10)
   --------------------------------------------------------------------- */

/* THE FAQ AT THE FOOT OF A CARD rather than in a rail of its own. One
   hairline and the space around it: no ground, no border, no radius. A
   second card inside a card is a box in a box. */
.help-rail { margin-top: var(--s4); padding-top: 14px; border-top: 1px solid var(--line); }
.help-rail .faq { margin-top: 0; }

/* THE CARET THE MOCKUP TURNS AND THE SHIPPED SHEET FORGOT. One rule for
   every FAQ in the product: the wizard's guidance rail ships the same
   markup and has been drawing a chevron that never moved. */
.faq summary .chevron { flex: none; color: var(--ink-2); transition: transform var(--dur-1) var(--ease); }
.faq details[open] > summary .chevron { transform: rotate(180deg); }

/* THE LEAD OVER EACH LIST ON A STATEMENT PAGE ("This will remove:",
   "This will keep:"). A sentence at a sentence's size, never a heading,
   and it carries NO COLOUR: the rows under it are counted facts and this
   product has no red panel. */
.danger-lead { margin: var(--s4) 0 var(--s1); font-size: var(--fs-body); font-weight: 600; color: var(--ink); }
.card-body > .danger-lead:first-child,
.tally + .danger-lead { margin-top: var(--s4); }
.wrap > .danger-lead:first-of-type { margin-top: 0; }

/* ---------------------------------------------------------------------
   S2.10 THE QUESTIONS TABLE (the page T2.3 planned and T2.11 built)
   --------------------------------------------------------------------- */

/* FIVE COLUMNS: the question as asked, its count with its n, the strip
   of its sampled Mondays, the reading, and the caret. It is the shipped
   answers vocabulary widened, so a reader who has opened a row on the
   overview already knows how this one opens. */
.qtable .cols,
.qtable details.row > summary { grid-template-columns: minmax(0, 1fr) 96px 180px 210px 24px; }

/* AND ITS FOOT DOES NOT BLEED TWICE. `.card-foot` carries negative side
   margins so that it reaches the edges of a card that pads its body;
   `.answers` pads nothing at all, because the table IS the card's body,
   so on this card those margins push the foot 24px past the card on each
   side. Measured on the questions page at 390: the foot was 404 wide in a
   356 card and the page scrolled sideways by 7px. The overview's and the
   week page's answers cards never carried a foot, so nothing regressed;
   this is the rule that lets one. */
.answers > .card-foot { margin: 0; }

/* THE COUNT IS LEFT ALIGNED HERE, because the cell beside it is a
   drawing and a right aligned figure would float away from its row. */
.qtable .named { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink-2); white-space: nowrap; }

/* AND THE ACCENT IS SPENT ON A COUNT ABOVE ZERO, once, with the dot the
   named pill already uses. A question that never named the reader is the
   top of the worklist and it is drawn in plain ink: an empty count is
   not a failure state. */
.qtable .named.is { color: var(--ink); }
.qtable .named.is::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--accent);
  vertical-align: 1px;
}

/* THE SLOT IN THE PACK, beside the question. It is not the row's
   position and the card's foot says so in words. */
.qtable .q .qn { margin-right: 6px; font-size: var(--fs-mini); color: var(--ink-2); }

/* THE READING: a sentence in secondary ink, never a colour and never a
   chip. "Too few answers here to read yet" is a fact about the sample,
   not a warning about the business. */
.reading-cell { font-size: var(--fs-meta); color: var(--ink-2); white-space: normal; }

/* THE ROW OPENED: what the strip drew, in figures, beside the window's
   own totals, with the doors under both. */
.qtable .row-open {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px var(--s5);
  align-items: start;
  margin: 0 var(--s5) 14px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.qtable .row-open h3 { margin: 0 0 var(--s1); font-size: var(--fs-meta); font-weight: 600; }
.qtable .row-open .kv { font-size: var(--fs-meta); }

.qtable .row-open .doors {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.qtable .row-open .doors span { min-width: 0; flex: 1 1 24ch; }
.qtable .row-open .doors .btn { flex: none; }

/* The strip inside a row keeps to its cell and loses the head's cap. */
.qtable .weeks.weeks-sm { max-width: 100%; }

/* ---------------------------------------------------------------------
   S2.11 THE PHONE, 390, AND THE SHEET'S OTHER BREAK AT 768
   --------------------------------------------------------------------- */

/* The measure law's answer on a wide screen is two columns; at 900 the
   calculator's two cards have less room each than the reading needs, so
   they stack in document order: the dials, then what they do. */
@media (max-width: 900px) {
  .calc { grid-template-columns: minmax(0, 1fr); }
  .qtable .row-open { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
  /* THE ANSWERS TABLE'S PHONE SHAPE, EXTENDED TO THE FIVE COLUMN ONE.
     The strip and the reading come under the question rather than
     squeezing it, the caret spans the rows, and nothing is hidden. */
  .qtable details.row > summary {
    grid-template-columns: minmax(0, 1fr) 24px;
    grid-template-rows: auto auto auto auto;
    row-gap: 4px;
  }

  .qtable .q,
  .qtable .named,
  .qtable .weeks,
  .qtable .reading-cell { grid-column: 1; }

  .qtable .caret { grid-row: 1 / span 4; grid-column: 2; align-self: center; }
  .qtable .row-open { margin-left: var(--s4); margin-right: var(--s4); }

  /* The date, the sentence and the chevron: two columns with the stamp
     on its own line and the chevron spanning both. Nothing truncates. */
  .joined-row {
    grid-template-columns: minmax(0, 1fr) 16px;
    grid-template-rows: auto auto;
    padding-left: var(--s4);
    padding-right: var(--s4);
  }

  .joined-row .d { grid-column: 1; }
  .joined-row .t { grid-column: 1; }
  .joined-row .go { grid-column: 2; grid-row: 1 / span 2; }

  /* The dials keep their full width and step in with the card. */
  .fixed-point,
  .calc-example,
  .dial { padding-left: var(--s4); padding-right: var(--s4); }
  .dial .dial-head .val { font-size: 20px; }

  /* THE STRIP KEEPS ITS LANES AND DROPS THE LABEL. The sr-only sentence
     under it carries the reader's own count, which is what the label
     was for. */
  .peer-dots .ds-label { display: none; }

  /* The queue's doors wrap under the name they belong to. */
  .queue-row { grid-template-columns: minmax(0, 1fr); }

  /* THE METRIC CARD KEEPS ITS COUNT AND LOSES A LITTLE OF ITS SIZE. "19
     to 36" at 40px is 168px of mono inside a 308px card, which fits, and
     30px leaves the denominator room to sit beside it rather than under
     it on the two cards whose figure is one number. */
  .metric { padding: var(--s3) var(--s4) 12px; }
  .metric .m-fig { font-size: 30px; }

  /* The repeat lines follow the answer they open, which is inset by the
     card's phone margin. */
  details.repeat { margin-left: var(--s3); margin-right: var(--s3); }
}

/* ---------------------------------------------------------------------
   S2.12 MOTION, THE NORMAL STATEMENT AND ITS ALTERNATIVE
   --------------------------------------------------------------------- */

/* NORMAL MOTION on what this region added: a chevron turns over 150ms
   and a slider thumb grows under a finger. Nothing here loops, nothing
   pings, nothing shines, and no band, dot or read moves on its own: the
   calculator is a fresh document at every slider position and the peers
   strip is a photograph of one week. */

/* THE ONE TWEEN THIS REGION ADDS: the activation card and its fold open
   their height rather than snapping (the T2.5 request's second note).
   Both are NATIVE details and stay native: there is no script anywhere
   near them, so a reader with JavaScript off still opens and closes the
   card, it still prints open, and a fragment still reaches it. The tween
   is written entirely inside @supports, so a browser without
   interpolate-size and ::details-content gets exactly what shipped
   before, which is the instant open, and nothing is ever hidden by a
   rule the browser only half understands. */
@media (prefers-reduced-motion: no-preference) {
  @supports (interpolate-size: allow-keywords) and selector(::details-content) {
    .activation,
    .act-more { interpolate-size: allow-keywords; }

    .activation::details-content,
    .act-more::details-content {
      height: 0;
      overflow: hidden;
      transition: height var(--dur-3) var(--ease), content-visibility var(--dur-3) allow-discrete;
    }

    .activation[open]::details-content,
    .act-more[open]::details-content { height: auto; }
  }
}

/* REDUCED MOTION is the alternative and not the specification. Every
   transition this region added goes to nothing; an open chevron is drawn
   already turned, because the browser draws it turned and the transition
   is the only thing that was ever animating. Colour still changes on
   hover and on focus, instantly, which section 17 already states. */
@media (prefers-reduced-motion: reduce) {
  .act-more > summary .chevron,
  .faq summary .chevron,
  details.repeat > summary .caret,
  .dial input[type="range"]::-webkit-slider-thumb,
  .dial input[type="range"]::-moz-range-thumb { transition: none; }
}

/* =====================================================================
   ==== P6R-S4 ====

   STAGE 4, THE FIX DESK, THE DIFF VIEW, THE APPROVALS LEDGER AND THE
   SHEET THE LEDGER PRINTS ON (T4.7).

   THE SOURCE. design/mockups/phase6r/s4/parts/s4.css with s4/NOTES.md
   section 5, and design/css-requests-phase6r-s4.md, which is the contract
   T4.1, T4.3 and F10 wrote for this region. Where the request and the
   mockup disagreed the difference is written down in the ruling's
   "Built as, Stage 4"; there are two of them and both are named there.

   THE MOCKUP'S SPELLINGS ARE TRANSLATED ON THE WAY IN, as the NOTES set
   out: `.btn-primary` is the shipped bare `.btn`, `.chip-fine` is
   `.chip.fine`, `.n` is `.mono`, `--hair` is `--line`, `--paper` is
   `--bg`, `--card` is `--surface`, `--panel` is `--band`, `--ink2` is
   `--ink-2`, `--fine` is `--ok`, `--fail` is `--bad`, `--mono` is
   `--font-mono` and `--sans` is `--font`, which is Geist, because every
   caller of it in the mockup sheet is a control label.

   IT IS AN APPENDED REGION AND EDITS NOTHING ABOVE IT. Sections 1 to 18
   and the S1, S3, S2A and S2 regions are untouched. Two things this
   region needs from the baseline are OVERTURNED where they stand below,
   each with its reason, because a correction hidden in a diff is a
   correction nobody can find.

   THE LAWS IT KEEPS. Raspberry is never a button and never a ground:
   the only colour spent in here is the fine and failing tints on a diff
   line, and the sign in the gutter carries that meaning without them.
   One filled button per DECISION REGION: the desk's open row is a
   region, the confirm question is a region, and nothing in this sheet
   fills anything. 12px is the floor, so every `small` in here carries a
   size of its own rather than inheriting the browser's 0.83em.
   ===================================================================== */

/* ---------------------------------------------------------------------
   S4.1 THE DESK: THE GROUP HEAD, THE ROW AND ITS SEVEN STATE WORDS
   (request section T4.3)
   --------------------------------------------------------------------- */

/* The last-read stamp, on the title row's own left edge. It is a fact
   about the page and not a card on it, so it has no ground and no rule:
   just the meta size with the date and the time set in the measured
   face by the markup's own `.mono` spans. */
.fixes-page .read-at { margin: 6px 0 0; }

/* The group head. `.answers .grp` is scoped to the answers table, so the
   desk's own four groups are drawn here at the same numbers. */
.desk .grp {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: 36px;
  padding: 6px var(--s5);
  background: var(--band);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body);
}

.desk .grp .g-name { font-weight: 600; color: var(--ink); min-width: 0; }
.desk .grp .g-sum { font-family: var(--font-mono); font-size: var(--fs-mini); color: var(--ink-2); white-space: nowrap; }

/* THE FINDING ROW IS A NATIVE DISCLOSURE. `details name="fix"` keeps one
   open at a time, so the guidance rail's answers always belong to the
   row that is open, and no script is involved in any of it. */
.desk details.fix { border-bottom: 1px solid var(--line); }
.desk details.fix:last-child { border-bottom: 0; }
.desk details.fix > summary { list-style: none; cursor: pointer; }
.desk details.fix > summary::-webkit-details-marker { display: none; }

/* The open row is one step heavier than its neighbours, so the words
   below are read as belonging to it. */
.desk details.fix[open] > summary { background: var(--zebra); }
.desk details.fix[open] > summary .t { font-weight: 500; }

/* The chevron turns 90 degrees, which is the only motion on this page.
   It points right when the row is shut, so it turns a quarter and not a
   half; the reduced-motion block at the foot of this region takes the
   transition away and leaves the turn. */
.desk-row .go { transition: transform var(--dur-1) var(--ease); }
.desk details.fix[open] > summary .go { transform: rotate(90deg); }

/* THE STATE IS ALWAYS A WORD AND NEVER A COLOUR ALONE. Seven of them,
   and the colour only ever repeats what the word says. The shipped
   `.state.bad` and `.state.fixed` above are the blockers desk's own two
   and are untouched. */
.desk-row .state.draft { color: var(--ink); font-weight: 500; }
.desk-row .state.pending,
.desk-row .state.reread_requested,
.desk-row .state.advice,
.desk-row .state.sent { color: var(--ink-2); }
.desk-row .state.not_found_yet { color: var(--warn); }
.desk-row .state.done { color: var(--ok); }
.desk-row .state.refused { color: var(--bad); }

/* ---------------------------------------------------------------------
   S4.2 THE OPENED FINDING: THE WORDS, THE DRAFT AND THE ACTION STRIP
   (request section T4.3)
   --------------------------------------------------------------------- */

/* Two columns, the words at about 55 hundredths of the width and the
   draft beside them, so a 540px reading column never stands beside a
   hole at 1920. The left inset lines the body up under the row's title:
   the card's own 24, the 20px mark and the 12px gap. */
.fix-body {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 18px 32px;
  align-items: start;
  padding: 6px var(--s5) 18px calc(var(--s5) + 32px);
}

.fix-words { min-width: 0; }
.fix-draft { min-width: 0; padding-top: 10px; }

.fix-words .fact { padding: 10px 0 0; font-size: var(--fs-body); }
.fix-words .fact + .fact { margin-top: 4px; border-top: 1px solid var(--line); }
.fix-words .fact p { margin: 0; max-width: 68ch; }
.fix-words .fact p + p { margin-top: 6px; }

/* The mini label over each block. `.mini` is site.css's own object and
   the workspace draws it at the same numbers; all this adds is the 6px
   under it and the block display a span does not have. */
.fix-words .fact .mini,
.fix-draft > .mini { display: block; margin-bottom: 6px; }

/* A file or a page address inside a sentence: the measured face, and it
   breaks at any character so a long URL never widens the column. */
.fix-words .fact a.file,
.fix-words .fact p.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* A numbered step: a circled ordinal and the sentence beside it. */
.fix-words .step {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: 8px;
  align-items: start;
  margin-top: 10px;
  font-size: var(--fs-body);
}

.fix-words .step i {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
  font: 500 var(--fs-mini)/20px var(--font-mono);
  font-style: normal;
  text-align: center;
  color: var(--ink-2);
}

/* Where a draft cannot be built the hole is the shape of the thing that
   is missing: the shipped hatch, at the code box's own width. */
.fix-draft .hatch,
.fix-words .fact .hatch { padding: var(--s4); text-align: left; }

/* The Google box's own two lines under it (T4.4's markup, drawn here
   because it renders inside `.fix-draft` on this desk). */
.fix-draft .counter { display: flex; justify-content: space-between; gap: 12px; margin: 6px 0 0; font-size: var(--fs-mini); color: var(--ink-2); }
.fix-draft .under { margin: 8px 0 0; font-size: var(--fs-meta); color: var(--ink-2); }

/* THE ACTION STRIP CLOSES THE ROW and spans both columns: the verb, the
   quiet refusal beside it, and the sentence that a refusal writes
   nothing on its own line. */
.fix-actions {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 8px 16px;
  flex-wrap: wrap;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.fix-actions form { margin: 0; }
.fix-actions .costs { flex-basis: 100%; font-size: var(--fs-meta); color: var(--ink-2); }
.fix-actions .costs .mono { color: var(--ink); }

/* THE AGENCY'S DOOR WHERE THE VERB WOULD BE (T5.6). It is the same
   strip laid out as a column, because it holds a field, its provenance
   line, the verb and two notes rather than two buttons. */
.fix-actions.send-client { flex-direction: column; align-items: stretch; gap: 10px; }
.fix-actions.send-client .field { margin: 0; max-width: 420px; }
.fix-actions.send-client .meta { margin: 0; }

/* ---------------------------------------------------------------------
   S4.3 THE CODE BOX: A DRAFT AND A DIFF ARE ONE OBJECT
   (request section T4.1, lifted unchanged by T4.3)
   --------------------------------------------------------------------- */

/* Mono at the verbatim size, a line a row, a gutter with the line
   number as it will sit in the file, and for a diff the sign. The box
   scrolls SIDEWAYS ON ITS OWN at 390, which is the table law applied to
   the one object on these pages that is not a table. */
.code-box {
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
  overflow: hidden;
}

.code-box .code-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.code-box .code-head .file { font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 500; line-height: 1.4; color: var(--ink); overflow-wrap: anywhere; }
.code-box .code-body { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.code-box table { border-collapse: collapse; width: 100%; font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 400; line-height: 1.6; font-variant-numeric: tabular-nums; }
.code-box td { padding: 0 12px 0 0; white-space: pre; vertical-align: top; border: 0; height: auto; }

/* The line number of the after side: a fixed narrow column that is never
   selected with the text, so a copied block is the lines and not the
   numbering. */
.code-box td.ln { width: 1px; padding: 0 10px 0 12px; text-align: right; color: var(--ink-2); user-select: none; }

/* The gutter sign, one character wide. THE SIGN IS WHAT MAKES THE
   MARKING READABLE WITHOUT COLOUR, which is why it is not optional on a
   diff and absent on a draft, where every line is an addition. */
.code-box td.sg { width: 1px; padding: 0 8px 0 0; color: var(--ink-2); user-select: none; }

/* The line itself. `white-space: pre-wrap` is the request's, so a long
   line wraps at the box's width instead of clipping; the box still
   scrolls for the one that cannot wrap. */
.code-box td.txt { width: 100%; padding-right: 16px; white-space: pre-wrap; overflow-wrap: anywhere; }

.code-box tr.ctx td { color: var(--ink-2); }
.code-box tr.add td { background: var(--ok-bg); color: var(--ink); font-weight: 500; }
.code-box tr.add td.sg { color: var(--ok); font-weight: 600; }
.code-box tr.del td { background: var(--bad-bg); color: var(--ink); font-weight: 500; }
.code-box tr.del td.sg { color: var(--bad); font-weight: 600; }
.code-box tr.new td { background: var(--ok-bg); color: var(--ink); font-weight: 500; }
.code-box tr.new td.ln { background: var(--ok-bg); font-weight: 400; }

/* A zebra is a reading aid on a record of many rows and noise on a file:
   `.app-main .table tbody tr:nth-child(even) td` would have striped
   every other line of somebody's robots.txt. OVERTURNED HERE, for this
   one object, and nowhere else. */
.app-main .code-box table tbody tr:nth-child(even) td { background: none; }
.app-main .code-box table tbody tr.add:nth-child(even) td,
.app-main .code-box table tbody tr.new:nth-child(even) td { background: var(--ok-bg); }
.app-main .code-box table tbody tr.del:nth-child(even) td { background: var(--bad-bg); }

.code-box .code-foot {
  padding: 8px 12px;
  border-top: 1px solid var(--line);
  background: var(--surface);
  font-size: var(--fs-mini);
  line-height: 1.5;
  color: var(--ink-2);
}

/* A draft that is a paragraph rather than lines: the same box, prose in
   the measured face, wrapping. */
.code-box .prose { padding: 10px 12px; font-family: var(--font-mono); font-size: var(--fs-body); line-height: 1.6; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; margin: 0; }
.code-box > pre.mono { margin: 0; padding: 10px 12px; font-size: var(--fs-meta); line-height: 1.5; overflow-x: auto; }

/* ---------------------------------------------------------------------
   S4.4 THE COPY DOOR (request section F10)
   --------------------------------------------------------------------- */

/* The strip under the code box: no ground of its own, the box and its
   controls stacked. */
.fix-draft .copy-door { margin-top: 10px; }

/* THE DRAFT AS TEXT, READY TO SELECT. It is readonly, so it never reads
   as a field that wants filling in: the ordinary ink on the surface
   ground, not the input ground. The whole of the fallback is in this
   box, which is why it is drawn whether or not the clipboard can be
   reached. */
.fix-draft .copy-field {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--ink);
  resize: vertical;
}

.fix-draft .copy-field:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }

.fix-draft .copy-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}

/* The word after a press. It is a live region, so it is never hidden by
   this sheet: with nothing in it, it simply has nothing to draw. */
.fix-draft .copy-strip .said { font-size: var(--fs-mini); color: var(--ok); }
.fix-draft .copy-strip .hint { flex-basis: 100%; font-size: var(--fs-mini); line-height: 1.5; color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S4.5 THE NEVER-DRAFT CARD (request section T4.3)
   --------------------------------------------------------------------- */

/* Five rows: the kind in a fixed left column, what it is and its
   citation beside it. It is the printed refusal, so the citation is a
   line of the row and never a footnote somewhere else. */
.never .never-row {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 4px 20px;
  padding: 12px var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.never .never-row:first-child { border-top: 0; }
.never .never-row .k { font-weight: 500; }
.never .never-row .k small { display: block; font-weight: 400; font-size: var(--fs-meta); color: var(--ink-2); }
.never .never-row .why { min-width: 0; color: var(--ink-2); }
.never .never-row .why .cite { display: block; margin-top: 4px; font-size: var(--fs-meta); }
.never .never-row .why .cite a { color: var(--ink); }

/* ---------------------------------------------------------------------
   S4.6 THE RAIL: WHAT IS LEFT, AND THE GUIDANCE CARD
   (request section T4.3)
   --------------------------------------------------------------------- */

/* One row a draft, the title on the left and the minutes on the right in
   the measured face, and the sum under a 2px ink rule, which is the
   activation card's own arithmetic drawn on the desk. */
.rail .left-list { list-style: none; margin: 0; padding: 0; }

.rail .left-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 12px;
  align-items: baseline;
  padding: 8px 0;
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
}

.rail .left-list li:first-child { border-top: 0; padding-top: 0; }
.rail .left-list li a { color: var(--ink); }
.rail .left-list li.on a { font-weight: 500; }
.rail .left-list .min { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink-2); white-space: nowrap; }
.rail .left-list li.sum { margin-top: 4px; border-top: 2px solid var(--ink); font-weight: 600; }
.rail .left-list li.sum .min { color: var(--ink); font-weight: 600; }

/* `.card.guide` on panel ground and `.guide .two` as a stack are already
   in the baseline at the mockup's own numbers, so this region adds
   nothing to either: the two lists sit one above the other in a 265px
   rail because two columns there would be 120px each. */

/* ---------------------------------------------------------------------
   S4.7 THE QUESTION BEFORE THE CLICK, AND ITS FROSTED SCRIM
   (request section T4.3)
   --------------------------------------------------------------------- */

/* THE SCRIM IS A PAGE STATE, NOT A SCRIPT. The desk behind it carries
   `inert` and this is what says so visually: frosted white, because the
   page under it is paper and a black wash would be somebody else's
   product. The shipped `.scrim` and `.dialog` in site.css draw the box;
   `.page` is the whole-document variant this desk uses, so a full page
   shot shows the frost over the desk. */
.scrim.page { position: fixed; inset: 0; overflow-y: auto; }

/* The four parts of the question: a mini label and its answer, a
   hairline between them. */
.dialog .confirm-list { margin-top: var(--s4); border-top: 1px solid var(--line); }

.dialog .confirm-list > div {
  display: grid;
  grid-template-columns: 112px minmax(0, 1fr);
  gap: 2px 14px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body);
}

.dialog .confirm-list .mini { padding-top: 3px; }
.dialog .confirm-list p { margin: 0; }
.dialog .confirm-list p + p { margin-top: 4px; }

/* The lines being approved, inside the question. Its own scroll rather
   than a clipped line, which is the code box's rule asked of a `pre`. */
.dialog .confirm-list pre.lines {
  margin: 6px 0 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--ink);
  white-space: pre;
  overflow-x: auto;
}

.dialog .row-note { margin-top: 12px; font-size: var(--fs-meta); color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S4.8 THE APPROVALS LEDGER (request section T4.1)
   --------------------------------------------------------------------- */

/* THE CARD IS THE LEDGER'S BOX, so its padding goes and every row
   carries the card's own inset instead. It is the answers table's
   grammar, which is what makes this page the customer's twin of
   /admin/audit rather than a second table to learn. */
.ledger { padding: 0; }
.ledger > .card-head { margin: 0; padding: var(--s5) var(--s5) var(--s4); }

/* And the foot does not bleed, because the card pads nothing (the S2
   lesson, `.answers > .card-foot`). */
.ledger > .card-foot { margin: 0; }

/* SIX COLUMNS, ONE MEASURE, shared by the head strip and every row:
   when, kind and target, who, result, proof, and the caret. */
.ledger .cols,
.ledger details.row > summary {
  grid-template-columns: 150px minmax(0, 1.25fr) minmax(0, 1fr) minmax(0, 1.25fr) 96px 24px;
}

.ledger .cols {
  display: grid;
  gap: 12px;
  align-items: center;
  min-height: 36px;
  height: auto;
  padding: 6px var(--s5);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink-2);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.ledger .cols span { min-width: 0; }

/* TOP ALIGNED, NOT CENTRED. Three of the five cells are two or three
   lines and a centred row leaves the date floating beside a paragraph. */
.ledger details.row > summary { align-items: start; min-height: 44px; padding: 8px var(--s5); }

.ledger .when { text-align: left; line-height: 1.5; color: var(--ink); font-size: var(--fs-body); white-space: normal; }
.ledger .when small { display: block; font-family: var(--font-mono); font-size: var(--fs-mini); font-weight: 400; line-height: 1.4; color: var(--ink-2); }

.ledger .kind { min-width: 0; font-weight: 500; }
.ledger .kind small { display: block; font-weight: 400; font-size: var(--fs-meta); color: var(--ink-2); }

/* The file, the page or the field. It is the longest cell on the row, so
   it wraps at any character rather than clipping or pushing its
   neighbours. */
.ledger .kind .target { display: block; margin-top: 2px; font-family: var(--font-mono); font-size: var(--fs-mini); font-weight: 400; line-height: 1.5; color: var(--ink-2); overflow-wrap: anywhere; }

/* The negative word spacing the mockup's actor cell carries, so an
   address does not push the column. */
.ledger .actor { min-width: 0; font-size: var(--fs-body); word-spacing: -0.02em; }
.ledger .actor small { display: block; font-size: var(--fs-meta); color: var(--ink-2); word-spacing: normal; overflow-wrap: anywhere; }

.ledger .result { min-width: 0; font-size: var(--fs-meta); line-height: 1.45; color: var(--ink-2); }
.ledger .result .chip { margin-bottom: 2px; }

.ledger .proof { font-family: var(--font-mono); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); white-space: nowrap; }

/* AND A ROW WITH NOTHING READ BACK YET SAYS SO IN WORDS. It is a
   sentence and not a figure, so it takes the spoken face and it wraps
   inside its own column: nowrap on four words is what pushed the printed
   ledger 44px past the paper at 1400. */
.ledger .proof.none { font-family: var(--font-pub); white-space: normal; }

/* The chevron of a ledger row. `.answers .caret` is scoped to the
   answers table, so this row's own disclosure mark is drawn here. */
.ledger details.row > summary .chevron { color: var(--ink-2); justify-self: end; margin-top: 4px; flex: none; transition: transform var(--dur-1) var(--ease); }
.ledger details.row[open] > summary .chevron { transform: rotate(180deg); }

/* THE UNDO HALF OF A PAIR, ONE STEP QUIETER. Panel ground and the kind
   in secondary, so a ledger reads as pairs at a glance. No colour: an
   undo is not a warning. */
.ledger details.row.undo > summary { background: var(--band); }
.ledger details.row.undo > summary .kind { color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S4.9 THE OPENED ROW: THE FACTS, THE DIFF, THE ATTEMPTS AND THE DOOR
   (request sections T4.1 and T4.3)
   --------------------------------------------------------------------- */

/* Two columns at 1400, the facts at about 40 hundredths of the width and
   the diff beside them; one column at 900 and under. It is an inset box
   on panel ground so an opened row is read as one thing. */
.arow-body {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 16px 24px;
  align-items: start;
  margin: 0 var(--s5) var(--s4);
  padding: 14px var(--s4) var(--s4);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
}

.arow-body > div { min-width: 0; }
.arow-body h3 { font-size: var(--fs-meta); font-weight: 600; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.06em; margin: 0 0 6px; }

/* THE FACTS AS A DEFINITION GRID. `.app-main .kv` is a flex row with the
   value on the right and never wrapping, which is right for a figure in
   a rail and wrong for a sixty four character digest in a 300px column,
   so `.kv.def` is the same rows laid out as label over value. */
.arow-body .kv.def {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 2px 12px;
  padding: 6px 0;
  font-size: var(--fs-meta);
  border-top: 1px solid var(--line);
}

.arow-body .kv.def:first-of-type { border-top: 0; padding-top: 0; }
.arow-body .kv.def > span:first-child { color: var(--ink-2); }
.arow-body .kv.def b { white-space: normal; text-align: left; overflow-wrap: anywhere; font-weight: 500; }
.arow-body .kv.def .meta { min-width: 0; }

/* The whole digest under its label. It breaks anywhere, so a sha never
   widens the column it sits in. */
.arow-body .sha { margin: 2px 0 0; font-size: var(--fs-mini); line-height: 1.5; overflow-wrap: anywhere; }

/* WHAT HAPPENED AFTER THE CLICK, IN ORDER. Full width inside the opened
   row, on the panel ground, under a hairline. */
.arow-body .events,
.arow-body .after { grid-column: 1 / -1; margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--line); }

.approvals-page .event-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.approvals-page .event-list li { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 2px 12px; font-size: var(--fs-mini); line-height: 1.5; }
.approvals-page .event-list li .when { font-family: var(--font-mono); color: var(--ink-2); }
.approvals-page .event-list li .what small { display: block; font-size: var(--fs-mini); color: var(--ink-2); }

/* WHAT CAME BACK, VERBATIM. About eight lines with its own scroll, so a
   2KB body does not push the row off the screen, and nothing of it is
   thrown away. */
.approvals-page .event-list pre.excerpt {
  grid-column: 1 / -1;
  margin: 4px 0 0;
  padding: 8px 10px;
  max-height: 13em;
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  line-height: 1.5;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.approvals-page .event-list li .meta.mono { grid-column: 1 / -1; font-size: var(--fs-mini); }

/* THE STRIP UNDER BOTH COLUMNS: the quiet door on the left when there is
   one, the sentence beside it. Two doors sit side by side and stack at
   390 with the sentence under them (F10). */
.arow-body .undo-strip {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 8px 16px;
  flex-wrap: wrap;
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.arow-body .undo-strip form { margin: 0; }

/* THE ELEVEN COLUMNS AS LABEL AND PROSE. It is the one `.kv` in the rail
   whose right side is a sentence rather than a figure, so it is a grid
   and the value wraps. */
.rail .holds .kv { display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 2px 12px; }
.rail .holds .kv > span:first-child { font-weight: 500; color: var(--ink); }
.rail .holds .kv > span:last-child { color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S4.10 THE HONEST READING AFTER A FIX (T4.5's markup, which wrote no
   request of its own; the mockup's `.attrib` under the shipped names)
   --------------------------------------------------------------------- */

/* Two counts with their n and their usual ranges, the week the fix
   landed, and the sentence that says whether the two ranges separate. It
   never says why, and the line under it says so out loud. */
.after-row { font-size: var(--fs-body); }
.after-row > .mini { display: block; margin-bottom: 6px; }
.after-row .meta { margin: 0 0 4px; }

/* A LITTLE SPACE OVER THE WHO LINE (T5.6). It is the same size and
   colour as the two lines above it and reads tight against them. */
.after-row .meta.who { margin-top: 8px; }

.after-row .pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 10px; }
.after-row .pair .reading { padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--surface); gap: 0; }
.after-row .pair .reading .mini { display: block; margin-bottom: 6px; }
.after-row .pair .reading .count { display: block; }
.after-row .pair .reading .usual { margin-top: 6px; }
.after-row .verdict { margin: 12px 0 0; font-size: var(--fs-body); }
.after-row .verdict + .meta { margin-top: 6px; }

/* The same block inside an opened desk row spans the two columns. */
.fix-body .after-row { grid-column: 1 / -1; padding-top: 14px; border-top: 1px solid var(--line); }

/* The ledger's own reading block, which is a heading and two sentences. */
.arow-body .after p { margin: 0 0 4px; font-size: var(--fs-body); }
.arow-body .after p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------------
   S4.11 THE GOOGLE CARD (T4.4's markup, which wrote no request of its
   own; the mockup's `.connect`, `.asks` and `.review` lifted)
   --------------------------------------------------------------------- */

.connect { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 20px; align-items: center; padding: 4px 0; }
.connect .cwho { margin: 0; font-size: var(--fs-body); min-width: 0; }
.connect .cwho small { display: block; font-size: var(--fs-meta); color: var(--ink-2); }
.connect .doors { display: flex; gap: 8px; flex: none; }
.connect .doors form { margin: 0; }

.asks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 32px; }
.asks h3 { font-size: var(--fs-meta); font-weight: 600; margin: 0 0 6px; }
.asks ul { margin: 0; padding-left: 16px; font-size: var(--fs-body); display: flex; flex-direction: column; gap: 4px; }

.reviews-pick h3 { font-size: var(--fs-meta); font-weight: 600; margin: 0 0 6px; }
.review-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.review-list li { padding: 12px 14px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-s); }
.review-list li.on { border-color: var(--ink); }
.review-list .r-head { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0 0 8px; font-size: var(--fs-meta); color: var(--ink-2); }

/* The review is the customer's own words, stored as they came, so it is
   set as an answer is. */
.review-list .r-body { margin: 0; font-family: var(--font-mono); font-size: var(--fs-body); line-height: 1.6; color: var(--ink); overflow-wrap: anywhere; }
.review-list .doors { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.review-list .doors form { margin: 0; }
.review-list .doors .hint { font-size: var(--fs-mini); color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S4.12 THE PHONE, 390 AND 768 (the request's own phone pass)
   --------------------------------------------------------------------- */

@media (max-width: 900px) {
  .fix-body { grid-template-columns: minmax(0, 1fr); }
  .arow-body { grid-template-columns: minmax(0, 1fr); }
  .asks { grid-template-columns: minmax(0, 1fr); }
  .after-row .pair { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 760px) {
  /* The open row's two columns stack with the words first and the draft
     scrolling sideways in its own box; the body loses the mark's inset,
     because at 390 there is no room to line anything up under it. */
  .fix-body { padding: 6px var(--s4) 16px; gap: 14px; }
  .fix-actions { align-items: stretch; }

  .never .never-row { grid-template-columns: minmax(0, 1fr); padding: 12px var(--s4); }

  .code-box .code-head { padding: 8px 10px; }

  /* THE LEDGER ROW BECOMES FOUR ROWS INSIDE ITS OWN GRID: when and kind,
     then who and the result, with the caret spanning them. The proof
     leaves the summary and is read in the opened body, where the whole
     digest is printed. */
  .ledger .cols { display: none; }
  .ledger details.row > summary { grid-template-columns: minmax(0, 1fr) 20px; row-gap: 2px; padding: 10px var(--s4); }
  .ledger .when,
  .ledger .kind,
  .ledger .actor,
  .ledger .result { grid-column: 1; }
  .ledger .proof { display: none; }
  .ledger details.row > summary .chevron { grid-row: 1 / span 4; grid-column: 2; align-self: start; margin-top: 0; }

  .arow-body { margin: 0 var(--s3) var(--s3); padding: 12px; }
  .arow-body .kv.def { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .approvals-page .event-list li { grid-template-columns: minmax(0, 1fr); }

  .connect { grid-template-columns: minmax(0, 1fr); }
  .connect .doors { flex-wrap: wrap; }

  .dialog .confirm-list > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* ---------------------------------------------------------------------
   S4.13 THE LEDGER ON PAPER (request section T4.1, the print sheet)
   --------------------------------------------------------------------- */

/* THE APPROVALS PAGE IS A DOCUMENT AND THE FIX DESK IS A WORKING SCREEN,
   and both of them print. What goes is every piece of chrome a printed
   page cannot use: the bar, the sidebar, the tab bar, the tab strip, the
   rail, the title row's doors and the help line. What is left is one
   column at the paper's own width, black on white, with every row open.

   THE ROWS ARE OPENED BY THIS SHEET AND BY THE SCRIPT, so the two cover
   each other: public/js/approvals.js opens them on `beforeprint` for a
   browser that honours it, and the rule below prints the body of a shut
   row for one that does not. Neither is enough alone. */
.print-only { display: none; }

@media print {
  .app-top,
  .app-side,
  .side-collapse,
  .tabbar,
  .tabs,
  .rail,
  .title-row .title-actions,
  .guide.help-rail,
  .scrim { display: none !important; }

  .app-shell { display: block; background: none; }
  .app-main { padding: 0; max-width: none; }
  .with-rail { grid-template-columns: minmax(0, 1fr); }

  /* NO FILLED BUTTON ON PAPER. A printed page has nothing to press, and
     an ink filled rectangle is a quarter of a cartridge. */
  .btn,
  .btn-link,
  .fix-actions,
  .undo-strip form { display: none !important; }

  body.app { background: #ffffff; color: #000000; }
  .card { border-color: #999999; break-inside: auto; box-shadow: none; }

  /* THE LEDGER TABLE AT THE PAPER'S FULL WIDTH, and a page break between
     approvals rather than through one: an approval cut in half over two
     sheets is a receipt nobody can file. */
  .ledger { padding: 0; }
  .ledger details.row { break-inside: avoid; page-break-inside: avoid; }
  .ledger details.row > summary { break-after: avoid; page-break-after: avoid; }
  .ledger details.row > summary .chevron { display: none; }

  /* EVERY ROW PRINTS OPEN, whatever the reader left open on the screen.
     `display` alone cannot do it: a closed `details` hides its body
     through `::details-content { content-visibility: hidden }` in every
     browser that has the pseudo element, and a display on the child
     inside it changes nothing. So both models are answered, the modern
     one first. */
  details::details-content { content-visibility: visible !important; block-size: auto !important; }

  .approvals-page details.row > .arow-body,
  .approvals-page details.fix > .fix-body,
  .fixes-page details.fix > .fix-body { display: grid; }

  /* The id, the timestamps and the digests stay in the measured face:
     they are what a printed receipt is checked against. */
  .ledger .when small,
  .ledger .proof,
  .arow-body .sha,
  .arow-body .kv.def b { font-family: var(--font-mono); }

  /* THE TINTS ARE FORCED. The gutter sign carries the meaning, but on a
     long file the ground is what makes it readable, and a printer drops
     backgrounds unless it is told not to. */
  .code-box { break-inside: avoid; page-break-inside: avoid; }
  .code-box tr.add td,
  .code-box tr.new td { background: #e6f3ea !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .code-box tr.del td { background: #fbe4e1 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  /* The one line that exists only on paper: where this copy came from
     and the day it was made. */
  .print-only { display: block !important; padding: 0 var(--s5) var(--s2); }
}

/* ---------------------------------------------------------------------
   S4.14 REDUCED MOTION (the ruling's section 5, in its own order)
   --------------------------------------------------------------------- */

/* Normal motion in this region is two turns of 150ms and nothing else.
   Under reduced motion the transition goes and the browser draws the
   open mark already turned, which is what the rule was animating. */
@media (prefers-reduced-motion: reduce) {
  .desk-row .go,
  .ledger details.row > summary .chevron { transition: none; }
}

/* =====================================================================
   ==== P6R-S5 ====

   STAGE 5, THE WORKSPACE LAYER: THE CLIENT TABLE AND ITS TILES, THE
   SWITCHER POPOVER AND ITS PHONE SHEET, THE MEMBERS PAGE, THE IMPORT
   PREVIEW, THE PAST IMPORTS PAGE, THE PITCH TOOL AND THE KIND CARD
   (T5.8).

   THE SOURCE. design/mockups/phase6r/s5/parts/s5.css with s5/NOTES.md
   section 5, and design/css-requests-phase6r-s5.md, which is the
   contract T5.2, T5.3, T5.4, T5.5, F14 and T5.6 wrote for this region.
   The three names the NOTES deliberately kept off the obvious ones are
   kept here too (decision 14): `.capped` and not `.cap`, `.prospect` and
   not `.who`, `.addr` for an address cell.

   THE SPELLINGS ARE TRANSLATED ON THE WAY IN exactly as the S4 region
   above translates them, and `.tbl` in `.scroll > .tbl-box` is the
   shipped `.card-table > table.table`.

   WHAT THIS REGION DOES NOT DO. The client's own approval page and the
   pitch state of the public results page are drawn on the PUBLIC sheet
   (design/tokens.css), which belongs to another lane while this one
   runs, so neither is in here and both are named in the ruling's
   "Built as, Stage 5" with the file that owns them.

   NOBODY IS COUNTED. There is no rule in here that draws a number of
   people against a number bought, and none may be added: people are
   unlimited and what is sold is client slots.
   ===================================================================== */

/* ---------------------------------------------------------------------
   S5.1 THE TOP BAR'S SECOND CRUMB AND THE SWITCHER'S TRIGGER
   (request section T5.2, the switcher popover)
   --------------------------------------------------------------------- */

/* THE WORKSPACE, THEN THE CLIENT. On a client's page the bar reads mark,
   workspace, client, and the workspace crumb is the way back to the list
   from every page of every client. It is quieter than the client beside
   it, because the client is what the page is about. It carries `.extra`
   in the markup, so at 900 and under it and its slash leave the bar and
   the phone keeps the mark, the client and the avatar. */
.app-top .crumb.ws { font-weight: 400; color: var(--ink-2); }
.app-top .crumb.ws:hover { color: var(--ink); text-decoration: none; }

/* THE SWITCHER'S SUMMARY IS THE CRUMB. The shipped rule above already
   draws the 30px trigger with its 8px corners and its panel ground on
   hover; this adds the pressed state while the popover is open, which is
   the one thing a native summary does not say for itself. */
.app-top .switcher > summary.crumb-btn { font-size: var(--fs-body); font-weight: 500; color: var(--ink); white-space: nowrap; }
.app-top .switcher[open] > summary.crumb-btn { background: var(--band); }
.app-top .switcher > summary .chevron { transition: transform var(--dur-1) var(--ease); }
.app-top .switcher[open] > summary .chevron { transform: rotate(180deg); }

/* ---------------------------------------------------------------------
   S5.2 THE POPOVER (request section T5.2)
   --------------------------------------------------------------------- */

/* The shipped `.app-top .sheet` is the account menu, which hangs off the
   right edge of the bar. This one hangs off the crumb, which is on the
   left, and it holds rows rather than a menu. */
.app-top .sheet.pop { left: 0; right: auto; gap: 0; }

/* THE SEARCH FIELD FIRST, AND ONLY PAST TEN CLIENTS. The glyph sits
   inside the field on the left and the field fills the row. */
.app-top .sheet.pop .pop-search { position: relative; margin-bottom: var(--s2); }
.app-top .sheet.pop .pop-search .ico { position: absolute; left: 10px; top: 9px; width: 16px; height: 16px; color: var(--ink-2); pointer-events: none; }
.app-top .sheet.pop .pop-search .input { padding-left: 32px; }

/* "Clients, 12": the mini label, one hairline under it. */
.app-top .sheet.pop .pop-head {
  margin: 0;
  padding: var(--s2) var(--s2) var(--s1);
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}

/* THE ROW: the name at 14 with its town under it, this week's count on
   the right. The shipped `a.two` already stacks the row; the town moved
   out of the count sentence and under the name (D5 decision 10), because
   a town like Birkenhead beside a name like The Bold Street Bakehouse
   truncated on one line. */
.app-top .sheet.pop a { display: grid; grid-template-columns: minmax(0, 1fr) auto 14px; gap: 10px; align-items: center; height: 40px; padding: 0 var(--s2); }
.app-top .sheet.pop a .nm { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-body); font-weight: 500; line-height: 1.2; }
.app-top .sheet.pop a .nm .town { display: block; margin-top: 2px; font-size: var(--fs-mini); font-weight: 400; line-height: 1.2; color: var(--ink-2); }
.app-top .sheet.pop a .cnt { padding-left: 0; font-family: var(--font-mono); font-size: var(--fs-mini); line-height: 1.2; color: var(--ink-2); white-space: nowrap; text-align: right; }

/* THE BUSINESS BEING READ takes the panel ground and the weight from the
   shipped rule; the tick is what says which row it is without asking a
   reader to compare two greys. */
.app-top .sheet.pop a .tick { display: none; width: 14px; height: 14px; color: var(--ink); flex: none; }
.app-top .sheet.pop a[aria-current="page"] .tick { display: block; }

/* The two doors under a hairline, and the sentence under them: what a
   switch does, and when a search field appears. */
.app-top .sheet.pop .pop-doors { margin-top: var(--s1); padding-top: var(--s1); border-top: 1px solid var(--line); }
.app-top .sheet.pop .pop-doors a { grid-template-columns: minmax(0, 1fr); height: 36px; }
.app-top .sheet.pop .pop-foot { margin: var(--s1) 0 0; padding: var(--s2) var(--s2) var(--s1); border-top: 1px solid var(--line); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* The grab bar is drawn only when the popover is a sheet, which is the
   phone block at the foot of this region. */
.app-top .sheet.pop .grab { display: none; }

/* ---------------------------------------------------------------------
   S5.3 THE HEAD WITH ITS CAPACITY TILES (request section T5.2)
   --------------------------------------------------------------------- */

/* THE MEASURE LAW'S FIRST ANSWER, ON THIS PAGE'S HEAD. The reading column
   is capped and the three counts stand beside it, so the cap never
   leaves a hole on its right at 1920. It is a hand written `header`
   because the shipped title row is a grid of eyebrow, heading, actions
   and lede with no room beside it. */
.title-row.with-tiles {
  grid-template-columns: minmax(0, 1fr) minmax(0, 640px);
  align-items: start;
  gap: 20px 32px;
}

.title-row.with-tiles > div { min-width: 0; }
.title-row .lede.capped { max-width: 62ch; }

/* CAPACITY IS A FACT AND THE MOCKUP DRAWS NO METER. Three tiles, each a
   count, the noun under it and one line of what the count is made of.
   No fill, no bar, nothing that empties.

   IT IS `.client-tiles` AND NOT THE MOCKUP'S OWN SHORTER NAME (V2's
   ruling of 2026-09-05). tests/Feature/DeploymentAssetsTest pins that no
   rule under the shorter name exists in either sheet, because ruling A2
   removed the stat-card row from the whole panel and the pin is what
   stops it coming back under its old name. These three are not that
   object: they are three facts beside a title, drawn without a fill, a
   bar or a delta, and they take a name of their own so the pin stays
   exactly as strict as it was. The scoping also keeps them clear of
   `.tally li.tile`, which is a third thing with the same word in it. */
.client-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3); min-width: 0; }

.client-tiles .tile {
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  min-width: 0;
}

.client-tiles .tile .count { display: block; font-family: var(--font-mono); font-size: 24px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.client-tiles .tile .t-label { display: block; margin-top: 6px; font-size: var(--fs-meta); font-weight: 500; line-height: 1.4; }
.client-tiles .tile .t-meta { display: block; margin-top: 2px; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S5.4 THE CLIENT LIST (request section T5.2)
   --------------------------------------------------------------------- */

/* THE ROWS GRAMMAR AND NOT A TABLE (D5 decision 3): the whole row is one
   anchor, which cannot be done inside a `table` without a script. So the
   column strip is aria-hidden furniture over grid rows, exactly as the
   answers table draws its own.

   FIVE TRACKS AND NOT THE MOCKUP'S SIX. The mockup's last track is a
   16px caret and the shipped row draws no caret: the whole row is the
   link and a chevron on it would be a second affordance for the same
   press. Written down here because the request counts six. */
.clients .cols,
.clients a.client-row {
  grid-template-columns: minmax(0, 1.35fr) 128px minmax(0, 1.1fr) 104px minmax(0, 1fr);
}

.clients .cols {
  display: grid;
  gap: 12px;
  align-items: center;
  min-height: 36px;
  padding: 6px var(--s5);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}

.clients .cols span { min-width: 0; }

/* TOP ALIGNED, because three of the five cells are two lines. The focus
   ring is on the row and not on a word inside it, which is the point of
   making the row one anchor. */
.clients a.client-row {
  display: grid;
  gap: 12px;
  align-items: start;
  min-height: 52px;
  padding: 12px var(--s5);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body);
  color: var(--ink);
  text-decoration: none;
}

.clients a.client-row:hover { background: var(--zebra); text-decoration: none; }
.clients a.client-row:last-child { border-bottom: 0; }

.clients .c-name { min-width: 0; font-weight: 500; }
.clients .c-name small { display: block; font-weight: 400; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

.clients .c-plan { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.clients .c-plan small { font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

.clients .c-week { min-width: 0; font-family: var(--font-mono); font-size: var(--fs-meta); font-variant-numeric: tabular-nums; }
.clients .c-week small { display: block; font-family: var(--font-pub); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* A CELL WITH NO COUNT IS A SENTENCE, never a zero and never a dash: a
   week that was not sampled and a week that named nobody are two
   different facts and a 0 would print them the same. */
.clients .c-week .none,
.clients .c-last .none { font-family: var(--font-pub); font-size: var(--fs-meta); color: var(--ink-2); }

.clients .c-last { min-width: 0; font-family: var(--font-mono); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* The cell's own label, which the column strip already carries from 900
   up and which the phone block below turns back on. */
.clients .c-last .ph { display: none; font-family: var(--font-pub); }

.clients .c-setup { min-width: 0; font-size: var(--fs-meta); color: var(--ink-2); }
.clients .c-setup.done { color: var(--ok); }

/* THE PLAN CHIP: the plan word with its price beside it, no dot, on the
   shipped chip's ground. It is a label and not a state, which is why the
   dot goes, the same reason `.chip.src` drops it. */
.chip.chip-plan { color: var(--ink); background: var(--band); }
.chip.chip-plan::before { display: none; }

/* A CLIENT NO SLOT PAYS FOR IS A FACT AND NOT AN ALARM. It is the
   shipped quiet chip with its own name and a little air above it, so it
   sits under the plan chip rather than beside it. Nothing red, no icon. */
.chip.chip-unpaid { margin-top: 1px; }

/* ---------------------------------------------------------------------
   S5.5 THE WORKSPACE KIND CARD (request section T5.2)
   --------------------------------------------------------------------- */

/* THE CARD THAT IS DRAWN BUT NOT OFFERED. The input inside it is
   disabled, so the browser already refuses it; this is what says so on
   the screen. The reason reads as part of the card and never as an
   error, so there is no colour in here at all. */
.choice.off { background: var(--band); cursor: not-allowed; }
.choice.off .t,
.choice.off .m { color: var(--ink-2); }

/* The capacity sentence under the two cards, and it never says how much
   is left. */
.kind-note { margin: 12px 0 0; font-size: var(--fs-meta); color: var(--ink-2); max-width: 70ch; }

/* ---------------------------------------------------------------------
   S5.6 THE MEMBERS PAGE (request section T5.3)
   --------------------------------------------------------------------- */

/* THE PUBLISHED ROLE MATRIX. The first column takes the sentence and the
   three role columns are equal and narrow with their answers centred. It
   scrolls in the shipped card table at 390 and it STACKS NOWHERE: a
   matrix that stacks is twelve headings and thirty six lines. */
.table.matrix { table-layout: auto; }
.table.matrix th,
.table.matrix td { text-align: center; }
.table.matrix th:first-child,
.table.matrix td:first-child { text-align: left; white-space: normal; width: 100%; }
.table.matrix th:not(:first-child),
.table.matrix td:not(:first-child) { width: 96px; }
.table.matrix td { height: 34px; font-size: var(--fs-meta); }

/* THE ROW THAT IS NOBODY'S. Approving is never a role, so this row spans
   the three of them with the sentence that says so. It is a fact about
   the product, not a warning: panel ground, secondary ink, no colour and
   no icon. */
.table.matrix td.nobody { text-align: left; white-space: normal; background: var(--band); color: var(--ink-2); font-size: var(--fs-meta); }

/* IT IS CALLED nobody AND NOT never, AND THE RENAME IS THE FIX (F20 item
   9). design/tokens.css owns `.never { display: grid }` for the "what we
   never do" block on /how-it-works, and every stylesheet is read by every
   surface, so that rule reached this cell and turned a colspan=3 table
   cell into a grid box: the browser dropped it out of the table's column
   arithmetic and laid it out at 80px, with its 106 character sentence
   overflowing on two lines, at 390, 1400 and 1920 alike. The other two
   collisions of the same shape (.shape-choice, .plan-door) were renamed in
   the markup for the same reason; a second name for the same object is
   cheaper than a wider selector that the next surface breaks again.

   AND IT IS NOT 96px WIDE EITHER. The role column rule above hits this
   cell too, because it is not the first child, and a colspan=3 cell held
   to 96px would break its sentence one word a line whatever the display
   said. The span decides its width; the rule does not.

   THE FLOOR IS THE THREE COLUMNS THIS SHEET ALREADY DECLARES. `width` is
   a suggestion in an auto laid out table and the first column's 100% beats
   it, so the three role columns collapse to the width of the word "Owner"
   and the sentence spanning them came out at 176px and seven lines at
   1400 and at 1920. A minimum is not a suggestion: 288px is the 96px this
   sheet gives each role column, three times, so the cell asks for exactly
   the room the matrix was drawn with and the first column keeps the rest. */
.table.matrix td.nobody { width: auto; min-width: 288px; }
.app-main .table.matrix tbody tr:nth-child(even) td.nobody { background: var(--band); }

/* AN OUTSTANDING INVITATION IS THE SAME TABLE, one step quieter. No
   stripe and no tint that reads as an error: an invitation nobody has
   accepted yet is a fact about a Tuesday, not a problem. */
.table tr.invited td { color: var(--ink-2); }
.table tr.invited td:first-child { font-weight: 400; }

/* THE ROW'S OWN DOOR, AND IT TAKES TWO PRESSES. The summary is drawn as
   the shipped quiet button at the small size with the native triangle
   gone; the panel that opens is what grows, so the table does not widen
   under it. */
details.row-edit > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--control-s);
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  font-size: var(--fs-meta);
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}

details.row-edit > summary::-webkit-details-marker { display: none; }
details.row-edit > summary:hover { background: var(--band); }
details.row-edit[open] > summary { background: var(--band); }

/* THE PANEL IS EVERY CHILD BUT THE SUMMARY, drawn as one box: the role
   radios, the client checklist, then the sentence and the Remove button
   under a hairline. There is no wrapper element to hang it on, so the
   box is made of the children's own edges. */
details.row-edit > :not(summary) {
  margin: 0;
  padding: 0 12px;
  min-width: 240px;
  background: var(--band);
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  text-align: left;
  white-space: normal;
}

details.row-edit > :not(summary):first-of-type {
  margin-top: var(--s3);
  padding-top: 12px;
  border-top: 1px solid var(--line);
  border-radius: var(--radius-s) var(--radius-s) 0 0;
}

details.row-edit > :last-child {
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  border-radius: 0 0 var(--radius-s) var(--radius-s);
}

details.row-edit > p { padding-top: 12px; border-top: 1px solid var(--line); font-size: var(--fs-meta); color: var(--ink-2); }
details.row-edit .submit { margin-top: var(--s3); }

/* THE CLIENT CHECKLIST under the two scope radios. Indented by the
   checkline's own gutter so it reads as belonging to the second radio,
   and capped in height past about twelve clients so the button below it
   is never pushed off the screen. */
.client-checks {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 20px;
  margin: var(--s1) 0 0 26px;
  padding: 12px 14px;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
}

/* The line above the ticks: a client added later is not in this list
   until somebody adds it here. */
.client-checks .meta { grid-column: 1 / -1; margin: 0 0 var(--s1); padding-bottom: 6px; border-bottom: 1px solid var(--line); font-size: var(--fs-meta); color: var(--ink-2); }
.client-checks .checkline { font-size: var(--fs-body); }
.client-checks .checkline .meta { grid-column: auto; display: block; margin: 0; padding: 0; border: 0; font-size: var(--fs-meta); color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S5.7 THE IMPORT PREVIEW AND ITS COMMIT CARD (request section T5.4)
   --------------------------------------------------------------------- */

/* SIX NARROW COLUMNS AND ONE WIDE ONE. "Reads as" is the one cell in the
   panel that WRAPS, against the shipped nowrap, because the sentence is
   the point of the row; the five file columns are the ones to narrow if
   anything has to give. */
.import-table td.reads {
  white-space: normal;
  min-width: 260px;
  padding-top: 8px;
  padding-bottom: 8px;
  font-size: var(--fs-meta);
  color: var(--ink-2);
}

.import-table td.reads .chip { margin-bottom: var(--s1); }
.import-table td.reads small { display: block; font-size: var(--fs-meta); line-height: 1.5; }

/* A DUPLICATE AND AN UNREADABLE ROW READ IN SECONDARY AND NOTHING ELSE.
   No red, no ground, no border: nothing on this surface is a warning
   (D5 decision 6), and the chip and the sentence carry the meaning, so
   colour is never alone. */
.import-table tr.dup td,
.import-table tr.bad td { color: var(--ink-2); }

/* The website cell breaks anywhere, so a long host never widens the
   table; the placeholder is a word and not an address, so it stays in
   the spoken face. */
.import-table td.web { font-family: var(--font-mono); font-size: var(--fs-meta); overflow-wrap: anywhere; }

/* THE EIGHTH COLUMN, WHICH IS THE SHAPE (T6.0, named by T6.6 on
   2026-09-05). "Local shop" and "Online only" are two words and the cell
   is never three lines of them, so it does not wrap; it is one of the
   narrow file columns and it gives its width to the state column rather
   than taking width from it, which is the request's own instruction
   about which columns yield.

   MEASURED WITH EIGHT COLUMNS AND EIGHT ROWS: 887px of table in an 887px
   box at 1400 and 941 in 941 at 1920, so nothing scrolls and nothing is
   cut; 1073 in a 734px box at 768, where the shipped `.card-table`
   scrolls it sideways in its own box and the page does not move; and at
   390 the shipped `.table.stack` makes every row a labelled record, so
   there is no table to scroll at all. */
.import-table td.shape { white-space: nowrap; }

/* THE COMMIT FORM: the attestation, the sentence, the one filled verb,
   stacked. It is a form inside a card and not a card of its own. */
.commit { display: grid; gap: var(--s4); }

/* The sentence over the button. It is a paragraph and not a card, so it
   has no ground and no border: the card is the box. `.sum` is taken by
   the answers table, which is why this one is named for what it is. */
.commit-sum { margin: 0; font-size: var(--fs-body); }
.commit-sum small { display: block; margin-top: var(--s1); font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* ---------------------------------------------------------------------
   S5.8 THE PITCH TOOL (request section T5.5)
   --------------------------------------------------------------------- */

/* The crumb over the h1, which the S1 wizard has no need of because it
   has no section above it. */
.wiz .pitch-crumb {
  margin: 0 0 6px;
  font-family: var(--font-mono);
  font-size: var(--fs-mini);
  font-weight: 500;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-2);
}

/* THE PITCHES LIST IS NOT A THIRD COLUMN OF THE WIZARD. It is full width
   under the two column form with the frame's own gap over it, at every
   width; the table inside it is the shipped one and stacks at 390 on its
   own. */
.pitch-list { margin-top: var(--gap); }

/* The last cell of a row: the Open link and the quiet door beside it on
   one line at 1400, stacked inside the stacked row at 390. */
.pitch-doors { white-space: nowrap; }
.pitch-doors form { display: inline-block; margin: 0 0 0 10px; }
.pitch-doors .btn { min-height: 28px; padding: 0 10px; }
.pitch-doors .meta { margin-left: 10px; }

/* ---------------------------------------------------------------------
   S5.9 THE PHONE, 390 AND 768
   --------------------------------------------------------------------- */

@media (max-width: 900px) {
  /* THE HEAD IS ONE COLUMN AND THE TILES FOLLOW THE LEDE. */
  .title-row.with-tiles { grid-template-columns: minmax(0, 1fr); }

  /* THE SWITCHER IS A SHEET FROM THE BOTTOM (the ruling's section 2: no
     desktop-only wall). The sticky bar is a stacking context at 10 and
     the tab bar sits at 20, so an open sheet drew under the tabs; the
     bar rises while it is open and falls back the moment it shuts. */
  .app-top:has(.switcher[open]) { z-index: 45; }

  .app-top .switcher[open]::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 39;
    background: rgba(255, 255, 255, 0.72);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }

  .app-top .sheet.pop {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    z-index: 40;
    width: auto;
    max-width: none;
    max-height: 80vh;
    overflow-y: auto;
    padding: 10px 10px var(--s4);
    border-radius: 14px 14px 0 0;
  }

  .app-top .sheet.pop .grab { display: block; width: 36px; height: 4px; margin: 0 auto 10px; border-radius: 2px; background: var(--line); flex: none; }
}

@media (max-width: 760px) {
  /* THE TILES BECOME THREE ROWS: the count on the left, the label and
     its line on the right. */
  .client-tiles { grid-template-columns: minmax(0, 1fr); gap: var(--s2); }
  .client-tiles .tile { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 0 12px; align-items: center; padding: 10px 14px; }
  .client-tiles .tile .count { grid-row: 1 / span 2; font-size: 22px; }
  .client-tiles .tile .t-label { grid-column: 2; margin-top: 0; }
  .client-tiles .tile .t-meta { grid-column: 2; }

  /* THE CLIENT ROW STACKS, each cell on its own line with its own label,
     and the column strip goes. Nothing on this page is a table, so
     nothing scrolls sideways here at any width. */
  .clients .cols { display: none; }
  .clients a.client-row { grid-template-columns: minmax(0, 1fr); row-gap: var(--s1); padding: 12px var(--s4); }
  .clients .c-last .ph { display: inline; }
  .clients .c-week small { display: inline; margin-left: 6px; }

  /* The card head's three doors wrap under its title: four buttons are
     wider than 358. */
  .clients > .card-head .card-actions { flex: 1 1 100%; margin-left: 0; }

  .client-checks { grid-template-columns: minmax(0, 1fr); margin-left: 0; }

  details.row-edit > :not(summary) { min-width: 0; }

  .pitch-doors { white-space: normal; }
  .pitch-doors form { display: block; margin: var(--s2) 0 0; }
}

/* ---------------------------------------------------------------------
   S5.10 REDUCED MOTION
   --------------------------------------------------------------------- */

/* One turn of 150ms was added by this region. Under reduced motion the
   transition goes and the browser draws the open chevron already
   turned. */
@media (prefers-reduced-motion: reduce) {
  .app-top .switcher > summary .chevron { transition: none; }
}

/* ---------------------------------------------------------------------
   S5.11 THE WORKSPACE'S TABLE ROW IS 36px (V2's finding of 2026-09-05)

   THE RULING'S SECTION 2 SAYS "table rows 36 with zebra at half on the
   panel", and no table in the panel measured 36. site.css draws the
   shared table at the public sheet's own size, which is the body
   sentence at 14 / 1.55 with 9px of padding: 40 to 41px on the peers
   and named pages, and 33 on the receipts page's dense variant, so the
   one number the ruling names was true nowhere.

   IT IS FIXED HERE AND NOT THERE. design/tokens.css draws one table for
   the public site, the workspace and the operator panel, and the public
   sheet's own rows are correct at its own size; what was wrong is that
   the workspace inherited a size it does not set. So this is scoped to
   `.app-main`, the App sheet's own frame, and the two other sheets are
   untouched.

   THE CELL IS 13 / 1.4 AND THE HEIGHT IS WHAT DECIDES. 13 times 1.4 is
   18.2, and 8px above and below it comes to 34.2, so the 36 is the
   binding floor and every one line row measures the same 36 whatever
   words are in it; `height` on a table cell is a minimum rather than a
   cap, so a cell with two lines in it grows past it as it should. The
   arithmetic is deliberately left short of 36 rather than made to hit it
   exactly: padding that computed to 36.2 measured 36 on one page and 37
   on the next, and a number that is only nearly right is a number nobody
   can check. The dense variant keeps its own name and lands on the same
   36: it is dense because it holds less, not because its rows are
   shorter than the ruling's.

   AND A STACKED TABLE HAS NO ROWS AT ALL. Under 640 `.table.stack` turns
   every cell into a block and a height on a block is a height and not a
   floor, so the floor is taken away again there and the record reads as
   it always did.
   --------------------------------------------------------------------- */
.app-main .table th,
.app-main .table td {
  height: var(--row);
  padding: 8px var(--s2);
  font-size: var(--fs-meta);
  line-height: 1.4;
}

.app-main .table th { font-size: var(--fs-mini); }
.app-main .table.dense td { padding: 8px var(--s2); font-size: var(--fs-meta); }

/* A CHIP IN A CELL SITS ON THE CELL'S OWN LINE, not on its baseline and
   not above it. The chip is a 20px inline flex box and the cell's strut
   is 18.2, so on the baseline the room a descender wants underneath it
   pushed the row to 38, and aligned to the top the extra 1.8 still made
   it 37. Pulled back by the difference, top and bottom, the chip's box
   IS the line and a row that carries one measures the same 36 as a row
   of plain words. The chip itself is untouched: the same height, the
   same ground, the same dot. */
.app-main .table td > .chip,
.app-main .table td > .status-word { vertical-align: top; margin-top: -1px; margin-bottom: -1px; }

/* The ledger's own rows are grid and not table cells, so the two objects
   agree by measuring the same way rather than by sharing a rule: the
   answers table, the desk and the client list are all built on a 36px
   minimum already. */

@media (max-width: 639px) {
  .app-main .table.stack th,
  .app-main .table.stack td { height: auto; }
}

/* ---------------------------------------------------------------------
   S5.12 A PROSE LEDGER WITH ONE MEASURED CELL IN IT (T2.11's leftover on
   the business billing page, closed here)
   --------------------------------------------------------------------- */

/* `.tally.prose` says the values in this list are sentences and puts them
   back into the spoken face, which is right for "does not count" and
   wrong for the plan change ledger, whose left column is a sentence and
   whose right column is money. The markup already says which is which by
   carrying `.mono` on the cell; this is the sheet believing it. Scoped to
   the App frame, because the public sheet's own figures are set in
   Satoshi by its own reference sheet. */
.app-main .tally.prose li > span.mono:last-child {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "zero" 1;
}

/* =====================================================================
   ==== P9.3 ====

   THE KEYWORDS PAGE: ONE ROW A PHRASE, ONE GOOGLE POSITION A MONDAY
   (T9.3.6, phase 9).

   THE SOURCE. design/mockups/phase9/keywords/parts/p9.css, lifted whole,
   with keywords/NOTES.md sections 3 and 4 for the why of every rule, and
   design/css-requests-phase9.md, which is the contract T9.3.4 wrote for
   this region. Nothing above this banner is rewritten. The markup is
   resources/views/app/keywords/index.blade.php.

   EVERY VALUE IS A TOKEN THE SHIPPED SHEETS ALREADY DECLARE, or one of
   the chrome numbers the App sheet names (36 the table row, 24 the small
   strip, 12 the type floor). No new hex, no new font, no token added to
   design/tokens.css. NO COLOUR RULE BEYOND THE SHIPPED CHIP KINDS: the
   only tinted thing on this page is `.chip.fine` on the AI Overview cell,
   which the baseline already draws, and the accent appears once, on a
   link, which is the accent every link in this sheet already takes.

   WHAT IS NOT RESTATED HERE. The strip (`.weeks.weeks-sm`, `.wcol`,
   `.wcol.now`), the add row (`.qadd`), the hatch, the chips, the card,
   its table box and its foot, the help block and the rail are shipped
   objects; this region adjusts them in place and never draws a second
   one. The seven column measures are the only bare numbers in it, and
   they are measures: NOTES section 2 records what each one was measured
   against at 768, 1400 and 1920.

   THREE THINGS THE MOCKUP SHEET DID NOT CARRY, each with its reason.

   1. `.kw-pos.unread`, the fifth kind of position cell, is added to the
      four kinds' existing selector rather than as a rule of its own,
      which is what design/css-requests-phase9.md asks for. It is the
      Monday that HAS a history and was not read this week, and it is on
      the screen every Monday morning between midnight UTC and the job
      finishing.
   2. `.kwtable .table.stack td.kwtable td.kw-url a` in the mockup sheet
      is a doubled selector that can never match anything. Landed as
      `.kwtable .table.stack td.kw-url a`, which is what it was written
      to say: in a stacked row the value column is flush right, so the
      host and the path under it are too.
   3. The strip rules carry the cell's own class (`td.kw-strip`), so the
      region names every class section 4.2 of docs/phase9/P9.3-keywords.md
      asks for. There is no other strip inside the card, so this is the
      same paint on the same lanes.

   The request's `.kwtable .cols` is the `thead` here (NOTES decision 1):
   the rows are `tr` in a real `table.table.stack`, so site.css's stack
   rule, section 18's scroll box and the harness's clipped table check all
   read the object without a new one being invented for them.
   ===================================================================== */

/* ---------------------------------------------------------------------
   K1 THE TABLE. A real table in the shipped `.card-table` box, which
   scrolls in its own box from 900 down and stacks under 640 through
   site.css's `table.table.stack`. `.kwtable` sits on the card.
   --------------------------------------------------------------------- */

/* SEVEN FIXED TRACKS FROM 640 UP, the phrase taking the remainder. The six
   fixed tracks are 724; at 1400 the card is 889 wide and every text cell
   fits without a scroll (the mockup's measure, section 4.2), at 1920 the
   card is 943. Fixed rather than automatic because an automatic layout
   hands the columns out by their longest word, and at 768 it gave the
   page found 101px and broke the host inside a word. */
@media (min-width: 640px) {
  .kwtable .table { table-layout: fixed; }
  .kwtable .table th:nth-child(2), .kwtable .table td:nth-child(2) { width: 136px; }
  .kwtable .table th:nth-child(3), .kwtable .table td:nth-child(3) { width: 80px; }
  .kwtable .table th:nth-child(4), .kwtable .table td:nth-child(4) { width: 92px; }
  .kwtable .table th:nth-child(5), .kwtable .table td:nth-child(5) { width: 96px; }
  .kwtable .table th:nth-child(6), .kwtable .table td:nth-child(6) { width: 170px; }
  .kwtable .table th:nth-child(7), .kwtable .table td:nth-child(7) { width: 150px; }
}

/* A column head of three words wraps rather than being cut: site.css sets
   every th nowrap for the one-word heads it was written for. */
.kwtable .table th { white-space: normal; vertical-align: bottom; }

/* Every cell top aligned and given the row's own vertical rhythm: 9px of
   the sheet's padding plus the line height puts the phrase's first line
   and the position figure on one baseline. */
.kwtable .table td { vertical-align: top; }

/* THE PHRASE, as typed, in the spoken face at 14 / 500 (a person's own
   words are prose, not chrome; S2A's rule). Under it, the country and the
   device as a fact at 12 in secondary, never bold, even when the stacked
   table sets the first cell to 600. Every cell rule below carries the
   .kwtable prefix because site.css's `.table td` outranks a bare class
   and was setting the phrase to 13 (measured). */
.kwtable td.kw-phrase { font-size: var(--fs-body); line-height: 1.4; font-weight: 500; color: var(--ink); overflow-wrap: break-word; }
.kwtable td.kw-phrase small { display: block; margin-top: 2px; font-size: var(--fs-mini); font-weight: 400; line-height: 1.4; color: var(--ink-2); }

/* THE POSITION CELL. The figure at 13 in the measured face, weight 600,
   in ink: it is the row's fact. Under it, on every read row, the sentence
   that says whose fact it is, at 12 secondary, wrapping and never clipped.
   Left aligned rather than the request's right: the same column prints
   words on other rows and a sentence under every figure, and a right
   aligned figure over a left aligned sentence reads as two cells. */
.kwtable td.kw-pos b { font-family: var(--font-mono); font-size: var(--fs-meta); font-weight: 600; line-height: 1.4; color: var(--ink); font-variant-numeric: tabular-nums; font-feature-settings: "zero" 1; }
.kwtable td.kw-pos small { display: block; margin-top: 2px; font-size: var(--fs-mini); line-height: 1.5; color: var(--ink-2); white-space: normal; overflow-wrap: anywhere; }

/* THE FOUR KINDS THAT ARE NOT A FIGURE, and the fifth the mockup had no
   state for. The not-found, the could-not-read, the first-reading, the
   not-read-yet-this-week and the over-cap words: 13 in the spoken face,
   secondary, no figure, no chip and no colour. None of them is a warning
   about the business, and `.unread` above all is not: it is what the page
   says between midnight UTC on a Monday and the job finishing. The one
   figure inside the words (the depth, the date) keeps the measured face. */
.kwtable td.kw-pos.none, .kwtable td.kw-pos.failed, .kwtable td.kw-pos.first, .kwtable td.kw-pos.unread, .kwtable td.kw-pos.beyond { font-size: var(--fs-meta); line-height: 1.4; color: var(--ink-2); }
.kwtable td.kw-pos .mono { font-variant-numeric: tabular-nums; }

/* LAST MONDAY: the figure or the words, 13, secondary. The measured face
   for the figure only. No sign, no arrow, no colour (ruling R5). */
.kwtable td.kw-last { font-size: var(--fs-meta); line-height: 1.4; color: var(--ink-2); }
.kwtable td.kw-last .mono { font-weight: 500; }

/* THE FOUR MONDAYS: the shipped `.weeks.weeks-sm` at the height the caller
   passes (24), filling its cell rather than the head's 180 cap, and never
   pushed to the cell's end the way the head's strip is. */
.kwtable td.kw-strip .weeks.weeks-sm { max-width: none; width: 100%; justify-self: stretch; }

/* THE AXIS UNDER THE STRIP IS NOT DRAWN HERE. The shipped strip prints
   its first label and "this week" under the columns; in a 92px track the
   two wrap onto four lines and the row doubles. The column head says
   "oldest first", the sentence beside the figure names this Monday, and
   the sr-only line names all four. The markup stays the component's. */
.kwtable td.kw-strip .weeks-x { display: none; }

/* A Monday that was not read draws no column: the lane stays so the
   other three keep their place, the paint goes. A not-found week keeps the
   sheet's 2px stub, which is the questions page's own zero week. Scoped to
   the card, which is where the request records this class living until a
   second surface draws an unread lane. */
.kwtable .wcol.unread { background: transparent; }

/* AI OVERVIEW: the chip alone, dropped 1px so its 20px box centres on the
   phrase's first 22px line. */
.kwtable td.kw-aio { padding-top: 10px; }

/* THE PAGE FOUND: the path at 13 in the measured face, the host first in
   secondary, breaking anywhere so a long path never widens the column. A
   row link keeps the workspace's accent and gains its underline on hover,
   as every link in the sheet does. */
.kwtable td.kw-url { font-family: var(--font-mono); font-size: var(--fs-meta); line-height: 1.4; overflow-wrap: anywhere; }
.kwtable td.kw-url a { color: var(--accent); }
/* The host on its own line above the path, so an eighteen character host
   never breaks inside a word to share a line with a path. */
.kwtable td.kw-url .host { display: block; color: var(--ink-2); }

/* THE DOORS: two text buttons, one under the other in the table column
   (the wide table's last track is 150 and "Ask it as a question" is 118
   on its own), 4px apart; the stacked phone row puts them side by side
   (K5). */
.kw-doors-inner { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s1); }
.kw-doors-inner form { margin: 0; }
.kw-doors-inner .btn-link { min-height: 22px; font-size: var(--fs-meta); }

/* A ROW PAST THE PLAN'S CAP is a fact, not a control: secondary ink on
   the phrase, no chip, no strip, the doors kept. The shipped `.qrow.beyond`
   rule, said for a tr. */
.kwtable tr.beyond .kw-phrase { color: var(--ink-2); font-weight: 400; }

/* ---------------------------------------------------------------------
   K2 THE ADD ROW AND THE CAP LINE, at the head of the card
   --------------------------------------------------------------------- */

/* The S1 `.qadd` row moved from the foot of a card to its head: it keeps
   the input at the App sheet's boundary and the filled button beside it,
   drops the foot's negative bottom margin and its top rule, and lets the
   table's own top rule close it. The card head sits directly above. */
.kw-add {
  margin: 0 calc(-1 * var(--s5)) 0;
  padding: 0 var(--s5) var(--s4);
  border-top: 0;
  align-items: start;
}

/* The field keeps the whole first track; the button never shrinks. */
.kw-add .field { min-width: 0; }
.kw-add > .btn { flex: none; margin-top: 0; }

/* THE FACT LINE under the field: 13 secondary, every number from the plan
   and never typed, the figures in the measured face. It spans the row. */
.kw-cap { grid-column: 1 / -1; margin: 0; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* AT THE CAP the row is the fact line alone: panel ground, the card's
   inset, a hairline under it, bleeding to the card's edges as the add
   row does. No control, because there is nothing to press. */
.kw-cap.full {
  display: block;
  margin: 0 calc(-1 * var(--s5)) 0;
  padding: var(--s3) var(--s5);
  background: var(--band);
  border-bottom: 1px solid var(--line);
}

/* The empty state under an add row: the shipped hatch, with the card's
   inset above it so it does not touch the field. */
.kw-add + .card-body { margin-top: var(--s4); }

/* ---------------------------------------------------------------------
   K3 THE RETIRED FOLD, under the table, inside the card
   --------------------------------------------------------------------- */

/* A native details bleeding to the card's edges, so the page works with
   scripts off. The summary is a 36px row at 13 secondary with the count in
   mono and the sheet's chevron; the rows under it are 36px, hairline
   apart, the phrase, the date retired in mono and the Track again text
   button. */
.kw-retired { margin: 0 calc(-1 * var(--s5)); border-top: 1px solid var(--line); }
.kw-retired > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--row);
  padding: 0 var(--s5);
  font-size: var(--fs-meta);
  color: var(--ink-2);
}
.kw-retired > summary::-webkit-details-marker { display: none; }
.kw-retired > summary:hover { background: var(--zebra); }
.kw-retired > summary .chevron { margin-left: auto; color: var(--ink-2); flex: none; transition: transform var(--dur-1) var(--ease); }
.kw-retired[open] > summary .chevron { transform: rotate(180deg); }
.kw-retired ul { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.kw-retired li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: var(--s1) var(--s4);
  align-items: center;
  min-height: var(--row);
  padding: 6px var(--s5);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-meta);
}
.kw-retired li:last-child { border-bottom: 0; }

/* A ROW WITH NO DOOR IS A ROW OF TWO TRACKS (F22). "Track again" is not drawn
   at the cap or under any of the three refusals, because there is nothing to
   add to; the third track then collapses but the gap in front of it does not,
   and the date stood 16px short of the card's own inset while every other row
   in the card reached it. `:has` is the shipped floor: the App sheet already
   asks it eight times, `.card-table:has(> .table.stack)` among them. */
.kw-retired li:not(:has(form)) { grid-template-columns: minmax(0, 1fr) auto; }

.kw-retired li .rp { color: var(--ink-2); }
.kw-retired li .rd { font-family: var(--font-mono); font-size: var(--fs-mini); color: var(--ink-2); white-space: nowrap; }
.kw-retired li form { margin: 0; }
.kw-retired li .btn-link { min-height: 22px; font-size: var(--fs-meta); }

/* The shipped help block (x-app.help with a FAQ) follows the fold or the
   hatch and keeps its own hairline and 16px above; the card foot after it
   keeps the foot's. Nothing is drawn twice and nothing here restates
   either. */

/* ---------------------------------------------------------------------
   K4 THE RAIL'S FACTS CARD
   --------------------------------------------------------------------- */

/* A value that is a word (the vendor, the market, the device) is not a
   figure: the spoken face at 500, allowed to wrap and right aligned, so
   a market name of thirty characters fits a 265px rail. The figures keep
   the sheet's own `.kv b`. */
.rail .kw-facts .kv .word { font-family: var(--font-pub); font-weight: 500; white-space: normal; text-align: right; }

/* One sentence under the facts when a Monday's readings failed: 13
   secondary, the card's own gap above it. It is a fact about the vendor,
   so it carries no colour. */
.rail .kw-facts .kw-note { margin: 10px 0 0; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }

/* Three sentences in a rail card, at the description size. */
.rail .kw-is p { margin: 0; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-2); }
.rail .kw-is p + p { margin-top: var(--s2); }

/* ---------------------------------------------------------------------
   K5 THE PHONE AND THE MIDDLE WIDTH
   --------------------------------------------------------------------- */

/* Between 640 and 1399 the table scrolls in its box with the edge fade
   (section 18; the card is 736 at 768 and 389 to 889 beside the rail
   from 901). It asks for 880 rather than the sheet's 560, so the phrase
   keeps 156px beside the six fixed tracks, and its three prose cells are
   allowed to wrap where section 18 sets every cell nowrap. Measured at
   768 with 700: the phrase broke inside a word and the sentence ran six
   lines. */
@media (min-width: 640px) and (max-width: 1399px) {
  .kwtable .table { min-width: 880px; }
  .kwtable .table td.kw-phrase,
  .kwtable .table td.kw-pos,
  .kwtable .table td.kw-url { white-space: normal; }
}

/* UNDER 640 THE TABLE STACKS (site.css `table.table.stack` and region S3.6,
   which drops the min-width, the nowrap and the edge fade). What this
   block adds: the position cell puts its label over its two lines rather
   than beside them; an empty cell is not drawn, so a first-week row shows
   no labels with nothing after them; the doors sit side by side under the
   row with 12px between; the strip keeps 24px in a 132px box. */
@media (max-width: 639px) {
  .kwtable .table.stack td { white-space: normal; }
  .kwtable .table.stack td:empty { display: none; }
  .kwtable .table.stack td.kw-pos { flex-direction: column; align-items: flex-start; gap: 2px; }
  .kwtable .table.stack td.kw-doors { justify-content: flex-start; }
  .kwtable .table.stack td.kw-doors .kw-doors-inner { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--s1) var(--s3); }
  .kwtable .table.stack td.kw-doors::before { content: none; }
  .kwtable .table.stack td.kw-strip .weeks.weeks-sm { width: 132px; }
  .kwtable .table.stack td.kw-aio { padding-top: var(--s1); }
  .kwtable .table.stack td.kw-url { align-items: flex-start; }
  /* The value column of a stacked row is flush right, so the host and the
     path under it are too. */
  .kwtable .table.stack td.kw-url a { text-align: right; }

  /* The add row: the button under the field at full width. */
  .kw-add { grid-template-columns: minmax(0, 1fr); }
  .kw-add > .btn { width: 100%; }

  /* The retired rows: the date and the door under the phrase. */
  .kw-retired li { grid-template-columns: minmax(0, 1fr) auto; }
  .kw-retired li .rp { grid-column: 1 / -1; }
}
