/* ============================================================================
   Mission Control — the web shell.

   PROVENANCE. The shape rules below are copied from the web shell's single
   <style> block (rail, main, .btn, .panel, .tbl-wrap, .data-table, the field
   rule, .toast, .err, .loadline, the blocked card, .muted-note, .att-row,
   .lnk, .empty). Rules for screens this app does not have — tiles, segmented
   controls, show wall, calendar, to-dos, marketing, inbox/mail, the modal,
   the show page, the phone bottom nav and the phone table engine — were left
   behind rather than carried dead. Colours came out of those rules as they
   were copied: every one now reads a token.

   THE SKIN RULE, and it is the whole rule: :root carries the ORIGINAL palette
   and body[data-skin="neon"] overrides THE TOKENS AND NOTHING ELSE. No rule
   below is keyed to a skin. That is why a skin switch is one attribute write
   in applySkin() with no repaint, and why a third skin would be sixteen lines.
   ========================================================================= */

/* ══ TOKENS BEGIN ══ The two blocks below are the only place in this file a
   colour is spelled. test/unit/style-gate.test.js reads everything after the
   END marker and names any literal colour it finds there that is not one of
   its listed exceptions (paper white, and the black of a shadow or a scrim). */
:root {
  /* ORIGINAL — the desktop app's default palette. */
  --bg: #0b1020;
  --panel: #151b30;
  --panel-2: #1c2440;
  --text: #e8ecf8;
  --muted: #8a93b2;
  --accent: #4ade80;
  --accent-2: #38bdf8;
  --warn: #fbbf24;
  --bad: #f87171;
  --border: #28304f;
  /* Derived members of the same palette. They are tokens, not constants, for
     the same reason the ten above are: the neon block re-declares them. */
  /* --muted-2 is the dimmest text token, so it is the one that has to be
     MEASURED, not eyeballed. The value that came in with the copied rules,
     #5b6489, reads 2.95:1 on --panel — under the 4.5:1 floor — and it was
     carrying .muted-note at 10px, the smallest text on the page. Lifted here
     to 4.65:1; the neon block below reaches 4.78:1 on its own panel, and the
     note itself moved to 11px in the .muted-note rule. */
  --muted-2: #7b84ab;
  --line-soft: rgba(40, 48, 79, .55);
  --rail-1: #0d1226;
  --rail-2: #080c1a;
  --on-accent: #06210f;   /* text printed ON an accent fill */
  /* The scrollbar thumb (2026-10-02, review I1 of lane im-fix). MEASURED, like
     --muted-2: at least 3:1 — the non-text floor — on --bg, --panel and
     --panel-2 alike (3.33 on Raised Navy, the closest), because every scroller
     inherits it and fields and the print boxes are Raised Navy. --panel-2 was
     1.00:1 on those, and the Neon skin lost the rail's bar altogether. */
  --sb-thumb: #6a73a2;
  /* The Event Calendar's private-event hue (Private Events, 2026-09-22): a
     different KIND of booking rather than a firmer or looser one, so a new hue
     and not a reuse of the legend's three — the desktop picked magenta for the
     same reason (flight-director.css:158, 702-703). It was declared in that
     screen's block until the style wave moved it here (2026-09-23), where the
     Token Rule says every colour lives. */
  --pe: #d946ef;
  /* The MARKET's own violet (Chris, 2026-10-03, by dialog: "we also need to
     make a privat event a different color than market" → "Violet"). Until then
     a market wore --pe with a double edge, and on a phone — where a chip is a
     7px dot with no edge at all — a market and a private event were the same
     dot. MEASURED: 5.04:1 on --panel, 5.59 on --bg, 4.51 on --panel-2 (the
     4.5:1 text floor everywhere, so it clears the 3:1 non-text floor with
     room); hue 254° against --pe's 292°, and CIE76 ΔE 34 from --pe, 67 from
     --accent-2 (the HELD blue), 58 from --muted-2 (holds), 146 from --accent,
     149 from --warn. */
  --market: #9474ff;
  /* A CO-PRO show's orange (Chris, 2026-10-29: "We also need a color on the
     calendar for a co-pro show" → "Orange", by dialog). The one hue left that
     is clear of every other kind on the grid. MEASURED: 6.08:1 on --panel,
     6.75 on --bg, 5.45 on --panel-2; CIE76 ΔE 43 from --warn (rentals) and
     42 from --bad, 99+ from --muted-2, --accent, --accent-2, --pe, --market.
     The same value on Neon: 6.63:1 on its --panel, ΔE 43 / 80 from its
     --warn / --bad. */
  --copro: #f97316;

  /* ---- THE CONTROL RECIPE -------------------------------------------------
     ONE set of metrics for every button, select and text field on the page.
     The defect it exists to prevent: buttons declaring a size while FIELDS DO
     NOT, so a select falls back to the browser's 16px next to a 13px button
     and no two controls in a row line up. Nobody chooses that; it is a gap.
     TOKENS, NOT CONSTANTS. Both halves — .btn and the field rule — read
     --ctl-fs, so they move together. Change control sizing HERE, never on an
     individual control. The phone block below raises --ctl-fs to 16px, which
     is how the iOS focus-zoom floor is met without pinning any one field. */
  --ctl-fs: 13px;        /* text size: buttons AND fields */
  --ctl-pad: 7px 12px;   /* normal density */
  --ctl-pad-c: 4px 8px;  /* compact: inside tables and dense rows */
  --ctl-r: 8px;

  /* ---- THE NOTE -----------------------------------------------------------
     ONE size for a note: the 11px Dim Slate line under a control, a row or a
     panel title (`.muted-note`). It is a token because the style review of
     2026-09-23 found the same note restated at 11.5px on five screens (the
     calendar's avails line, a pipeline card's second line, the Client Book's
     guard, Shortage's notes, the Bar Station's sheet foot and request box), each
     a copy of the house note a half-pixel off. They read this now. */
  --note-fs: 11px;

  /* The TV's own ground (menu build, Lane S2): the swatch the TV brand's
     pictures are shown on, on Menu screens — the Void of web/menu-tv/tv.css,
     because the brand is a see-through PNG and is judged on the colour it will
     sit on. The same in both skins: it is the TV's colour, not the app's. */
  --tv-swatch: #04010A;
}

body[data-skin="neon"] {
  /* NEON CONSOLE — tokens only. Declared on BODY, not :root: everything that
     reads them lives inside body, and body-level declarations resolve on the
     same element cascade, so a skin swap needs no !important anywhere. */
  --bg: #05060a;
  --panel: #0a1322;
  --panel-2: #0c1220;
  --text: #e8f6ff;
  --muted: #6f87a8;
  --accent: #2bd97b;
  --accent-2: #00e5ff;
  --warn: #fbbf24;
  --bad: #ff2d95;
  --border: #17324d;
  --muted-2: #6a83a4;   /* 4.78:1 on this skin's --panel; the source's #4e6485 was 3.09:1 */
  --line-soft: rgba(23, 50, 77, .55);
  --rail-1: #070b14;
  --rail-2: #030407;
  --on-accent: #04140a;
  --sb-thumb: #4f6a8c;   /* 3.34:1 on this skin's --panel (the closest), 3.64 on --bg */
  --pe: #ff5ae5;
  --market: #a085ff;   /* 6.46:1 on this skin's --panel, 7.03 on --bg; ΔE 44 from its --pe, 83 from its --accent-2 */
  --copro: #f97316;    /* 6.63:1 on this skin's --panel, 7.23 on --bg; ΔE 43 from its --warn, 80 from its --bad */
}
/* ══ TOKENS END ══ */

* { box-sizing: border-box }
html, body { height: 100% }
/* EVERY SCROLLBAR WEARS THE PAGE (Chris, 2026-10-01, of the rail: "scroll bar
   on MC is gray and does not match MC"). The rail's bar was the browser's own
   grey with arrows, because `.rail-nav` had no scrollbar rule at all; and
   `.main`'s `scrollbar-width: thin` with no colour made Chrome (121+) drop its
   ::-webkit-scrollbar pair and fall back to grey too. So the house recipe — the
   one `.today-list` set first (2026-09-08): a thumb on nothing — is set
   here, in its own measured token `--sb-thumb` (2026-10-02; it was Raised
   Navy, 1.00:1 on every field and invisible in Neon), on the root and again on body, and every scroller INHERITS it
   (`scrollbar-color` is an inherited property). Twice because the Neon skin's
   tokens live on body: inherited from :root alone, the colour would be resolved
   before the skin is. `color-scheme: dark` is what keeps a native control or a
   scroller no rule reaches from painting light (audit M10). Pinned by
   test/unit/impeccable-style.test.js. */
:root, body { color-scheme: dark; scrollbar-color: var(--sb-thumb) transparent }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font-family: 'Segoe UI', system-ui, sans-serif; -webkit-font-smoothing: antialiased;
  /* The caret and the selection are the two browser surfaces a person sees
     while typing; on their defaults they belong to no palette. Cyan is the
     app's "active" colour everywhere else (focus ring, active tab, rail bar). */
  caret-color: var(--accent-2);
}
::selection { background: color-mix(in srgb, var(--accent-2) 35%, transparent); color: var(--text) }
/* The three top-level views are switched with the `hidden` ATTRIBUTE, so the
   attribute has to beat the display rules that follow it. */
[hidden] { display: none !important }

/* ── app shell: rail + main ───────────────────────────────────────────────── */
#app { display: flex; height: 100vh; height: 100dvh; width: 100% }
/* The brand is a screen title, not a caption: Chris, 2026-09-06, "atleast as big
   as the word Today" — so 23px, the size h1 already is. The tracking comes down
   with it: .24em was chosen for 11px letters, and that much air at this size
   pushed LOAD IN past the 208px rail. */
.wordmark { font-size: 23px; letter-spacing: .2em; text-transform: uppercase; font-weight: 700; color: var(--text) }
.wordmark span { color: var(--accent-2) }

.rail {
  flex: none; width: 208px; display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--rail-1), var(--rail-2)); border-right: 1px solid var(--border);
}
/* 16px of head rather than 18px: the taller wordmark brings its own leading with
   it, so the block keeps roughly the height it had. */
.rail-brand { padding: 16px 16px 14px; border-bottom: 1px solid var(--line-soft) }
.rail-venue { font-size: 12px; font-weight: 600; color: var(--muted); margin-top: 6px; word-break: break-word }
.rail-nav { flex: 1; padding: 10px 8px; display: flex; flex-direction: column; gap: 2px; overflow-y: auto; scrollbar-width: thin }
.rail-nav button {
  display: flex; align-items: center; gap: 10px; width: 100%; border: 0; background: none; color: var(--muted);
  font: inherit; font-size: 12.5px; font-weight: 600; padding: 9px 10px; border-radius: 9px; cursor: pointer; text-align: left;
}
.rail-nav button:hover { color: var(--text); background: var(--panel-2) }
.rail-nav button.active { color: var(--text); background: var(--panel-2); box-shadow: inset 2px 0 0 var(--accent-2) }
/* THE RAIL'S THREE SECTIONS (Chris, 2026-09-23): Concerts, Events, Admin, under
   the two unnamed tabs on top. A section's name is the panel title's eyebrow
   (10px, 700, .16em, uppercase, Slate Lavender) with a Soft Rule above it — the
   rail's inner separator — and it is not a control: no hover, no pointer, never
   a Tab stop (web/app.js draws it as a `div`). A header that opens the rail has
   no rule over it: there is nothing above it to divide it from. */
.rail-sec {
  margin: 8px 2px 0; padding: 10px 8px 4px; border-top: 1px solid var(--line-soft);
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700;
  cursor: default; user-select: none;
}
.rail-sec:first-child { margin-top: 0; padding-top: 2px; border-top: 0 }
/* A STRIP THAT SCROLLS SIDEWAYS SAYS SO (walk 2026-09-09, W-20). At 375 the rail
   is a row and prints `Today · Show Calendar · Payments · Missin` with three
   screens past the edge; the show page's tabs end mid-word the same way. Both
   scroll and neither said it could. `has-more` is set by LoadIn.watchOverflow
   whenever there is content to the right, so the fade is never painted over a
   strip that already fits.

   A STICKY flex item, not an absolute one: the scroller's own children are laid
   out in a row and an absolutely positioned pseudo-element would scroll away
   with them. The negative margin is what keeps it from taking a column's width
   of its own. Each fades to the token behind IT — the rail's foot, the page's
   ground — because there is no one background under both. */
.rail-nav.has-more::after, .show-tabs.has-more::after, .settabs.has-more::after {
  content: ''; position: sticky; right: 0; flex: 0 0 26px; align-self: stretch;
  margin-left: -26px; pointer-events: none;
}
.rail-nav.has-more::after { background: linear-gradient(to right, transparent, var(--rail-2)) }
.show-tabs.has-more::after, .settabs.has-more::after { background: linear-gradient(to right, transparent, var(--bg)) }
/* The links strip is EMPTY until an owner saves a link, and an empty strip is
   not a strip — :empty is what keeps it from printing a stray border/gap. */
.rail-links { flex: none; display: flex; flex-direction: column; gap: 4px; padding: 8px; border-top: 1px solid var(--line-soft) }
.rail-links:empty { display: none }
.rail-link {
  display: block; color: var(--accent-2); text-decoration: none; font-size: 12px; font-weight: 600;
  padding: 7px 10px; border-radius: 9px; border: 1px solid var(--border); background: var(--panel-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-link:hover { border-color: var(--accent-2) }
.rail-foot { flex: none; display: flex; align-items: center; gap: 9px; padding: 12px 14px; border-top: 1px solid var(--line-soft) }
.rf-av {
  flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--panel-2); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; color: var(--accent-2);
}
.rf-txt { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; flex: 1 }
.rf-txt b { font-size: 12px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
/* 10px is the floor for every label in this file (2026-09-06; the 2026-09-03
   critique found 8.5–9.5px in seven places). Labels sit beside larger text and
   never carry a fact alone, so they stay small — but not smaller than this. */
/* One line, like the name above it (im-fix review M7): the role line prints the
   role's NAME since 2026-10-01, and "SHOW MANAGEMENT" or a long custom role
   in capitals wrapped the identity block to two lines. */
.rf-txt span { font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
/* A co-pro partner's line is their COMPANY ("Colt Classic · partner", web/app.js),
   which is longer than a role name and is the one thing on it that matters, so
   it wraps rather than losing the company to an ellipsis. */
.rf-txt span.rf-partner { white-space: normal; letter-spacing: .06em; overflow-wrap: anywhere }
.rail-foot .lnk { flex: none; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; font-weight: 700; color: var(--muted) }
/* THE OWNER'S VIEW AS (2026-09-24). The control takes a row of its own under
   the name, so the foot wraps; it is the rail's label voice beside a house
   select. While viewing, the role line is the house pill (a `small`, so the
   `.rf-txt span` label rule above does not reach it), sat at its own width
   rather than stretched across the column. */
.rail-foot { flex-wrap: wrap }
.rf-txt small { align-self: flex-start; margin-top: 2px }
.rf-viewas { flex: 1 1 100%; display: flex; align-items: center; gap: 8px }
.rf-viewas span { flex: none; font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted) }
.rf-viewas select { flex: 1 1 0; width: 0; min-width: 0 }
/* The banner across the top of the column while the owner views the site as a
   role: the house banner (the Bar Checklist's note — panel, hairline, a 3px left
   rule) with a Rental Amber rule, because it is a mode that changes what the
   site does rather than a standing note; the role is the house pill. */
.viewas-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 12px; color: var(--text); background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--warn); border-radius: var(--ctl-r); padding: 8px 12px; margin-bottom: 12px }

/* THE ONE SCROLLER, AND THE POSITIONING PARENT (2026-09-29). `.main` scrolls
   every screen, and it is `position: relative` so that anything placed
   absolutely inside a screen — the clipped `.imp-file` input, `.sr-only` — is
   placed in, scrolled with and clipped by this column. Static, the page itself
   was their containing block: the Show page's hidden Files input sat far down
   the document and the WINDOW scrolled beside the column (Chris, 2026-09-29:
   "double scroll"; Settings › Venue and a market's Event page did the same).
   Pinned by test/e2e/main-scroller.spec.js. */
.main { position: relative; flex: 1; min-width: 0; overflow-y: auto; padding: 22px 26px 30px; scrollbar-width: thin }
.main::-webkit-scrollbar { width: 10px }
.main::-webkit-scrollbar-thumb { background: var(--sb-thumb); border-radius: 5px }
.screen { display: none } .screen.active { display: block }

h1 { font-size: 23px; margin: 2px 0 1px; font-weight: 650 }
/* THE SECONDARY HEADING (style wave, 2026-09-23; review M9): a record's own name
   under the page's name — the event on the Event page, tonight's show on Day of
   Show — set as the Headline (23px, 650; 19px on a phone, with h1). They were
   four private sizes, 18, 18 at 800, 19 and 20px, none of them on the type ramp.
   A screen that keeps a smaller h2 of its own says so in a scoped rule. */
h2 { font-size: 23px; margin: 0; font-weight: 650; line-height: 1.2 }
/* The page's name (Chris, 2026-09-07: "centered and all capitalized on each
   page with the same color look as LOAD IN"): the wordmark's own recipe — 700,
   0.2em, capitals — centred, with the last word in Signal Cyan the way IN is.
   The line under it centres with it. Keyed on the CLASS and not on h1: the
   show page's heading is the show's name and keeps the Headline rule above. */
.page-name { text-align: center; text-transform: uppercase; letter-spacing: .2em; font-weight: 700; margin: 0 0 3px }
.page-name span { color: var(--accent-2) }
/* A screen's h1 takes the focus when the screen is opened (web/app.js route(),
   audit A-M3) so a keyboard reader starts in the page they opened; it is a
   place, not a control, so it wears no ring. */
h1[tabindex="-1"]:focus { outline: none }
/* THE SUB-LINE: every screen is `LoadIn.pageName()` and then ONE `.sub` straight
   under it (style wave, 2026-09-23; style review X11). It centres with the name,
   and it is set in CAPITALS even when it is a sentence — the owner's ruling of
   2026-09-23, recorded in DESIGN.md as the one named exception to the Sentence
   Rule: the subtitle keeps its capitals; a panel title and a tag do not. The
   Event Calendar wrote a private recipe instead (`.pe-sub`, 12.5px sentence
   case); it writes `.sub` since its lane of the wave. */
.page-name + .sub { text-align: center }
.sub { font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-bottom: 12px }
p { font-size: 13px; line-height: 1.5; margin: 0 0 8px }

/* ── panels ───────────────────────────────────────────────────────────────── */
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 12px; margin-bottom: 12px }
.panel-title {
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700;
  margin-bottom: 9px; display: flex; align-items: center; gap: 8px;
}
.panel.danger { border-color: var(--bad) }
.muted-note { font-size: var(--note-fs); color: var(--muted-2); margin-top: 9px; letter-spacing: .04em }
/* setup.js emits the hint span whether or not there is a hint. An empty note is
   not a note — without this it still claims a line inside a .tog flex row. */
.muted-note:empty { display: none }
.empty { font-size: 12px; color: var(--muted); padding: 6px 0 }

/* ── controls: buttons and fields read the SAME recipe ────────────────────── */
.btn {
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text); border-radius: var(--ctl-r);
  padding: var(--ctl-pad); font: inherit; font-size: var(--ctl-fs); font-weight: 600; cursor: pointer; white-space: nowrap;
}
.btn:hover { border-color: var(--muted-2) }
.btn:disabled { opacity: .5; cursor: default }
/* Pressed. Chris's word for the controls is "tactile" (2026-09-06): a click
   should answer under the finger before the server does. One pixel down, and
   the green primary dims a shade; nothing animates, so reduced motion needs no
   exception. Every control built on the recipe shares it. */
.btn:active:not(:disabled), .show-tabs button:active, .settabs button:active, .sm-basis button:active,
.rail-nav button:active, .rail-link:active { transform: translateY(1px) }
.btn.primary:active:not(:disabled) { filter: brightness(.92) }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent) }
.btn.danger { border-color: var(--bad); color: var(--bad) }
/* SAVED — a receipt, not a second primary (Chris, 2026-09-24: "the buttons for
   each that we click to save turn green when it has been saved", then "keep it
   green after saved"). A Save that landed takes the primary's green and reads
   `✓ Saved · 4:12 PM`; it is disabled, because there is nothing left to save,
   but at full strength — a receipt at half opacity is a receipt nobody reads.
   The menu screens put it on and take it off (web/menu-common.js markSaved);
   the show page's own green Save went with Classic in Run Sheet slice 8, its
   save line being the receipt. DESIGN.md records it as the One Green Rule's
   transient exception. */
.btn.saved, .btn.saved:disabled { background: var(--accent); border-color: var(--accent); color: var(--on-accent); opacity: 1 }
.btn.saved[aria-disabled="true"] { cursor: default }
/* The show page's "saved Sep 24, 4:12 PM · Chris Fall" in a panel's title: the
   title's own tag, pushed to the right end, with the name kept as it is written
   rather than set in the tag's capitals. */
.panel-title .tag.saved-stamp { margin-left: auto; text-transform: none; letter-spacing: .02em }
/* Out of sight, still read aloud: the save line while its button carries the
   same words (the clipped-box recipe `.imp-file` uses). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The drawn icon (LoadIn.icon in web/app.js): 14px, the text's own colour,
   a word's space after it. Alone in a button (the × that closes a dialog) it
   carries no margin. `.after` is the one mark that follows its word. */
.ico { width: 14px; height: 14px; vertical-align: -2px; margin-right: 6px; flex: none }
.ico.after { margin: 0 0 0 4px }
/* Icon-only buttons are the ones that carry an aria-label instead of words
   (close, remove, the month arrows) — `:only-child` would also match "+ Row",
   whose word is a text node and not an element. */
.btn[aria-label] > .ico, .chip button[aria-label] > .ico { margin: 0; vertical-align: -3px }
/* `.ib` — an icon button small enough to live inside a list row (Today's Open
   and Clear, 2026-09-08). It is not a small `.btn`: `.btn` is built on the
   shared control metrics (--ctl-pad, --ctl-fs) that make every button, select
   and field on the page the same height, and this one has to be 22px square so
   two of them fit a 51px row without changing its height. The colours are
   `.btn`'s own, so it still belongs to the page. */
.ib { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; flex: none; padding: 0; background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; color: var(--muted); cursor: pointer }
.ib:hover { color: var(--accent-2); border-color: var(--muted-2) }
.ib > .ico { width: 13px; height: 13px; margin: 0; vertical-align: 0 }
/* Armed — the first of the two clicks this button asks for. It turns the colour
   of the question printed beside it, and since 2026-09-09 that colour is AMBER:
   the approved Missing concept called the Clear/Open pair "a two-click arm in
   amber", and amber is what this file gives a destructive act that went through.
   A clear is not destructive — the item stays on the Missing screen — and red is
   the refusal colour, which is a different thing to say (rulings review
   2026-09-09, finding 2). */
.ib[data-armed="1"] { color: var(--warn); border-color: var(--warn) }
/* A screen refilling itself: the last answer stays readable but steps back
   until the new one lands (Today, the Payments board). */
.stale { opacity: .45; transition: opacity .15s }
@media (prefers-reduced-motion: reduce) { .stale { transition: none } }
.lnk { background: none; border: 0; padding: 0; margin: 0; color: inherit; font: inherit; cursor: pointer; text-align: left }
.sm-pay-restore .lnk { text-decoration: underline; color: var(--text) }
.lnk:hover { color: var(--accent-2) }
.lnk:disabled { opacity: .5; cursor: default }

/* A field is not a button: same metrics, no 600 weight, no pointer cursor.
   Untyped inputs are in the list on purpose — the pages write <input> with no
   type for most text fields, and without `input:not([type])` those are the
   ones that fall back to the browser default beside a 13px button. */
select, textarea,
input[type="text"], input[type="number"], input[type="email"], input[type="password"], input[type="date"], input[type="search"], input:not([type]) {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--border); border-radius: var(--ctl-r);
  padding: var(--ctl-pad); font: inherit; font-size: var(--ctl-fs); color-scheme: dark; min-width: 0;
}
/* THE ONE FOCUS RING, on every control (DESIGN.md: "2px Signal Cyan outline,
   1px offset, on every control"). The icon buttons, the sortable heads, the
   layer switches, the tiles, the calendar chips and every link in the main
   column fell back to the browser's own ring until 2026-10-01 (Impeccable
   critique of Today; audit M2) — Today's row links, its tiles and its Open and
   Clear among them. A rule further down that draws a ring of its own (a
   calendar day, inside its cell) still wins on specificity. */
input:focus-visible, select:focus-visible, textarea:focus-visible, .btn:focus-visible, .lnk:focus-visible,
.rail-nav button:focus-visible, .rail-link:focus-visible,
.ib:focus-visible, .th-sort:focus-visible, .lay:focus-visible, .tile:focus-visible, .cal-ev:focus-visible,
.main a:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 1px }
input[readonly] { color: var(--muted); cursor: default }
/* A PLACEHOLDER IS THE APP'S, NOT THE BROWSER'S (style wave, 2026-09-23; audit
   T-M2): 86 placeholders were the browser's own grey, which belongs to no
   palette and dims differently on each engine. Slate Lavender rather than Dim
   Slate, because a placeholder sits on a field — Raised Navy — and Dim Slate
   measures 4.17:1 there against the 4.5:1 floor (Slate Lavender, 5.01:1; the
   Measured Muted Rule in DESIGN.md). `opacity: 1` because Firefox fades a
   placeholder on its own and the measurement would stop being true. */
::placeholder { color: var(--muted); opacity: 1 }
input:disabled, select:disabled, textarea:disabled { opacity: .55; cursor: default }
/* THE BOX IS DRAWN HERE, not by the operating system (walk 2026-09-09, W-26).
   `accent-color` only ever reached the CHECKED state: an unchecked native box is
   the UA's white fill with a grey border — the brightest element on the Neon
   skin's #05060a ground, and there are eleven of them in the roles grid alone.
   So the control is the app's own recipe at 16px: the raised surface every field
   sits on, the hairline border every control carries, and Signal Cyan when it is
   ticked, which is the accent it always wore.

   The tick is DRAWN, not an image: a data-URI SVG has to carry a literal colour,
   and no colour on this page is allowed to be literal. Two borders of a box
   rotated 45° make the mark, in the one colour that is ever printed on an accent
   fill. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  /* min-* as well as width/height: three rules in this file set `width: auto` on
     the inputs inside a `.tog` or `.chk` label, and a native box survived that on
     its intrinsic size. A drawn one has none, so the floor is stated. */
  width: 16px; height: 16px; min-width: 16px; min-height: 16px; box-sizing: border-box;
  /* padding: 0 as well as the size: a box-sizing box cannot go below its own
     padding plus border, and something upstream leaves 8px on an input. */
  margin: 0; padding: 0; flex: none; position: relative; cursor: pointer;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 4px;
}
input[type="checkbox"]:hover:not(:disabled) { border-color: var(--muted-2) }
input[type="checkbox"]:checked { background: var(--accent-2); border-color: var(--accent-2) }
input[type="checkbox"]:checked::after {
  content: ''; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
  border: solid var(--on-accent); border-width: 0 2px 2px 0; transform: rotate(45deg);
}
input[type="checkbox"]:disabled { cursor: default }
/* THE SELECT'S OWN CHEVRON (walk 2026-09-09, W-28). `appearance: none` takes the
   OS dropdown arrow off, and the mark that replaces it is two linear-gradients —
   again because a data-URI SVG would have to carry a literal colour and the
   search box next to it already draws its own. `background-position` is measured
   from the right edge, so the pair reads as one 8px chevron whatever the field's
   width; the extra right padding is the room it stands in. */
select {
  appearance: none; -webkit-appearance: none;
  padding-right: 26px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: right 14px center, right 10px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select:disabled { background-image: none }
/* The native calendar button inside a date field is drawn by the browser and
   takes no colour from anywhere: it is the ONE non-token colour treatment in
   this stylesheet, recorded in DESIGN.md. `color-scheme: dark` on the field
   above already darkens the popup; this is the glyph on the button. */
input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.6); cursor: pointer }
input[type="date"]:hover::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.9) }

/* Setup and Settings write `<label>Text <input></label>`, so the label IS the
   field's row. Stacked in the main column; inline for a checkbox. */
.main label { display: flex; flex-direction: column; gap: 4px; font-size: 11px; letter-spacing: .04em; color: var(--muted); margin-bottom: 9px }
.main label:last-child { margin-bottom: 0 }
.main label input, .main label select, .main label textarea { width: 100%; max-width: 420px; color: var(--text) }
.main label.tog { flex-direction: row; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text); flex-wrap: wrap }
.main label.tog input { width: auto }
.main label.tog .muted-note { margin-top: 0; flex-basis: 100%; }
.row3 { display: grid; grid-template-columns: 120px 1fr 2fr; gap: 8px; align-items: center; margin-bottom: 8px }
.row3 b { font-size: 12px; font-weight: 600 }

/* The venue logo row in setup. The box keeps its height whether it holds the
   image or the empty line, so the panel does not jump when one becomes the
   other; the dashed border says "a thing goes here" without a second sentence.
   48px is the height the logo prints at in the documents (server/docs/kit.js
   .logo), so what is seen here is the size it comes out. */
.su-logo-box {
  /* The same 420px every field on this form stops at: a dashed box the width
     of the window beside 420px fields read as a different kind of thing. */
  max-width: 420px; min-height: 56px; display: flex; align-items: center; padding: 8px;
  border: 1px dashed var(--border); border-radius: 4px; background: var(--panel-2);
}
.su-logo-box img { max-height: 48px; max-width: 240px }

/* ── rows and tables ──────────────────────────────────────────────────────── */
.att-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line-soft); font-size: 12px; flex-wrap: wrap }
.att-row:last-child { border-bottom: 0 }
.att-row .grow { flex: 1; min-width: 0 }
.att-row .err { margin: 0 }
/* The show page's Files panel as a DROP ZONE (Chris, 2026-10-02), for a role
   with act.edit only. Dragging files over it draws the calendar's drop
   outline (`.cal-day.drop`: Signal Cyan, a faint cyan wash) dashed, and lays
   the one line "Drop files to add them to this show" over the panel. The
   line takes no pointer, so the drop lands on the panel under it. Then one
   line per file in `.files-log`: muted while it uploads, green when added, the
   house `.err` when refused. */
.files-drop { position: relative }
.files-drop .att-row { border-bottom: 0 }
.files-drop.over { outline: 2px dashed var(--accent-2); outline-offset: -2px; background: color-mix(in srgb, var(--accent-2) 8%, var(--panel)) }
.files-drop-hint { display: none; position: absolute; inset: 0; align-items: center; justify-content: center; gap: 8px; padding: 0 16px; text-align: center;
  border-radius: inherit; background: color-mix(in srgb, var(--panel) 85%, transparent); color: var(--text); font-size: 13px; font-weight: 600; pointer-events: none }
.files-drop.over .files-drop-hint { display: flex }
.files-log { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted) }
.files-log:empty { display: none }
.files-log li { overflow-wrap: anywhere }
.files-log li.ok { color: var(--accent) }
.files-log li.err { margin: 0 }
/* A touch screen has nothing to drag from, so it is not told to drop: the
   button is its path. */
@media (hover: none) { #doc-drop-note { display: none } }
/* A checkbox and its own words on one line. The `.main label` rules above stack
   a field label over a full-width input, which would put the box on its own
   line and stretch it across the row — so, like `.tog`, this has to be written
   as `.main label.chk` to outrank them. */
.chk { display: inline-flex; gap: 6px; align-items: center }
.main label.chk { flex-direction: row; align-items: center; gap: 6px; margin: 0; font-size: 12px; color: var(--text) }
.main label.chk input { width: auto }
.tbl-wrap { overflow-x: auto }
.data-table { width: 100%; border-collapse: collapse; font-size: 13px }
.data-table th {
  text-align: left; color: var(--muted); font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--border);
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
}
.data-table td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: middle }
.data-table tbody tr:hover { background: var(--panel-2) }
/* ON RAISED NAVY, NOTHING IS DIMMER THAN SLATE LAVENDER (style wave,
   2026-09-23; audit A-M1). Dim Slate is measured for Desk Navy — 4.65:1 — and
   on Raised Navy it measures 4.17:1, under the 4.5:1 floor, on the original
   skin (the neon skin's pair is 4.80:1). So the house note steps up to Slate
   Lavender (5.01:1 there) wherever the surface under it is Raised Navy: a
   hovered row, and the raised bands a note sits in — the import block, a deal's
   detail band, the task editor rows, the To-Do editor, an opened Bar Checklist
   night, and the reader rows on Mail and the show page's Correspondence. The
   deal band's group labels (`.sm-grp-lbl`) take the same step at their own
   rule. DESIGN.md's Measured Muted Rule says it, and test/unit/style-gate.test.js
   re-measures both pairs from the tokens. */
.data-table tbody tr:hover .muted-note, .imp-block .muted-note, .sm-tbl tr.sm-detail .muted-note,
.st-edit .muted-note, .st-add .muted-note, #scr-todos .td-edit .muted-note, .bc-detail .muted-note,
#scr-mail tr.ml-sub .muted-note, #scr-show #corr-panel tr.corr-sub .muted-note { color: var(--muted) }
/* A whole cell can be secondary, and so can a run of text inside one — the Sent
   log's show date under the show's name, and the line it prints in place of a
   show that has since been deleted. One colour for both. */
.data-table td.m, .data-table .m { color: var(--muted) }
/* The Owner column of the privilege grid. Not a disabled checkbox: a tick nobody
   may untick invites the click that does nothing. The word says why instead. */
.data-table td.always { color: var(--muted); font-size: 11px; letter-spacing: .04em }
.data-table a { color: var(--accent-2); text-decoration: none }
/* Compact density inside a table row — the second half of the control recipe.
   A checkbox is exempt: it is a drawn 16px box since 2026-09-09 (W-26) and it
   has no text to give room to, so padding here would only inflate the box past
   the size the whole app gives it. */
.data-table .btn, .data-table input:not([type="checkbox"]), .data-table select { padding: var(--ctl-pad-c) }
/* …but a select still has to keep the room its chevron stands in. The shorthand
   above is the WHOLE of `padding`, so it overwrote the 26px the select rule
   sets and the option text ran under the mark (review 2026-09-09, M-4: the
   Artists panel's Paid by select holds partner names, and Payments' Source is
   one character short of the same). 22px is the chevron's own room — it is
   drawn from `right 10px` to `right 19px` — plus the 3px that keeps a letter
   clear of it. The pickers' selects take the same compact shorthand and get the
   same restatement in their OWN block: a shorthand later in the file would undo
   one written here. */
.data-table select { padding-right: 22px }

/* ── the two gates: sign-in and not-a-member ──────────────────────────────── */
.gate { display: flex; align-items: center; justify-content: center; min-height: 100vh; min-height: 100dvh; padding: 20px }
.gate-panel {
  width: 100%; max-width: 360px; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  padding: 24px 22px; text-align: center;
}
/* A gate is a panel with nothing else in it, so the brand goes one step above the
   rail's — the same relation it kept at 11/15. */
.gate .wordmark { font-size: 28px; letter-spacing: .24em }
.gate-sub { font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700; margin: 8px 0 18px }
.gate .btn { width: 100% }
.gate-or {
  display: flex; align-items: center; gap: 10px; margin: 16px 0;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted-2); font-weight: 700;
}
.gate-or::before, .gate-or::after { content: ""; flex: 1; height: 1px; background: var(--line-soft) }
.gate form { display: flex; flex-direction: column; gap: 10px; text-align: left }
.gate label { display: flex; flex-direction: column; gap: 4px; font-size: 11px; letter-spacing: .04em; color: var(--muted) }
.gate label input { width: 100%; color: var(--text) }
.gate .lnk { align-self: center; font-size: 11px; color: var(--muted) }
#blocked p { font-size: 13px; line-height: 1.5; color: var(--muted); margin: 8px 0 18px }

/* ── toast · error line · loading line ────────────────────────────────────── */
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(16px);
  background: var(--panel-2); border: 1px solid var(--accent); color: var(--text);
  font-size: 11.5px; font-weight: 600; padding: 8px 14px; border-radius: 20px; opacity: 0; pointer-events: none;
  transition: opacity .22s, transform .22s; box-shadow: 0 8px 24px rgba(0, 0, 0, .5); z-index: 5;
  /* A server sentence can be long and a phone is narrow: the capsule wraps
     inside the viewport rather than running off it. */
  max-width: calc(100vw - 32px); white-space: normal; text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0) }
/* Three kinds, told apart by the border (2026-09-06, from the 2026-09-03
   critique's P0: success, refusal and destruction used to arrive as one green
   capsule). Green is a save. Red is a refusal — the server said no. Amber is a
   destructive act that went through: a reset, a removal, a cancellation. The
   kind is set by toast(text, kind) in web/app.js, which also holds the two
   dwell times: a refusal stays on screen longer than a save. */
.toast.bad { border-color: var(--bad) }
.toast.warn { border-color: var(--warn) }
@media (prefers-reduced-motion: reduce) { .toast { transition: none } }
/* Keyed to :empty, NOT to a .show class: every writer of an error line sets
   .textContent and nothing else (that is also what keeps a server string out
   of innerHTML), so emptiness is the only signal there is. */
.err {
  display: block; color: var(--bad); font-size: 11.5px; margin: 8px 0 10px; padding: 8px 10px; border-radius: var(--ctl-r);
  border: 1px solid var(--bad); background: var(--panel-2); word-break: break-word; white-space: normal;
}
.err:empty { display: none }
/* The same slot carrying good news — "Check your email for a link" is not an
   error, and it used to sit in the red box because the gate has one line. The
   writer toggles the class with the text. */
.err.ok { color: var(--accent); border-color: var(--accent) }
.loadline { position: fixed; top: 0; left: 0; right: 0; height: 2px; background: var(--accent-2); display: none; z-index: 9; opacity: .9 }
.loadline.show { display: block; animation: ld 1.1s ease-in-out infinite }
@keyframes ld {
  0% { transform: scaleX(.15); transform-origin: left }
  50% { transform: scaleX(1); transform-origin: left }
  51% { transform-origin: right }
  100% { transform: scaleX(.15); transform-origin: right }
}
@media (prefers-reduced-motion: reduce) { .loadline.show { animation: none; transform: scaleX(1) } }

/* ── Show Calendar (ported from MC Web; colours from the tokens above) ─────── */
.cal-head { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; flex-wrap: wrap }
/* Chris, 2026-09-06: the month's "font size needs to be larger" and the arrows
   sit "directly to the left and right of the month name" — so the three are ONE
   group with the <b> between the two buttons. Since 2026-09-07 the row holds
   only that group and Today: the tools moved into .cal-tools below it. */
.cal-month { display: inline-flex; align-items: center; gap: 10px }
.cal-month b { font-size: 23px; font-weight: 650; margin: 0 }
.cal-arrow { width: 34px; height: 34px; padding: 0; justify-content: center }
/* The tools box (Chris, 2026-09-07: "in a box like payments tab search
   section"): the same .panel + .att-row the Payments filter is, so the two
   screens' tool rows are one recipe. The note between Find and Send days is a
   flex child of the row, so its usual top margin would sit it low.
   The hairline goes too: #cal-range-msg follows the row inside the box, so
   `.att-row:last-child` never matches and the border would sit under the tools
   with nothing beneath it — the Payments filter box has no such line. */
.cal-tools { margin-bottom: 10px }
.cal-tools .att-row { padding: 4px 0; border-bottom: 0 }
.cal-tools .muted-note { margin-top: 0 }
/* Send days, armed: the button reads as pressed in Signal Cyan, and the picked
   nights outline in the same cyan on the grid — never Add show's green, so the
   two pick modes cannot be mistaken for each other. */
#cal-send-days[aria-pressed="true"] { border-color: var(--accent-2); box-shadow: inset 0 0 0 1px var(--accent-2); color: var(--accent-2) }
#cal-send-days.primary[aria-pressed="true"] { color: var(--on-accent); border-color: var(--accent); box-shadow: none }
.cal-day.rng { outline: 2px solid var(--accent-2); outline-offset: -2px; background: color-mix(in srgb, var(--accent-2) 10%, var(--panel)) }
/* Find a show. The box is the positioning context for its own list, which hangs
   BELOW the tools box rather than pushing the calendar down — the grid must
   not move under the reader while they type. The mark sits inside the field's
   left padding and takes no clicks, so the whole box is still one target.
   `.sug` (the send modal's suggestion list) supplies the border, the panel and
   the scroll; only the placement, the shadow and the row layout are new here. */
.cal-find { position: relative; min-width: 200px }
.cal-find input { width: 100%; padding-left: 30px }
.cal-find-ico { position: absolute; left: 10px; top: 9px; color: var(--muted); pointer-events: none }
.cal-find .sug { position: absolute; left: 0; right: 0; top: 100%; z-index: 30; margin-top: 2px; box-shadow: 0 10px 30px rgba(0,0,0,.45) }
.cal-find .sug li { display: flex; justify-content: space-between; gap: 10px }
/* `.cur` is the keyboard's row and `:hover` is the pointer's; both paint the
   same, because they mean the same thing. */
.cal-find .sug li.cur { background: var(--panel) }
/* 11px, not the browser's `smaller` — which off a 12px row is 9.6px, under the
   10px floor this stylesheet keeps (see .muted-note). */
.cal-find .sug small { color: var(--muted); font-size: 11px; flex: none }
/* The name is one flex child, so the bold match and the letters around it sit
   together (Chris, 2026-09-07: the suggestion read "Swee  t Pill"). */
.cal-find-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
/* The range's refusal sits in the tools box, under the row that holds the
   button it is about, and only exists when there is something to say (.err is
   keyed to :empty). */
/* `pre-wrap` from stage 2: the same line is where ⧉ Copy for email puts the
   avails list when the browser withholds the clipboard, and a list of nights
   with its newlines collapsed is a list nobody can paste into an email. */
#cal-range-msg { margin-top: 0; white-space: pre-wrap }
/* The layer switches, under the month. They ARE the old legend — same three
   colours, same order — so nothing on the screen says the same thing twice. */
.cal-layers { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; flex-wrap: wrap; font-size: 12px; color: var(--muted) }
.lay { --lc: var(--muted-2); display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: 12px; font-weight: 600; color: var(--text);
  border: 1px solid var(--lc); background: color-mix(in srgb, var(--lc) 14%, transparent); border-radius: 20px; padding: 4px 11px 4px 9px; cursor: pointer }
.lay::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--lc) }
.lay.l-shows { --lc: var(--accent) }
.lay.l-rentals { --lc: var(--warn) }
.lay.l-partner { --lc: var(--copro) }
/* Off is said three ways — dimmed, struck through, and the word beside the
   label — because a switch whose only state is a colour cannot be read by
   everyone looking at it. */
.lay[aria-pressed="false"] { opacity: .45; text-decoration: line-through; background: transparent }
/* The on/off word is Lamp White (Impeccable critique, 2026-10-01): in Slate
   Lavender it measured 4.2:1 on the Confirmed and Rentals tints, under the
   4.5:1 floor. Pinned with the tints recomputed in
   test/unit/impeccable-style.test.js. */
.lay i { font-style: normal; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--text) }
/* The sentence rides on the same line as the switches, so it loses the note's
   own top margin — the same thing .sm-bar and .main label.tog do to it. */
.cal-layers .muted-note { margin-top: 0 }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px }
/* A week is a row for the READER — role="row" around its seven cells — and
   nothing at all for the layout: `display: contents` leaves the cells as the
   seven-column grid's own children, so the picture is the picture it always was
   (review 2026-09-09, b-52). */
.cal-week { display: contents }
.cal-dow { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted-2); font-weight: 700; text-align: center; padding: 3px 0 }
.cal-day { min-height: 96px; background: var(--panel); border: 1px solid var(--line-soft); border-radius: var(--ctl-r); padding: 4px 5px; font-size: 10px; color: var(--muted); overflow: hidden; cursor: pointer }
.cal-day.out { opacity: .32; cursor: default }
.cal-day.today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); color: var(--accent) }
.cal-day.sel { outline: 2px solid var(--accent); outline-offset: -2px }
/* The keyboard's own mark on the grid (review 2026-09-09, b-52). The same ring,
   token and width the rest of the app gives a focused control (see the
   :focus-visible rule near the top), drawn INSIDE the cell like .sel and .rng so
   a 96px box does not grow by two pixels and push the week about. */
.cal-day:focus-visible { outline: 2px solid var(--accent-2); outline-offset: -2px }
/* One chip recipe; the layer class picks the colour: --lc is the tint token.
   The chip is an ANCHOR to its show on both grids (Chris, 2026-09-06: "when
   clicking a show it should go directly to the show page"), so the block and
   the missing underline belong to the recipe rather than to one screen. */
.cal-ev { --lc: var(--muted-2); display: block; text-decoration: none; margin-top: 3px; font-size: 12px; line-height: 1.25; font-weight: 600; color: var(--text); cursor: pointer;
  border-left: 3px solid var(--lc); background: color-mix(in srgb, var(--lc) 16%, transparent); padding: 2px 5px; border-radius: 4px;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis }
.cal-ev:hover { background: color-mix(in srgb, var(--lc) 28%, transparent) }
.cal-ev.l-shows { --lc: var(--accent) }
.cal-ev.l-rentals { --lc: var(--warn) }
.cal-ev.l-holds { --lc: var(--muted-2); border-left-style: dashed; font-style: italic }
/* A partner company's hold (2026-11-12): its own layer switch, still a hold —
   dashed and italic — in co-pro orange. */
.cal-ev.l-partner { --lc: var(--copro); border-left-style: dashed; font-style: italic }
/* A settled show or rental (Chris, 2026-10-14, ruled "Cyan + check mark"): the
   Settled status pill's cyan, after the layer lines so it wins over both. The
   layer class stays on the chip — the layer switches still hide it. */
.cal-ev.settled { --lc: var(--accent-2) }
/* A CO-PRO SHOW (Chris, 2026-10-29, ruled "Orange"): a show with an outside
   partner on its Co-promotion block, or one a partner company placed — a hold
   (still dashed, italic), a confirmed show or a rental alike. After the layer
   lines so it wins over them; never on a settled chip ("Settled cyan wins" —
   web/calendar.js leaves the class off), so cyan means settled and only that.
   CO-PRO PARTNER HOLDS (2026-10-28, spec §7–§9) take it too: "H5 Colt Classic
   hold", cyan until 2026-10-29. On the partner's own screen their company's
   hold is the one solid, upright orange chip, "H1 Your hold", and the one chip
   there that opens anything; every other chip on that screen is a `<span>`
   with the plain pointer. */
.cal-ev.copro { --lc: var(--copro) }
.cal-ev.l-holds.mine { --lc: var(--copro); border-left-style: solid; font-style: normal; color: var(--copro) }
span.cal-ev { cursor: default }
/* While a partner is putting a hold, a night that can take one wears a dashed
   cyan ring (a picked night is the full `.rng`); a night that cannot — booked,
   past, ten holds — keeps the plain pointer. */
.cal-day.cp-can { outline: 1px dashed var(--accent-2); outline-offset: -2px }
.cal-day[aria-disabled="true"] { cursor: default }
#cp-notes { width: 100%; margin-top: 8px }
#cp-tools-msg { margin-top: 0 }
/* Settings › People › Co-pro partners: one block per company, a hairline over
   each, its name and counts on one row with Remove partner at the right. */
.cp-co { border-top: 1px solid var(--line-soft); margin-top: 10px; padding-top: 10px }
.cp-co .cal-row { margin-bottom: 6px }
.cp-co .att-row input { min-width: 240px }
.cal-more { display: none; margin-top: 3px; font-size: 10px; font-weight: 700; letter-spacing: .06em; color: var(--muted) }
.cal-avails { font-size: var(--note-fs); color: var(--muted); margin-top: 8px; line-height: 1.5 }
.cal-avails b { color: var(--text); font-weight: 600 }
#cal-empty { font-size: 12.5px; color: var(--muted); margin: 6px 0 10px }
#cal-empty:empty { display: none }
.cal-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px }
.cal-row .grow { flex: 1; min-width: 150px }
.tag { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted-2); font-weight: 700 }
/* The add panel's caption line wears .tag for its size and colour, but it
   carries a SENTENCE — "Saving…". The tag case is for the one-word status beside
   a show name, and a sentence is never shouted. Its refusals moved out to
   `#calw-err`, the app's own red line under the Save button, on 2026-09-09
   (walk W-6): a refusal printed at 10px in the dimmest token, in the slot every
   other panel uses for a static caption, is a refusal nobody reads. */
#calw-msg { text-transform: none; letter-spacing: .02em }
/* The add panel's Notes box runs the panel's width, and its button row stands
   10px clear of the dates note above it; the note beside the buttons rides the
   row's line (style wave, 2026-09-23 — these three were inline styles). */
#calw-notes { width: 100% }
.calw-acts { margin-top: 10px }
.calw-acts .muted-note { margin-top: 0 }
/* The phone's day list (web/calendar.js `LoadIn.calDayList`, audit R-I1): one
   line — the night, then a link per show, Close at the far end. Both calendars
   draw it on these classes. */
.cal-daylist { margin-bottom: 10px }
.cal-daylist .cal-row { margin-bottom: 0; gap: 6px 10px }
.cal-daylist .lnk { font-weight: 600 }
/* A private event for a viewer without screen.pe: its line is printed, not
   linked (fix wave fx-e), in the weight the links carry. The chip that opens
   it is a button, so it takes the grid's focus ring. */
.cal-daylist .cal-dl-info { font-weight: 600 }
.cal-ev[data-pe-info]:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 1px }
.cal-daylist .ib { margin-left: auto }
@media (max-width: 899px) {
  /* phone: the chip becomes a DOT in its layer colour; chips 4+ collapse to "+n" */
  .cal-day { min-height: 52px; padding: 3px }
  .cal-ev { display: inline-block; width: 7px; height: 7px; border-radius: 50%; padding: 0; border: 0; margin: 3px 1px 0; overflow: hidden; color: transparent; font-size: 0; background: var(--lc) }
  .cal-ev:nth-of-type(n+4) { display: none }
  .cal-more { display: inline; font-size: 10px; margin-left: 2px }
}

/* ── phone ────────────────────────────────────────────────────────────────── */
@media (max-width: 899px) {
  :root {
    /* 16px floor on focusable fields: below 16px iOS Safari zooms the page on
       focus and never zooms back. Raised on the TOKEN, so buttons and fields
       grow together and no control is pinned to a number. */
    --ctl-fs: 16px;
  }
  /* No bottom nav here — the rail becomes a strip across the top and the two
     screens scroll under it. Fewer moving parts than a second navigation. */
  #app { flex-direction: column }
  .rail { width: 100%; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 12px; border-right: 0; border-bottom: 1px solid var(--border) }
  .rail-brand { padding: 0; border-bottom: 0; display: flex; align-items: baseline; gap: 10px }
  /* Here the rail is a STRIP, and the brand shares its first row with the venue
     name. At 23px that row is the wordmark alone and the name drops to a line of
     its own, so the brand steps down with h1 — 19px, and the two still read as
     one row. */
  .rail .wordmark { font-size: 19px }
  .rail-venue { margin-top: 0 }
  .rail-nav { flex: 1 1 100%; flex-direction: row; padding: 0; gap: 6px; overflow-x: auto }
  .rail-nav button { width: auto; flex: 0 0 auto }
  /* In the strip a section keeps its place and loses its words: a 1px Soft Rule
     standing between the last tab of one section and the first of the next.
     The words are `display: none`, so the strip is no wider for them; a divider
     that would open the strip is not drawn at all. */
  .rail-sec { flex: 0 0 1px; align-self: stretch; margin: 6px 0; padding: 0; border: 0; background: var(--line-soft) }
  .rail-sec span { display: none }
  .rail-sec:first-child { display: none }
  .rail-links { flex: 1 1 100%; flex-direction: row; padding: 0; border-top: 0; overflow-x: auto }
  .rail-foot { margin-left: auto; padding: 0; border-top: 0 }
  .main { padding: 14px 14px 18px }
  h1, h2 { font-size: 19px }
  /* The screen subtitle is a sentence; at .14em tracking it ran to three lines
     on a phone. Less air between the letters, the same voice. */
  .sub { letter-spacing: .08em }
  .row3 { grid-template-columns: 1fr }
  .row3 b { margin-top: 6px }
  /* A PANEL HEADER WRAPS ON A PHONE (walk 2026-09-09, W-3). Contacts' header —
     the book's count, two captions, the search box, New contact and Import CSV —
     measured 525px inside a 345px panel whose `overflow-x` is visible: Import
     CSV sat at x 410–540, off the side of the screen, and nothing could scroll
     it back. `.panel-title` is a flex row with no wrap, so it simply ran past
     the edge; here it wraps, and the spacer that pushes the actions right on a
     desktop takes a whole line of its own so the actions start a new row rather
     than being squeezed into what is left of this one. */
  .panel-title { flex-wrap: wrap }
  .panel-title .grow { flex-basis: 100%; height: 0 }
  /* A THUMB, NOT A POINTER (style wave, 2026-09-23; audit R-M2, R-M3). The 22px
     icon button — Today's Open and Clear, a to-do's lock and ⋯, a task's
     arrows — was under WCAG 2.5.8's 24px and passed only on spacing, beside a
     row whose whole width ticks a to-do. On a phone it is 28px, which still sits
     inside a 51px Today row. Sign out, the one way out, was a 10.5px word with
     no padding; on a phone it is 12px inside 6px of target. */
  .ib { width: 28px; height: 28px }
  .rail-foot .lnk { font-size: 12px; padding: 6px }
  /* The owner's View as in the strip: one row with the name and Sign out, the
     select without its label word, and the foot allowed to shrink — a foot that
     could not was wider than the phone and scrolled the page sideways. */
  .rail-foot { flex: 0 1 auto; flex-wrap: nowrap; min-width: 0; max-width: 100% }
  .rf-viewas { flex: 0 1 auto; min-width: 0 }
  .rf-viewas span { display: none }
  .rf-viewas select { flex: none; width: auto; max-width: 132px }
}

/* ── the show page (#show/<id>) ────────────────────────────────────────────
   Tokens only, like every rule above it: the neon skin recolours this block
   without a line of its own. The money rules are the point — a column of
   figures only reads as a column when the digits are the same width, so every
   money cell and every money box is tabular-nums and right-aligned. */
.show-back { margin: 0 0 8px }
.show-back .lnk { font-size: 12px; font-weight: 600; color: var(--muted) }
/* The title and its Edit on one line: the link sits on the headline's own
   baseline so the h1 still reads as the one big thing on the screen. The h1
   keeps its size and gives its bottom margin to the row. */
.sh-title-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; margin: 0 0 6px }
.sh-title-row .lnk { font-size: 12px; font-weight: 600; color: var(--muted) }
.show-head h1 { margin: 0 }
.show-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 10px; font-size: 12px; color: var(--muted) }
/* The nights the show plays and — beside it, when there is one — the day the
   room committed to it. Two facts, two class names, one colour: the confirm
   stamp was born on `.show-dates`, which made the head carry two elements under
   the name of one. The settled stamp (2026-09-25) is a third fact in the same
   colour. */
.show-meta .show-dates, .show-meta .show-confirmed, .show-meta .show-settled { color: var(--muted) }
/* ── THE PILL: one recipe, declared once, unscoped (style wave, 2026-09-23) ──
   Anything that names a status is this. It used to be drawn nine ways (style
   review X2): a selector list here, scoped to the screens that had asked for it,
   and eight private copies further down this file, each with its own radius,
   fill or case — `.sm-tbl .pill`, `.reqh .pill`, `#scr-pe .pill` and its
   `.pill.status`, `.sh-pill`, `#scr-dayofshow .pill`, `.tw-chip`, `.mk-pill` and
   `.td-app`. The old worry that "a bare `.pill` would reach the calendar's
   legend chips" is gone with the legend: the switches that replaced it are
   `.lay`, and nothing else in the app is called `pill`.

   Three parts, and no fourth:
   · `.pill` AT REST — Raised Navy, Slate Lavender, 10px 700 at .06em, 20px
     round, never wrapping. A slot marker, a source, a kind.
   · `.pill.status` — a status: Lamp White on a 16% tint of `--lc`, bordered in
     `--lc`. `--lc` is Dim Slate until a tone names another.
   · A TONE — `go` Settled Green, `sky` Signal Cyan, `warn` Rental Amber, `bad`
     Refusal Red, `dim` Dim Slate. A tone draws the status look on its own, so
     `pill go` and `pill status go` are one pill.
   A screen's own words for its states (`s-confirmed`, Missing's `s-open` …) are
   one line each that sets `--lc` and nothing else — scoped wherever two screens
   use one word for two colours (Missing's `s-open` is dim, the Bar Checklist
   flag's is amber).

   ONE CASE: capitals, by `text-transform`, so the DOM keeps the word as it was
   written and a pin on the text still reads it. The one exception is
   `verbatim`: a value read off a row and printed as it was typed — the To-Do
   screen's app pill, where "Gmail / Drive / Prism" is one cell in the crew sheet.

   NO ALIASES remain: the screen lanes of the 2026-09-23 style wave renamed every
   private copy to these classes, and test/unit/style-gate.test.js goes red if
   one of the old names (.sh-pill, .tw-chip, .mk-pill, .td-app, #scr-pe .pill.s-*,
   .reqh .pill.ordered/.received) comes back. */
.pill {
  --lc: var(--muted-2); display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); border: 1px solid var(--border); background: var(--panel-2);
  border-radius: 20px; padding: 1px 8px; white-space: nowrap;
}
.pill.status, .pill.go, .pill.sky, .pill.warn, .pill.bad, .pill.dim {
  color: var(--text); border-color: var(--lc); background: color-mix(in srgb, var(--lc) 16%, transparent);
}
.pill.go { --lc: var(--accent) }
.pill.sky { --lc: var(--accent-2) }
.pill.warn { --lc: var(--warn) }
.pill.bad { --lc: var(--bad) }
.pill.dim { --lc: var(--muted-2) }
.pill.verbatim { text-transform: none }
/* The show's own states, on the show page's head, Today's six-week table and
   the Client Book's event history alike. Rental Amber is the calendar's own
   colour for a rental (Chris, 2026-09-08: the app's own colours); the show page
   never prints `s-rental`, because a status is never `rental`, and Today does. */
.pill.status.s-confirmed { --lc: var(--accent) }
.pill.status.s-hold { --lc: var(--muted-2) }
.pill.status.s-cancelled { --lc: var(--bad) }
.pill.status.s-cleared { --lc: var(--muted) }
.pill.status.s-settled { --lc: var(--accent-2) }
.pill.status.s-rental { --lc: var(--warn) }

/* ── THE TAB STRIP: one recipe for every range and every view switch ────────
   (style wave, 2026-09-23; style review X3). The show page's panes, Settings'
   tabs, a date range (Clover, Show P&L, Homebase; Payroll's week is an arrow
   stepper, `.pr-step`, not a strip), a view (Inventory),
   a list of people (To-Do's), a set of segments (the Client Book) and a pick of
   tonight's show (Day of Show) are all this: buttons in the control recipe on
   Desk Navy, 4px apart, the active one Raised Navy with a Signal Cyan border and
   a 2px inset underline. It SCROLLS SIDEWAYS rather than wrapping or clipping, so
   a phone never loses a button off the edge — the Tour Watch filter and the
   Homebase range were both measured clipped at 375px inside the segment recipe,
   which is `overflow: hidden`. `LoadIn.watchOverflow` paints the fade that says
   so, and web/app.js hands it every strip that lands in the main column (fix
   wave 2026-09-23, review M3).

   `.settabs.wrap` is the one variant: it WRAPS onto a second line instead of
   scrolling, for a strip a person must read whole — the To-Do screen's row of
   people, which is read on a phone and where a name past the edge is a person
   whose work nobody sees, and Mail's five destinations (polish, 2026-10-01),
   where a destination past the edge is one nobody picks.

   The SEGMENT (`.sm-basis`, below) is not a tab strip and stays the settlement
   basis's alone: Potential | Estimated | Actual is one question with three
   answers, drawn as one capsule. `.show-tabs` is the show page's name for this
   same strip and is declared here with it, not twice.

   Tour Watch's filters wrote a private copy of the segment (`.tw-seg`) until
   the style wave moved them onto this strip. The strip sits directly under
   the head card on the show page: the panes are the page, and burying them
   under a panel is how a reader stops finding them. */
.settabs, .show-tabs { display: flex; gap: 4px; margin: 0 0 12px; overflow-x: auto; scrollbar-width: thin }
.settabs button, .show-tabs button {
  flex: 0 0 auto; background: var(--panel); border: 1px solid var(--border); color: var(--muted); border-radius: var(--ctl-r);
  padding: var(--ctl-pad); font: inherit; font-size: var(--ctl-fs); font-weight: 600; cursor: pointer; white-space: nowrap;
}
.settabs button:hover, .show-tabs button:hover { color: var(--text); border-color: var(--muted-2) }
.settabs button.active, .show-tabs button.active { color: var(--text); background: var(--panel-2); border-color: var(--accent-2); box-shadow: inset 0 -2px 0 var(--accent-2) }
.settabs button:focus-visible, .show-tabs button:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 1px }
.settabs.wrap { flex-wrap: wrap; overflow-x: visible }
/* Inside a row of other controls (Clover's and Show P&L's range bar, Tour
   Watch's filter row, the Homebase range in its panel head) the strip takes no
   margin of its own: the row owns it. In a panel head the strip would inherit
   the eyebrow's capitals and tracking, so its words are put back as the strip
   writes them everywhere else. */
.cv-bar .settabs, .panel-title .settabs, #scr-tourwatch .tw-filterrow .settabs { margin: 0 }
.panel-title .settabs button { text-transform: none; letter-spacing: normal }

/* A control row on the show page: the label is a word beside the box, not a
   stacked field label (the .main label rule is for Setup and Settings). The
   DIRECTION has to be said: `.main label` is a column, and a rule that only
   names `display` keeps it — measured 2026-09-23, the head's Name, Renter and
   Drive folder and the hold-level row each sat over their box (style review
   item 26). The box beside the word takes its own width, not the label's. */
.sm-bar label, .show-head .sm-opt-fields label { display: inline-flex; flex-direction: row; align-items: center; flex: none; margin: 0 }
.sm-bar label input, .sm-bar label select, .show-head .sm-opt-fields label input { width: auto }
.sm-bar .muted-note { margin-top: 0 }
/* The hold's row in the show head: one level box per night, with the date as
   the word beside it. `.sm-bar label` already lays the pair out side by side —
   this is the gap between the two halves, and the date reading as a label
   rather than as a second value. */
.sh-holds .sm-bar-lbl { gap: 6px; color: var(--muted) }

/* Money columns, in tables and in boxes. A money box is a TEXT input (it has to
   survive a pasted "$1,250.00"), so the alignment rule keys on the cell, not on
   the input type. */
.data-table th.num, .data-table td.num { text-align: right }
.data-table td.num input, .sm-tbl input[type="number"] { text-align: right; font-variant-numeric: tabular-nums }
/* A money box holding something that is not a dollar amount: the save refused
   and named the column — this is the box it meant. */
.sm-tbl input.bad, input.bad { border-color: var(--bad); color: var(--bad) }

/* A FIXED row on the Payments table (2026-09-04): the engine's own obligation,
   with only the four person-owned boxes on it. Its first five cells are text
   where every other row has a box, so they are kept on one line — a wrapped
   "Settlement payout" beside a full-height select reads as a broken row — and
   the amount is set in the tabular figures every other money column uses. */
.sm-tbl tr.sm-fixed td { white-space: nowrap }
.sm-tbl tr.sm-fixed td.num { font-weight: 600; font-variant-numeric: tabular-nums }
/* THE TOTALS ROW (Chris, 2026-10-14: "add totals to the bottom of summed
   amounts. like Gross$"). A section table's <tfoot>: "Total" in the first
   column in the body's own voice (not the head's small capitals), each summed
   column's figure bold and tabular under its boxes, set in by the boxes' own
   inner padding so the digits line up with the digits above, and a rule over
   the row in Hairline Indigo, the one border every table rule is drawn in. */
.sm-tbl tfoot th, .sm-tbl tfoot td { border-top: 1px solid var(--border); border-bottom: 0; color: var(--text); font-weight: 700 }
.sm-tbl tfoot th { font-size: 13px; letter-spacing: 0; text-transform: none; padding: 8px }
.sm-tbl tfoot td.num { padding-right: 17px; white-space: nowrap; font-variant-numeric: tabular-nums }

/* Offer / settlement summaries: label left, figure right, one row per line. */
.sm-summary .sm-row { display: flex; align-items: baseline; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--line-soft); font-size: 12.5px }
.sm-summary .sm-row:last-child { border-bottom: 0 }
.sm-summary .sm-lbl { flex: 1; min-width: 0; color: var(--muted) }
.sm-summary .sm-lbl small { font-size: 10.5px; color: var(--muted-2) }
.sm-summary .sm-val { flex: none; min-width: 110px; text-align: right; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums }
.sm-summary .sm-row.child .sm-lbl { padding-left: 14px; color: var(--muted-2) }
.sm-summary .sm-row.strong .sm-lbl { color: var(--text); font-weight: 650 }
.sm-summary .sm-row.strong .sm-val { color: var(--accent) }
.sm-summary .sm-more > summary { cursor: pointer; padding: 6px 0; font-size: 12.5px; font-weight: 600; color: var(--muted) }
.sm-summary .sm-more > summary:hover { color: var(--accent-2) }
.sm-summary .sm-more > summary small { font-size: 10.5px; font-weight: 400; color: var(--muted-2) }
.sm-summary .sm-more[open] > summary { border-bottom: 1px solid var(--line-soft) }

/* The settlement basis segment (Potential | Estimated | Actual) — and the
   settlement basis's ALONE since the style wave (2026-09-23). Every other range
   and view switch is the tab strip further up, because this capsule is
   `overflow: hidden` and clips on a phone. Homebase's range, Payroll's week and
   Mail's destination wrote it until the style wave moved them off it. */
.sm-basis { display: inline-flex; border: 1px solid var(--border); border-radius: var(--ctl-r); overflow: hidden; background: var(--panel-2) }
.sm-basis button {
  border: 0; background: none; color: var(--muted); padding: var(--ctl-pad);
  font: inherit; font-size: var(--ctl-fs); font-weight: 600; cursor: pointer; white-space: nowrap;
}
.sm-basis button + button { border-left: 1px solid var(--border) }
.sm-basis button.active { background: var(--accent); color: var(--on-accent) }

/* The single-row document forms (Confirmation, Advance): a heading spans the
   grid so the fields under it visibly belong to it. */
.sm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px 12px }
/* A group heading. Inside the grid it spans every column, so the fields
   under it visibly belong to it rather than reading as one more field. */
.chk-h { margin: 12px 0 6px; font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700 }
.sm-grid .chk-h { grid-column: 1 / -1; margin-top: 6px }
.sm-grid label { display: block; margin: 0 0 3px; font-size: 11px; color: var(--muted) }
.sm-grid input, .sm-grid textarea, .sm-grid select { width: 100% }


/* The single-row documents (Confirmation, Advance) and the panels that carry
   read-only figures beside their tables. */
.sm-field { display: flex; flex-direction: column; gap: 3px; min-width: 0 }
.sm-field label { margin: 0 }
.sm-field input, .sm-field textarea { width: 100% }
.sm-bar .tog-inline { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 11px; color: var(--muted) }
.sm-bar .tog-inline input { width: auto }
/* The Advance's "Exclude from advance" tick (2026-09-28) on a field's head line:
   the field's own label on the left, the tick on the right, the same checkbox
   recipe as the bar's above. Written as `.sm-field-h label.tog-inline` because
   `.sm-grid label` (display: block) and `.main label` (a column) both reach it. */
.sm-field-h { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap }
.sm-field-h label.tog-inline { display: inline-flex; flex-direction: row; align-items: center; gap: 6px; margin: 0; font-size: 11px; color: var(--muted) }
.sm-field-h label.tog-inline input { width: auto }
/* A table cell holding a paragraph: the box breathes, the row stays compact. */
.sm-tbl textarea { padding: var(--ctl-pad-c); min-height: 54px }
/* A summary panel that also carries a table (Sales breakdown, Partner results)
   keeps its figures clear of the table above them. */
.sm-summary .tbl-wrap { margin-bottom: 8px }
.sm-summary .chk-h { margin-top: 8px }

/* The Partners panel's row badge: this has already gone out. */
.partner-sent { font-size: 10px; font-weight: 650; color: var(--accent); white-space: nowrap }

/* The send modal. A .panel floated over the page on the same tokens as every
   other panel — the overlay is the only new colour, and it is the page's own
   background at opacity rather than a black nobody chose. */
.doc-modal {
  position: fixed; inset: 0; z-index: 20; display: flex; align-items: flex-start; justify-content: center;
  padding: 24px 16px; overflow-y: auto; background: color-mix(in srgb, var(--bg) 82%, transparent);
}
.doc-modal-panel { width: 100%; max-width: 760px; margin: 0; box-shadow: 0 18px 48px rgba(0, 0, 0, .55) }
.doc-modal .sm-field { margin-bottom: 8px }
/* The title row: a heading in sentence case, the filename as its tag (the
   2026-10-19 critique, P3-7: "SEND OFFER OFFER · BELMONT · …" read as one caps
   eyebrow, the document said twice). Beside the heading on a desktop; on a
   phone the heading and the close button keep the first row and the tag takes
   the whole second one (a long title squeezed the filename into a column one
   word wide). */
.doc-modal-head { align-items: flex-start; flex-wrap: nowrap }
.doc-modal-h { font-size: 14px; font-weight: 650; letter-spacing: normal; text-transform: none; color: var(--text); flex: 0 1 auto; min-width: 0; line-height: 1.4 }
.doc-modal-head .tag { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; line-height: 1.4; padding-top: 2px }
@media (max-width: 899px) {
  .doc-modal-head { flex-wrap: wrap }
  .doc-modal-h { flex: 1 1 0 }
  .doc-modal-head .tag { order: 3; flex-basis: 100%; padding-top: 0 }
}
/* Send, Download PDF, Save to files, Close and the send line stay on screen at
   the foot of the window while the box scrolls (P3-7: Send sat ~1,700px below
   To, under the whole preview). The panel's own ground, edge to edge, so the
   preview scrolls under it rather than through it. */
.doc-modal .doc-actions {
  position: sticky; bottom: -24px; z-index: 1; background: var(--panel); border-top: 1px solid var(--border);
  margin: 4px -12px 0; padding: 10px 12px 12px
}
/* A value nobody edits, printed as TEXT rather than shut inside a disabled box
   (walk 2026-09-09, W-24). It wears the read-only colour every read-only value
   on the site wears, on the raised surface a field would have had, so the row
   still belongs to the form. It wraps instead of clipping: the From line is the
   one thing a person checks before they send. */
.sm-ro {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r);
  padding: var(--ctl-pad); font-size: var(--ctl-fs); color: var(--muted);
  overflow-wrap: anywhere; white-space: normal;
}
/* Its label is a field label, not a note under something: .muted-note carries a
   9px top margin for the notes that sit UNDER a control. */
.sm-ro-label { margin-top: 0 }
/* The document as it will be attached. White ground because that is the paper
   it prints on; the sandbox attribute is what keeps it inert.

   TALL, since review batch 6 (walk 2026-09-11, F12): the box was 320 px — about
   a quarter of a settlement, measured at 1,255 px — so "only about the first 250
   px of each document is readable", and a reader who reached for the wheel was
   working a scroller inside a scroller inside the modal's own. The whole point
   of the pane is that somebody checks the figures before the document goes out,
   and the modal's own line says so. It now takes most of the window's height, so
   a short document is whole and a long one shows most of itself and scrolls for
   the rest — under the pointer, which is what the wheel was already doing. The
   modal scrolls around it, for the fields (the bar sticks — .doc-actions).

   CAPPED at 1,000px (the 2026-10-19 critique, P3-7): on a very tall window 70vh
   drew a sheet of white under a short document. The sandboxed frame cannot be
   measured from outside, so it cannot fit itself to the paper; a cap is the
   sensible max, and the frame scrolls for the rest. */
.doc-preview {
  width: 100%; height: min(70vh, 1000px); min-height: 480px; border: 1px solid var(--border); border-radius: var(--ctl-r);
  background: #fff; margin: 8px 0 4px;
}
@media (max-width: 899px) { .doc-preview { height: 60vh; min-height: 320px } }

/* The To box, and only the To box: the chip strip that holds the recipients and
   the suggestion list that adds to it. Both sit INSIDE the .sm-field, under
   the input, so the row grows rather than the grid shifting. */
.chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px }
.chip { display: inline-flex; gap: 4px; align-items: center; border: 1px solid var(--border); border-radius: 12px; padding: 1px 8px; font-size: 11px }
.chip button { background: none; border: 0; color: var(--muted); cursor: pointer; font: inherit }
.sug { list-style: none; margin: 2px 0 0; padding: 0; border: 1px solid var(--border); background: var(--panel-2); border-radius: 8px; max-height: 180px; overflow: auto }
.sug li { padding: 6px 10px; cursor: pointer; font-size: 12px }
.sug li:hover { background: var(--panel) }

@media (max-width: 899px) {
  .sm-summary .sm-val { min-width: 92px }
}

/* ── Wave B · the Actuals CSV import ──────────────────────────────────────
   The file input is HIDDEN rather than absent: a real input is what holds the
   chosen file, and the button beside it is what a person aims at. Clipped
   rather than display:none so the control still exists for the click() that
   opens it, and out of the tab order so the button is the one keyboard stop. */
.imp-file {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Nothing chosen, nothing to show — an empty host must not leave a gap under
   the save bar. */
.imp-panel:empty { display: none }
/* The picker and the preview: a panel INSIDE the panel, on the same tokens, so
   what it says reads as being about the table above it. */
.imp-block {
  margin-top: 10px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--ctl-r); background: var(--panel-2);
}
.imp-block .chk-h { margin-top: 0 }
.imp-block .sm-bar { margin-top: 8px }
.imp-block .tbl-wrap { margin-bottom: 6px }
/* Contacts' New contact form, opened under the book's header row and above its
   table (web/contacts.js placeForm, 2026-09-24): the same panel-inside-a-panel
   tokens as the import block. */
#ct-form.ct-inset {
  margin: 0 0 10px; padding: 10px 12px; border-radius: var(--ctl-r); background: var(--panel-2);
}
/* The way back to the column picker after a list of bad lines: one link, on its
   own, so it does not read as a second Import button. */
.imp-remap { padding: 6px 12px }
.imp-remap .lnk { font-size: 11px; font-weight: 600; color: var(--accent-2) }

/* ── The pickers (web/pickers.js) ───────────────────────────────────────────
   A control keeps the stored string on one hidden or real input; these rules
   only lay out the visible parts. */
.pk { display: inline-flex; align-items: center; gap: 4px; max-width: 100% }
/* The compact shorthand, and then the chevron's room back — the same pair the
   table rule makes, for the same reason (review 2026-09-09, M-4). Two
   declarations rather than one because `padding` is the whole of it and the
   longhand has to come after. */
.pk-time select, .pk-datetime select { width: auto; min-width: 54px; padding: var(--ctl-pad-c); padding-right: 22px }
.pk-datetime .pk-date { width: 128px; padding: var(--ctl-pad-c) }
.pk-colon { color: var(--muted); font-weight: 700 }
.pk-money { position: relative; width: 100% }
.pk-money .pk-cur { position: absolute; left: 9px; color: var(--muted); font-size: 12px; pointer-events: none }
.pk-money .pk-amt { padding-left: 20px; text-align: right; font-variant-numeric: tabular-nums }
/* One list for the whole page, moved under whichever amount box has focus. It
   is a child of <body> and positioned in page coordinates, so nothing a table
   clips can cut it off. Its corner is the control radius, the house suggestion
   list's (`.sug`), and a row inside it takes the chip's 4px — they were 10px and
   6px, off the scale (style wave, 2026-09-23; the detector baseline). */
#pk-list {
  position: absolute; z-index: 40; width: 150px; max-height: 236px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--accent-2); border-radius: var(--ctl-r);
  padding: 4px; box-shadow: 0 10px 30px rgba(0,0,0,.45); scrollbar-width: thin;
}
#pk-list button {
  display: block; width: 100%; text-align: right; background: transparent; border: 0;
  color: var(--text); border-radius: 4px; padding: 5px 12px; font: inherit; font-size: 12.5px;
  cursor: pointer; font-variant-numeric: tabular-nums;
}
#pk-list button:hover { background: var(--panel-2) }
#pk-list button.cur { background: var(--panel-2); color: var(--accent-2); font-weight: 700 }
#pk-list[hidden] { display: none }

/* A deal's second line: the escalator and merch groups, opened by their Yes. */
/* --panel-2, not the sketch's literal rgba(28,36,64,.45) — which IS the original
   skin's --panel-2. This file's skin rule is that every colour reads a token, so
   the band follows a skin swap instead of staying the first skin's navy. */
.sm-tbl tr.sm-detail td { background: var(--panel-2); padding: 8px 12px }
.sm-grp { display: inline-flex; align-items: center; gap: 8px; margin-right: 26px; vertical-align: middle }
.sm-grp[hidden] { display: none }
.sm-grp-lbl { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); font-weight: 700; white-space: nowrap }
.sm-grp-f { display: inline-flex; align-items: center; gap: 6px }
.sm-grp-f .muted-note { margin: 0; white-space: nowrap }
.sm-grp-f input, .sm-grp-f select { width: auto }
/* A BOX THAT FILLS ITS CELL, and the column's floor (style wave, 2026-09-23;
   style review, show page). The show page's tables, its pickers and the notes
   boxes wrote `width:100%;min-width:<n>px` inline on every box — a rule no skin
   and no stylesheet could reach. They are two classes now: `fill` is the whole
   cell, `mw-<n>` is the floor a column names (`w: '88px'` in
   web/show-sections.js becomes `mw-88`; test/unit/style-lane-a.test.js pins that
   every width a column names has its rule here). `input.fill` is written after
   `.sm-grp-f input` on purpose: at the same weight the later rule wins, which is
   what the inline style did to it — the deal band's boxes keep the width they
   had. `resize-v` is the textareas' one direction of resize.
   Measured 2026-10-19 (Run Sheet critique): on a typed input a bare `.mw-<n>`
   loses to the base control rule's `min-width: 0` (input[type=…] weighs more),
   so the floor holds for dropdowns and text areas only — Run Sheet re-states it
   for its tables where it needs it (the lane A block at the end of this file). */
input.fill, select.fill, textarea.fill { width: 100% }
textarea.resize-v { resize: vertical }
.mw-60 { min-width: 60px } .mw-64 { min-width: 64px } .mw-70 { min-width: 70px } .mw-72 { min-width: 72px } .mw-76 { min-width: 76px }
.mw-78 { min-width: 78px } .mw-80 { min-width: 80px } .mw-82 { min-width: 82px } .mw-84 { min-width: 84px }
.mw-88 { min-width: 88px } .mw-90 { min-width: 90px } .mw-98 { min-width: 98px } .mw-100 { min-width: 100px }
.mw-104 { min-width: 104px } .mw-110 { min-width: 110px } .mw-112 { min-width: 112px } .mw-118 { min-width: 118px }
.mw-120 { min-width: 120px } .mw-140 { min-width: 140px } .mw-160 { min-width: 160px } .mw-220 { min-width: 220px }
/* The templates panel's picker and name box share the row's spare width. */
[data-tpl] .tpl-box { flex: 1; min-width: 160px }
.muted-note.sh-rental-note { margin: 0 0 8px }

/* The Offer tab's options line: two questions side by side, each opening its own fields. */
.sm-options { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: center }
.sm-opt { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-height: 36px }
.sm-opt .tog-inline { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text); cursor: pointer; margin: 0 }
.sm-opt-fields { display: inline-flex; align-items: center; gap: 8px; padding-left: 12px; border-left: 1px solid var(--border) }
.sm-opt-fields[hidden] { display: none }
.sm-opt-fields input { width: auto }
/* The same reveal, used once on the show HEAD — Confirm's "which day does the
   show land on?" for a hold over more than one night. There it stands on a row
   of its own instead of beside the checkbox that opened it, so it drops the
   divider that separates it from that checkbox and takes the head's own row
   rhythm and wrapping instead. The [hidden] rule is restated because these two
   selectors weigh the same and the later one would otherwise win and show a row
   nobody has asked for yet. */
.show-head .sm-opt-fields { display: flex; flex-wrap: wrap; padding: 9px 0 0; border-left: 0 }
.show-head .sm-opt-fields[hidden] { display: none }
@media (max-width: 899px) { .sm-options { grid-template-columns: 1fr } }
/* Guards, not decoration. `#scr-show` is painted inside `<main class="main">`, so
   the Setup/Settings stacked-field rules further up — `.main label { display: flex;
   flex-direction: column; margin-bottom: 9px }` and `.main label input { width:
   100%; max-width: 420px }` — reach every label on the show page as well. The head
   markup these two questions came from was a `.sm-bar`, which carries its own
   escape (`.sm-bar label`, `.sm-bar .tog-inline input`); `.sm-options` is not one,
   and without the three lines below the checkbox stretches to 420px and stacks
   ABOVE its question instead of sitting beside it, and the "Sales tax %" label
   drops 9px out of the line. Two classes each, so each outweighs `.main label`. */
.sm-opt .tog-inline { flex-direction: row }
/* 16px is the box `input[type="checkbox"]` sets for the whole app; it is only
   restated because `.main label input`'s 100% outweighs it here. */
.sm-opt .tog-inline input[type="checkbox"] { width: 16px }
.sm-opt .sm-opt-fields .muted-note { margin: 0 }

/* The confirmation's two addressed panels wear the colour of who reads them. */
.panel.int { border-left: 3px solid var(--accent-2) }
.panel.ext { border-left: 3px solid var(--warn) }
.tag.tag-int { color: var(--accent-2) } .tag.tag-ext { color: var(--warn) }
.sm-grid .sm-field-wide { grid-column: 1 / -1 }

/* The advance shows what its ladder will PRINT, in the document's own shape:
   fixed pitch, the lines unwrapped, and scrolled sideways rather than folded —
   a folded line is not the line that goes out. */
.sm-prints { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 10px 12px; margin: 0; font-family: Consolas, 'Segoe UI Mono', monospace; font-size: 12px; white-space: pre; color: var(--text); line-height: 1.6; overflow-x: auto }

/* The show's history — one line per thing that happened, at the foot of every
   pane. Three columns: when, who, and what (the action key, then its summary).
   Not a .data-table: these are lines to be read down, not figures to compare
   across, and a table's cell padding would double the height of a panel that is
   mostly a list. The last row drops its rule so the panel does not end on one.
   The when column is 150px, not 130: the clock is the house's 12-hour one
   (Chris, 2026-09-04), so the widest stamp is `2026-11-06 12:45 PM`, and 130
   wrapped that onto two lines. */
.hist-row { display: grid; grid-template-columns: 150px 1fr 2fr; gap: 8px; font-size: 12px; padding: 5px 0; border-bottom: 1px solid var(--line-soft) }
.hist-row:last-of-type { border-bottom: 0 }
.hist-row .m { color: var(--muted) }
/* What happened, in the page's own words (web/show-history.js actionName; Run
   Sheet critique 2026-10-19): a weight heavier than the summary after it. */
.hist-row .hist-act { font-weight: 600 }
/* At phone width three columns leave the summary a thumbnail's width, so the
   line stacks: when and who on one row, what underneath. */
@media (max-width: 899px) { .hist-row { grid-template-columns: 150px 1fr } .hist-row span:last-child { grid-column: 1 / -1 } }

/* ── Settings in tabs ─────────────────────────────────────────────────────
   Chris's ruling 2026-09-05: selection tabs inside Settings for the different
   areas. The strip is THE tab strip (.settabs, declared once with the show
   page's .show-tabs further up this file), because two tab strips in one app
   that look different are two things to learn. It sits
   under the h1 rather than inside a panel, for the reason the show page gives:
   the groups ARE the page, and burying the way into them is how a reader stops
   finding them.

   A hidden group is `[hidden]` and nothing more: the whole page is painted from
   one render, so every id on it exists whichever tab is open, and switching is a
   flag rather than a round trip. `display: none` is spelled out because .panel
   inside it sets `display` of its own on some children and `[hidden]` alone
   loses to any later display rule. */
.setgrp[hidden] { display: none }
/* WHILE A SETTINGS RENDER IS IN FLIGHT, NOTHING ON IT CAN BE PRESSED. Settings
   is the one screen that paints all of itself at the END of its loads
   (web/settings.js says why at length), so until that paint lands the markup on
   the glass belongs to the PREVIOUS visit and is about to be discarded — a click
   or a keystroke on it is thrown away with it, along with the answer to any save
   it started. `aria-busy` is set for that window and removed by the paint, so a
   press simply waits for the controls it belongs to. It is announced rather than
   only styled: `aria-busy` is what a screen reader is listening for, and
   `#loadline` is already running across the top for everyone else. */
.screen[aria-busy="true"] { pointer-events: none }
/* The feed link is a URL to be read character by character before it is pasted
   somewhere it cannot be taken back from, so it is fixed pitch and wraps rather
   than running off the row. */
#st-feed-url { font-family: Consolas, 'Segoe UI Mono', monospace; font-size: 12px; word-break: break-all; color: var(--text) }
/* ── Today (the landing screen) ────────────────────────────────────────────── */
/* Three columns of open items over one six-week grid. The grid itself is the
   calendar's — .cal-grid/.cal-day/.cal-ev unchanged — so a hold looks the same
   on both screens, chips included: both are anchors to their show now, so the
   block and the missing underline live in .cal-ev itself. */
/* The tiles over the columns (Chris, 2026-09-06: "with the name of each column
   large with a large number"). Three across, one per column, on the columns'
   own grid so a tile sits over the list it summarises. Each is an anchor to its
   column — the label is small and quiet, the number is the thing seen from
   across a room, and the sub-line is the sentence under it. */
.tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px }
/* `container-type` makes the tile the ruler its own figure is measured against
   (style wave, 2026-09-23 — see .tile-value). A tile is always a grid item, so
   its width never depended on what it holds, which is the one thing inline-size
   containment takes away. */
.tile { display: block; background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 16px 18px; color: var(--text); text-decoration: none; container-type: inline-size }
.tile-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; font-weight: 700 }
/* THE FIGURE IS SIZED BY ITS TILE, not by the window (style wave, 2026-09-23;
   style review item 9 and X9). 34px is the house figure, and it overflowed: at
   375px Show P&L's "$10,755.92" measured 199px in a 166px tile and every
   Shortage tile 198–203px, so the page scrolled sideways; and a four-across row
   on a 1,024px laptop leaves about 144px inside a tile, where the same figure
   overflows too. Two private sizes had been patched in to cope — 22px on the
   Event Calendar and 18px (the date size) on the Client Book's money tile.

   One rule instead: 15% of the tile's own inside width, never more than 34px and
   never less than 16px. Measured on this face at 800 weight a figure is about
   0.585em a character, so a money figure of eleven characters ("$123,456.78") is
   6.4em and fits at 15%. On a desktop with room it is the 34px it always was;
   in a 2×2 grid on a phone it is about 19px, which is the Client Book's measured
   fit. A viewport breakpoint cannot do this, because the same window holds a
   three-across Today and a five-across Client Book. The plain 34px before it is
   for a browser that does not know container units. */
.tile-value { font-size: 34px; font-size: clamp(16px, 15cqi, 34px); font-weight: 800; margin-top: 4px; line-height: 1.1; font-variant-numeric: tabular-nums; overflow-wrap: anywhere }
.tile-sub { font-size: 12px; color: var(--muted); margin-top: 4px }
/* Colour on the NUMBER only, and only for the two states a person acts on:
   something is overdue, or there is nothing to do. Everything in between is
   the ordinary text colour, so red keeps meaning what it means. */
.tile.hot .tile-value { color: var(--bad) }
.tile.ok .tile-value { color: var(--accent) }
.tile:hover { border-color: var(--muted-2) }
/* THREE BOXES, SIDE BY SIDE, AT EVERY WIDTH (Chris, 2026-09-08: "Can we keep
   the three boxes side by side on the today page? I like that look."). A media
   query used to fold them into one column under 900px, so the look he asked for
   was gone on a narrow laptop, a split window and every phone. The floor is
   240px a column, and the ROW scrolls sideways inside itself when three of those
   do not fit — the document itself never scrolls horizontally, which is the rule
   the 2026-09-09 walk holds every screen to. */
.today-cols { display: grid; grid-template-columns: repeat(3, minmax(240px, 1fr)); gap: 12px; overflow-x: auto }
/* Five rows and a scrolling bar (Chris, 2026-09-06: "a maximum of 5 items each
   and have a scrolling bar"). 262px is MEASURED, not guessed: a .today-item is
   51px in the browser (8px + 8px of padding around its two lines, plus the 1px
   divider), so five whole rows are 255 and the remaining 7 are the height a
   sixth row must NOT fit in. Nothing peeks over the fold and nothing is meant
   to: those 7px sit under the 22px fade `.today-list.more` paints, and the fade
   is what tells a person there is more below before they have touched the wheel
   (walk 2026-09-09, W-13 — the sentence that used to stand here claimed the
   peek, which was the finding, not the fix). test/e2e/next-list-today.spec.js
   re-measures the row and fails if five stop fitting or a sixth starts to. */
/* The bar itself wears the page's tokens (Chris, 2026-09-08). `scrollbar-width:
   thin` was the whole rule and is all Firefox reads; `scrollbar-color` gives
   Firefox the house thumb (`--sb-thumb`) on nothing, and the ::-webkit pair is the same
   bar for Chrome and Safari — .main's two rules at 8px rather than 10, because
   this scroller is one column of a row of three and not the page. */
.today-list { max-height: 262px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--sb-thumb) transparent; padding-right: 4px }
.today-list::-webkit-scrollbar { width: 8px }
.today-list::-webkit-scrollbar-thumb { background: var(--sb-thumb); border-radius: 4px }
/* A list with more in it than fits (walk 2026-09-09, W-13). The height stays
   where Chris put it — "a maximum of 5 items each" — and no sixth row is half
   shown; what changes is that the bottom edge fades into the panel, which is how
   a reader learns there is more without the column growing. `more` is set by
   web/today.js after paint, so it is a fact about what is actually in the box.
   Sticky rather than absolute: the fade belongs to the SCROLLER's bottom edge
   and must not travel with the rows under it. The negative margin is what stops
   it claiming a row's worth of height. */
.today-list.more::after {
  content: ''; display: block; position: sticky; bottom: 0; height: 22px; margin-top: -22px;
  background: linear-gradient(to bottom, transparent, var(--panel)); pointer-events: none;
}
/* The row is a DIV holding a link since 2026-09-08 — a button may not sit
   inside an anchor — so the link is `.today-go` and the row is the flex box
   around it. `align-items: center` puts the two 22px buttons on the middle of
   the two lines of text: they are shorter than the text, so they add no height
   and the row stays 51px. */
/* 51px tall on EVERY platform: the two text lines measure 34px on Windows
   and 32px on the Linux gate runner (system-ui is a different face there), and
   the five-row height of .today-list is figured from 51 — so the row states it
   rather than inheriting it. Measured on gate 34278142150. */
.today-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 0; min-height: 51px; box-sizing: border-box; border-bottom: 1px solid var(--line-soft); color: var(--text) }
.today-item:last-child { border-bottom: 0 }
.today-go { display: block; flex: 1; min-width: 0; color: inherit; text-decoration: none }
.today-go:hover { color: var(--accent-2) }
/* Two lines and never three. The name half is the flexible one, so anything the
   right cluster gains — the two buttons, and the question one of them turns into
   — comes out of its width; without this a long name simply wrapped and the row
   grew. Each line is now cut with an ellipsis instead, which is what keeps 51px
   a property of the row rather than of what happens to be in it. */
/* AND ONLY HALF OF EACH LINE IS CUT (Impeccable critique, 2026-10-01). Each
   line is two spans (web/today.js `halves`; three on a money row's small line,
   whose request mark follows its due date): `.tw-cut` takes the ellipsis,
   `.tw-keep` never does — the kind on a money title ("· Payout"), the date a
   small line ends in or a money row is due on — because those are the facts that tell two rows
   apart, and the hover that held the whole line cannot open on a phone.
   `white-space: pre` on both halves, because a flex item drops the space at its
   own edge and " · Payout" printed as "· Payout" against the name. The cut half
   keeps 2ch — at least a letter and the ellipsis — and the line clips what
   still does not fit rather than letting it run under the buttons. */
.today-go b { display: flex; min-width: 0; white-space: nowrap }
.today-item small { display: flex; min-width: 0; color: var(--muted); font-size: 11px; white-space: nowrap }
.tw-cut { min-width: 2ch; overflow: hidden; text-overflow: ellipsis; white-space: pre }
.today-go .who { overflow: hidden }
.tw-keep { flex: none; white-space: pre }
/* AND THE LINE IS NEVER NARROWER THAN 2ch AND THE KIND (pe-walk, 2026-10-02).
   The kind span never shrank — the LINE did: with the Bar column Today draws
   four columns, and at 1280px a money row's text had 71px between its left
   edge and the right cluster (the figure, Open and Clear). 2ch of payee and
   " · Payout" are 84px, so `.who`'s clip took the last 13 — "Payo" on the
   walk, measured again in test/e2e/pe-walk-today.spec.js. So a list under
   300px wide drops the Open button, as a phone does (below) and for the phone's
   reason: the row is already the link, and Open's 28px are what the box is
   short of. Clear stays. Measured on the list itself (`container-type`), so
   it follows the column, not the window: three columns at 1280 keep Open.
   AND A KIND TOO LONG FOR THE LINE TAKES A LINE OF ITS OWN (review I3). Open
   gone, a 246px column still has 99–125px of title beside a $12,250.00 figure,
   and " · Co-pro settlement" is 150px — measured for every KIND word in the
   same spec, at 1280 and at 375. So in a narrow list the title may wrap: the
   payee's basis is its 2ch floor, so the kind stays on the payee's line
   whenever 2ch and the kind fit (Deposit, Payout, Refund, Other), and a kind
   that cannot fit there (Co-pro settlement, Vendor deposit) moves under it,
   whole — the row grows a line rather than cutting the fact that tells two
   rows apart. The payee then has the first line to itself, still cut with its
   ellipsis. A whole line is 99px beside the figure and Clear, still short of
   150, so a kind on its own line wraps between its words ("· Co-pro" over
   "settlement") rather than being cut.
   AND WITH ANY FONT, AT ANY WIDTH, ON BOTH LINES (gate run 37076035481). The
   Linux runner's fallback face is wider than Windows': the $12,250.00 figure
   grew, the line beside it was 83px, and "settlement" alone was wider than
   that, so a kind already on its own line was cut mid-word. A wide face did the
   same to the small line's due date ("overdue since" + " Thu Oct 1" in 72px).
   So the wrap is no longer a narrow-list rule: wherever a kept half cannot fit
   beside 2ch of its cut half it takes a line of its own, on the title and on
   the small line alike, and a word wider than its line breaks inside itself
   (`overflow-wrap: anywhere`) as the last resort — cut letters never. In a
   list where everything fits nothing wraps, so the row is still 51px there.
   `pre-wrap`, not `normal`: it keeps the space that starts a kept half (" ·
   Payout" against the payee) and still breaks between words. The payee grows
   only to its own width (`max-width: max-content`), so a short one does not
   push its kind to the far edge of the line; a cut half AFTER the kept one (a
   money row's request mark) has no 2ch floor, so it never takes a line of its
   own — it shows what fits beside the date. The test is font-proof: the spec
   draws every kind word in a forced wide face too, at 1280 and 375. */
.today-list { container-type: inline-size }
@container (max-width: 299px) {
  .today-item .today-open { display: none }
}
/* NOT while a Clear is armed: the question widens the right cluster for 2.6
   seconds, and a title re-wrapping under it grew the row to 93px and moved the
   ✕ the second click needs (test/e2e/phone-batch3.spec.js). The armed row keeps
   its one title line and its 51px; the question is what it is saying then. */
.today-item:not(:has(.today-arm:not([hidden]))) .today-go b,
.today-item:not(:has(.today-arm:not([hidden]))) .today-go small { flex-wrap: wrap }
.today-item:not(:has(.today-arm:not([hidden]))) .today-go b > .tw-cut,
.today-item:not(:has(.today-arm:not([hidden]))) .today-go small > .tw-cut { flex: 1 1 2ch; max-width: max-content }
.today-item:not(:has(.today-arm:not([hidden]))) .today-go small > .tw-keep ~ .tw-cut { flex-basis: 0; min-width: 0 }
.today-item:not(:has(.today-arm:not([hidden]))) .today-go b > .tw-keep,
.today-item:not(:has(.today-arm:not([hidden]))) .today-go small > .tw-keep { flex: 0 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere }
/* A request that has gone past its due date (Chris, 2026-09-08: "Overdue on
   Today, you resend"). The line already says so in words; this is the one colour
   the app gives a date that has gone by, the same --bad the overdue money wears. */
.today-item small.overdue { color: var(--bad) }
/* The desktop's AGE WORD, as its own badge in the right cluster (stage 2,
   ruling B1; flight-director.js qi(), 2778–2803). It sits where the amount sits
   on a money row and is painted only where there is no amount, so the 51px row
   does not move. Red for "overdue / out / past due", amber for "due" and its
   kin — the desktop's own two tones, in this app's tokens. NOT the small line's
   colour: that one belongs to the request mark above and means something else. */
/* 11px, not 10 (Impeccable critique, 2026-10-01): the Ten-Pixel Floor lets a
   label sit at 10px beside larger text, and "Text that carries a fact on its
   own is 11px or more" — this word is the only carrier of OVERDUE on its row. */
.today-age { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); white-space: nowrap }
.today-age.bad { color: var(--bad) }
.today-age.warn { color: var(--warn) }
/* SEVERITY, the desktop's accent down the left edge (sev 1–3, decided in
   server/lib/today.js TONE). An inset shadow rather than a border-left, so the
   accent cannot change the box the row occupies inside its column — the row is
   51px by measurement and a border-left is part of the box. The padding on the
   next line is what holds the text clear of the accent, and it applies to the
   toned rows only; every waiting and missing row carries a severity, so each
   column is internally aligned. In the money column only an OVERDUE row carries
   one — sev 1, beside its red figure (2026-10-01) — and the rest keep the zero
   padding they always had. */
.today-item.sev-1 { box-shadow: inset 3px 0 0 var(--bad) }
.today-item.sev-2 { box-shadow: inset 3px 0 0 var(--warn) }
.today-item.sev-3 { box-shadow: inset 3px 0 0 var(--muted-2) }
.today-item[class*="sev-"] { padding-left: 9px }
.today-right { flex: none; display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--muted) }
.today-amt { font-variant-numeric: tabular-nums }
/* Overdue money: the figure in Refusal Red (Impeccable critique, 2026-10-01),
   the same red as its tile's figure and the stripe beside it. */
.today-amt.bad { color: var(--bad) }
/* The armed question, BESIDE the amount and never under it: a second line here
   would grow the row. 10.5px is this file's floor for a label. Amber, with the
   button it belongs to (rulings review 2026-09-09, finding 2). */
.today-arm { font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--warn); white-space: nowrap }
/* A REFUSED clear, in the same slot (Impeccable critique, 2026-10-01): a
   refusal is a sentence in red, in its own case, never uppercased and never a
   toast. Up to two lines of 11px, which still sit inside the 51px row. */
.today-arm.bad { color: var(--bad); font-size: 11px; font-weight: 600; letter-spacing: 0; text-transform: none; white-space: normal; max-width: 15em; line-height: 1.25 }
/* A refused READ, in the sub-line where "Loading…" stood — in its own case,
   because the sub-line's capitals are for the date and the count. */
.sub.refused { color: var(--bad); text-transform: none; letter-spacing: normal }
/* A tag in red: something to act on (the Bar Checklist's flags, a task gone
   late). Today's column heads no longer use it for a plain count (ruling 8,
   2026-10-01) — a count is the rail badge's green `.cnt` below. */
.tag.bad { color: var(--bad) }
/* The rail's Today badge. It is part of the button's TEXT — it always prints,
   0 included — so the rail never jumps when the first count lands. Since
   2026-10-01 it is also the count in a Today column head that has no tile over
   it (the Bar; Chris, ruling 8: "panel count tags GREEN like the rail badge"). */
.cnt { display: inline-block; min-width: 18px; padding: 0 6px; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 10px; font-weight: 700; margin-left: 6px; text-align: center }
/* The six-week table on Today (2026-09-08). `.panel-title .grow` pushes the
   range to the right end of the head, as .att-row's spacer does. */
/* This head only, not every `.panel-title` (thirty screens share that class and
   none of them should move): NEXT SIX WEEKS + THIS WEEK + 5 + SEP 6 – OCT 17 fits
   a 375px panel by a pixel, so on a narrower phone the range drops to a second line
   instead of running past the edge. `.grow` still fills the gap on one line at
   desktop, and the range itself never breaks mid-date. */
#today-up-head { flex-wrap: wrap }
#today-up-range { white-space: nowrap }
#today-upcoming td.num, #today-upcoming th.num { text-align: right; font-variant-numeric: tabular-nums }
#today-upcoming td.empty { padding: 10px 8px }
/* ON A PHONE THE TILES ARE ONE SHORT STRIP (Chris, 2026-10-01, ruling 2: "one
   short strip under 899px — label + figure side by side, ~64px, sentence
   dropped"). Stacked, the three were 109px each and the first open item began
   at y≈630 of 812. The three boxes under them stay side by side, his 2026-09-08
   ruling. Scoped to `#today-tiles`: every other screen's tiles still stack. */
/* FEWER tiles, for a role that does not hold every tab behind them (Chris,
   2026-09-24 for Missing, 2026-09-25 for Money and Waiting): the server sends
   only the lists the role is shown and web/today.js draws one tile per list, so
   the row is split between the tiles drawn rather than keeping a hole where a
   third would have sat. `data-tiles` is the count web/today.js writes. The id
   outranks `.tiles` inside the media query too, which is why the stacking rule
   is said again for it. */
#today-tiles[data-tiles="1"] { grid-template-columns: minmax(0, 1fr) }
#today-tiles[data-tiles="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)) }
@media (max-width: 899px) {
  .tiles { grid-template-columns: 1fr }
  #today-tiles[data-tiles] { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px }
  #today-tiles[data-tiles="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)) }
  #today-tiles[data-tiles="1"] { grid-template-columns: minmax(0, 1fr) }
  #today-tiles .tile { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 8px 10px }
  #today-tiles .tile-label { font-size: 10px; letter-spacing: .04em; line-height: 1.2; min-width: 0 }
  #today-tiles .tile-value { flex: none; margin-top: 0 }
  #today-tiles .tile-sub { display: none }
  /* No Open on a phone (Impeccable critique, 2026-10-01): the row is already
     the link, and its 34px are what a 240px box is short of. Clear stays. */
  .today-item .today-open { display: none }
  /* The armed question wraps on a phone, in the same ruled words (im-fix
     review I2): "CLEAR FOR EVERYONE?" on one line pushed the ✕ the second
     click needs past the right edge of a 240px box — measured at 375px, the ✕
     sat outside the list. Two lines of 10.5px still sit inside the 51px row; a
     refused clear's sentence wraps in the same width, the row growing for it. */
  .today-arm, .today-arm.bad { white-space: normal; max-width: 7em; text-align: right; line-height: 1.15 }
  /* The six-week table's columns weighted for a phone (Impeccable critique,
     2026-10-01): the date wrapped to two lines and a long name to four inside
     a 321px panel. Now the date and the status hold one line, the name has a
     floor, and the table scrolls sideways inside .tbl-wrap, as Mail's does. */
  #today-upcoming td:first-child, #today-upcoming td:nth-child(3) { white-space: nowrap }
  #today-upcoming td:nth-child(2) { min-width: 12em }
}

/* ── the Payments board ───────────────────────────────────────────────────
   One wide table of every payment in a window, with the server's totals in the
   filter bar beside it. The figures sit where the eye lands after the dates,
   which is why they ride in the .att-row rather than under the table. */
/* Four figures since 2026-09-06 (In, Out, Unpaid in, Unpaid out), so the strip
   wraps onto a second line rather than pushing the filter bar off the screen. */
.pb-totals { display: flex; flex-wrap: wrap; gap: 4px 16px; flex: none; font-size: 12px; color: var(--muted) }
.pb-totals b { color: var(--text); font-variant-numeric: tabular-nums; margin-left: 4px }
/* The show's date under its name: the second line of the first cell, which
   is the house note (`.muted-note`, DESIGN.md › Tables: "a row's second line,
   not a second column") rather than the `.pb-sub` copy of it this was until
   the style wave (2026-09-23). Two pixels under the link, not the note's nine. */
#pb-rows td .muted-note { margin-top: 2px }
/* Method and Ref are short answers ("ACH", a cheque number), so the row does
   not spend a full column's width on either. */
#pb-rows input.pb-narrow { width: 84px }
/* Save, then the row's own answer beside it. .muted-note carries a top margin
   for the forms it was written for; in a table cell it would push the button
   line off centre. */
#pb-rows td.pb-act { white-space: nowrap }
/* A date is one line (Chris, 2026-09-07: with Date given its own head, Due wrapped
   "Aug 19, 2026" onto three lines and every row grew). */
#pb-rows td.pb-due { white-space: nowrap }
#pb-rows td.pb-act .muted-note { display: inline; margin-top: 0 }
@media (max-width: 899px) {
  /* The strip already wraps INSIDE itself; what it could not do was give up any
     width to wrap in. `flex: none` meant it took its max-content 418px inside a
     321px box and clipped "Unpaid out" — the figure a booker most needs (walk
     2026-09-09, W-11). A whole row of its own is what lets the four figures
     wrap onto two lines instead. */
  .pb-totals { gap: 10px; flex: 1 1 100%; min-width: 0 }
}
/* ── Payments › By person (Chris, 2026-11-06; web/payments-people.js) ──────
   One row per co-pro partner or renter; a row opens under itself. Owed to us
   reads in the accent, what we owe in the bad colour, like the board's In / Out. */
.pp-in { color: var(--accent) }
.pp-out { color: var(--bad) }
.pp-list td { vertical-align: middle }
.pp-list .pp-chev { width: 28px; padding-right: 0 }
.pp-tog { background: none; border: 0; padding: 4px; color: var(--muted); cursor: pointer; min-width: 28px; min-height: 28px }
.pp-tog:hover { color: var(--text) }
.pp-row { cursor: pointer }
.pp-bal { font-weight: 700; white-space: nowrap }
tr.pp-on > td { border-bottom-color: transparent }
tr.pp-detail > td { padding-top: 0 }
.pp-box { border-left: 2px solid var(--accent-2); padding: 8px 0 10px 14px; display: flex; flex-direction: column; gap: 10px; min-width: 0 }
.pp-contact { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; font-size: 13px; color: var(--muted) }
.pp-contact b { color: var(--text) }
.pp-contact .pp-sel { user-select: all }
.pp-lines tfoot td { font-weight: 700; border-top: 1px solid var(--border) }
.pp-act { text-align: right; white-space: nowrap }
.pp-fold > summary { font-size: 12px; color: var(--muted); cursor: pointer; padding: 2px 0 }
.pp-fold[open] > summary { margin-bottom: 6px }
.pp-same { gap: 8px; flex-wrap: wrap }
tr.pp-sq td { color: var(--muted) }
@media (max-width: 560px) {
  .pp-list .pp-sm-hide { display: none }
  .pp-box { padding-left: 8px }
}

/* ── the Missing screen ───────────────────────────────────────────────────
   One wide table of everything a played show still owes, modelled on the
   Payments board above it. Its Status column borrows the show page's pill
   recipe (the .pill.status block further up this file) rather than inventing a
   second one, so a state reads the same wherever it is printed.

   Five states, and the colours are the app's own tokens (Chris, 2026-09-08 —
   every colour a token): muted for open, because being owed something is the
   ordinary case and not a problem; Signal Cyan once somebody has asked; red
   when the date they asked for has gone by, which is the one row on this screen
   that needs acting on today; green when it came. A Won't come is dimmed on
   purpose — it is a decision, still on the record, no longer work. */
#ms-rows .pill.status.s-open { --lc: var(--muted-2) }
#ms-rows .pill.status.s-requested { --lc: var(--accent-2) }
#ms-rows .pill.status.s-overdue { --lc: var(--bad) }
#ms-rows .pill.status.s-received { --lc: var(--accent) }
#ms-rows .pill.status.s-wontcome { --lc: var(--muted); opacity: .62 }
/* The artist or partner an item is about, beside the item's own name, is the
   house note inline (`small.muted-note`; it was a `.ms-sub` copy of the note
   until the style wave, 2026-09-23). */
/* Open, Request and Won't come (or Reopen) on one line. Won't come has no
   reason box since 2026-09-30, so nothing on the row is printed under the pill. */
#ms-rows td.ms-act { white-space: nowrap }
#ms-rows td.ms-played, #ms-rows td.ms-due { white-space: nowrap }
/* The bulk bar (2026-09-28), Mail's rules under this screen's names: the tick
   column is the box and nothing else; the bar sits between the panel title and
   the table, the count first, then Reopen selected and Won't come selected
   (one click each since 2026-09-30), then the refusal sentence on a line of its
   own, wrapping on a phone. `[hidden]` is restated because a flex rule outranks
   the attribute. */
#ms-rows .ms-pick { width: 1%; padding-right: 0 }
#scr-missing .ms-bulk { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; margin: 8px 0 }
#scr-missing .ms-bulk[hidden], #scr-missing .ms-bulk [hidden] { display: none }
#scr-missing .ms-selcount { font-size: 12px; font-weight: 600 }
#scr-missing .ms-bulk .err { flex-basis: 100% }
#scr-missing .ms-bulk .err:empty { display: none }
/* ── Wave C · the Contacts screen's Import CSV popup ──────────────────────
   The Actuals import paints a block UNDER the table it will replace, because
   the table is what the reader is deciding about. This one has no such table —
   it is about the whole book — and Chris asked (2026-09-05) for something that
   "goes away after importing", so it is a dialog over the page instead.

   Darker than the send modal's ground on purpose: that one floats over a
   document the reader is still checking, and this one over a list they are done
   with. z-index 60 clears everything the app paints, the send modal (20)
   included — the two can never be open at once, but a popup that could end up
   BEHIND anything is not a dialog. */
.imp-overlay {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: 24px 16px; background: rgba(0, 0, 0, .55);
}
/* Wide enough for the five-column picker and the preview's four columns, and
   scrolled INSIDE rather than growing past the window: the Import button is the
   last line of the popup and has to stay reachable on a laptop. */
.imp-popup { width: min(720px, 92vw); max-height: 88vh; overflow: auto; margin: 0; box-shadow: 0 18px 48px rgba(0, 0, 0, .55) }
.imp-popup .chk-h { margin-top: 0 }
.imp-popup .sm-bar { margin-top: 10px }
/* The two dialogs' close button sits at the far end of the title line (it was
   an inline margin on each; style wave, 2026-09-23). */
#imp-cancel, #doc-close { margin-left: auto }
/* The × sits on the title line and must not stretch to a full-width button. */
.imp-popup .panel-title .btn { flex: none; padding: 2px 9px; line-height: 1.2 }
/* ── Batch 7 · Bring in your calendar ─────────────────────────────────────
   The calendar import's popup carries SEVENTEEN column selects and a six-column
   dry run where the contact book's carries five and four, and neither reads at
   720px. Same rule, one step wider — and bounded at the SAME 92vw, so the
   phone gets the popup it always had. */
.imp-popup.imp-wide { width: min(980px, 92vw) }
/* The panel's first heading sits under a sub-line that already carries a top
   margin of its own, which is the same thing `.imp-popup .chk-h` above and
   `.cal-layers .muted-note` further up do to theirs. */
.impc-panel .chk-h { margin-top: 10px }
/* The source picker and the file row are `.att-row`s inside a panel, so the
   last of them would otherwise draw a rule with nothing under it. */
.impc-panel .att-row:last-child { border-bottom: 0 }

/* ── Wave C · the Contacts screen ─────────────────────────────────────────
   The book's two actions sit at the RIGHT end of the panel title, past a
   spacer: `.grow` is only a flex child inside `.att-row` and `.cal-row`, and a
   title bar is the third place this app needs one. */
.panel-title .grow { flex: 1; min-width: 0 }
/* The message beside Save is an .err, which is `display:none` when empty (so
   the bar does not carry a gap) and never a toast: a refused save is about the
   field the reader is looking at. */
#ct-form .err { margin: 0 }

/* ── the table tools: a sortable head and a search box ────────────────────
   web/table-tools.js paints these into Payments, the Sent log and Contacts.

   The head stays a head. `.th-sort` inherits every letter of the `.data-table
   th` look — 10px, tracked, uppercase, muted — so a sortable column reads as a
   column heading that happens to be clickable, not as a row of small buttons
   above the table. What changes when a column is the sorted one is the mark
   beside it and the colour of the words: .35 opacity says "you could sort by
   this", full accent says "you did". */
.th-sort {
  background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit;
  letter-spacing: inherit; text-transform: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
}
/* The mark is spaced by the button's own `gap`; `.ico` ships a 6px right margin
   for text buttons, which here would print as a hole after the arrow. */
.th-sort .ico { margin: 0 }
.th-sort .s { opacity: .35 }
.th-sort[aria-sort="ascending"] .s, .th-sort[aria-sort="descending"] .s { opacity: 1; color: var(--accent-2) }
.th-sort[aria-sort="ascending"] .s { transform: scaleY(-1) }
th:has(.th-sort[aria-sort="ascending"]), th:has(.th-sort[aria-sort="descending"]) { color: var(--text) }
/* The glass sits INSIDE the box rather than beside it: a table's tools belong
   on one line, and a separate icon would be one more thing between the reader
   and the field. pointer-events:none so clicking the mark still lands in the
   input. */
.tbl-search { position: relative; width: 260px; max-width: 100% }
.tbl-search input { width: 100%; padding-left: 30px }
/* type="search" (Chris, 2026-09-06: "all of the search boxes are incorrect format"): it was
   the one input type the field rule above did not name, so the three boxes
   fell back to the browser's white field. The rule now names it; this strips
   the browser's own search decoration so the box is the recipe and nothing else. */
input[type="search"] { -webkit-appearance: none; appearance: none }
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; display: none }
.tbl-search .ico { position: absolute; left: 10px; top: 9px; color: var(--muted); pointer-events: none }

/* ── Clover (stage 1) ──────────────────────────────────────────────────────── */
/* Tokens only, like every rule above: the neon skin re-declares the tokens and
   nothing here is keyed to a skin. The CHARTS on this screen are drawn on a
   canvas and read the same tokens through getComputedStyle (web/clover.js), so
   a skin switch repaints them too and no colour is spelled in either file. */
/* The control bar: the range segment, the two date boxes, the history select
   and — for the owner — Sync. It wraps rather than scrolls, because the date
   boxes are fields and a field that has scrolled half out of view is a field
   nobody finds. */
.cv-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px }
.cv-bar .grow { flex: 1; min-width: 0 }
/* From, To and History are FIELD labels, in the field label's own voice (11px,
   .04em, Slate Lavender — which `.main label` already gives them), set beside
   their boxes with no margin under them. They were a 10.5px tracked uppercase
   copy of the eyebrow (style review M2), and `.main label`'s 9px bottom margin
   stood them above the boxes' line (the Clover minor). Show P&L's range bar is
   this bar and reads the same rule. */
.cv-bar label { flex-direction: row; margin: 0 }
.cv-dates { display: flex; align-items: center; gap: 6px; flex-wrap: wrap }
/* FOUR tiles across, not three — DECLARED ONCE, HERE, for every screen that
   wears the class. `.tiles` is Today's three-column grid; five screens want the
   desktop's four instead: Clover (Total Collected · Net Sales · Tips · Tax
   Collected), Homebase, Payroll, the Bar Checklist (Opening · Closing · Issues
   Flagged · Missed) and, from stage 2, Contacts (In the book · W-9 on file ·
   Never sent · Last send).

   It used to be declared THREE times, each with a stacking rule of its own —
   two asking for one column under 899px and the third, LAST and therefore the
   winner, asking for two under 900px — so the comment explaining the middle one
   had stopped describing what the browser did. The behaviour below is the one
   that was already shipping: FOUR above 900px, TWO at 900px and under. Making
   it something else is a look call and Chris's; this collapse changes nothing a
   person sees (whole-branch review M-7, 2026-09-17).

   The stacking rule is repeated for this class rather than inherited from
   `.tiles`, because `.tiles.four` outranks the `.tiles` rule inside the media
   query and would otherwise keep four columns on a phone. */
.tiles.four { grid-template-columns: repeat(4, minmax(0, 1fr)) }
@media (max-width: 900px) { .tiles.four { grid-template-columns: repeat(2, minmax(0, 1fr)) } }
/* A DATE IS NOT A FIGURE. `.tile-value` is 34px and tabular, which is right for
   a count and wrong for "Sep 16, 2026" — at that size the date is the loudest
   thing on the screen and it wraps on a phone. Contacts' Last send tile is the
   one that prints a date (stage 2); this size is the panel title's, so the value
   still reads as the tile's answer rather than as its caption. */
.tile-value.tile-date { font-size: 18px; font-size: clamp(12px, 15cqi, 18px); font-weight: 700; letter-spacing: -.01em }
/* The rate row: two live figures on one line with the sparkline under them. */
.cv-rate { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px }
/* The two figures are the children of #cv-rate, not of the row, so the row's
   gap never reached them and they ran together — "…$0.00LAST HOUR…" (the
   owner's phone walk, 2026-09-23). They carry the same 24px gap between them,
   each stays one line, and on a phone the second wraps under the first. */
#cv-rate { display: flex; flex-wrap: wrap; gap: 4px 24px }
.cv-rate-cell { font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap }
.cv-rate-cell .tile-label { display: inline; margin-right: 6px }
.cv-spark { flex: 1; min-width: 180px }
.cv-spark canvas { display: block; width: 100% }
/* Two panels side by side: the two charts, then the two tables. */
.cv-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px }
.cv-two canvas { display: block; width: 100% }
/* The one coloured word in the status line under the page: what Clover last
   said when it refused. Refusal Red, the token every other refusal wears. */
.cv-err { color: var(--bad) }
/* `.tiles.four` stacks with its own declaration above, not here: one rule for
   every screen that wears the class. */
@media (max-width: 899px) {
  .cv-two { grid-template-columns: 1fr }
}
/* ── Homebase and Payroll (stage 1, venue ops) ─────────────────────────────
   Two screens with the same shape: a control bar in a panel head, four tiles,
   and one wide table. Tokens only, and nothing here is keyed to a skin. */
/* FOUR tiles rather than three — these two screens wear `.tiles four`, which is
   declared once, with its stacking rule, in the Clover block above. */
/* The control bar carries a range (Homebase's tab strip, Payroll's week
   stepper), a checkbox and two buttons, which is more than a panel head was
   built for: it wraps rather than pushing the last button off a narrow screen. */
#scr-homebase .panel-title, #scr-payroll .panel-title { flex-wrap: wrap; row-gap: 6px }
/* A checkbox that reads as one control with its words. `.sm-bar .tog-inline`
   is the same recipe on the show page; this is the venue-ops screens' own so
   that neither has to reach into the other's scope. It is written as
   `.main label.vo-tog` because `.main label` — the stacked Setup field label —
   outranks a single class: until the style wave (review item 5) it drew the box
   as an 87px bar ABOVE its words with 9px under it, the same collision `.tog`
   and `.chk` state their way out of. */
.main label.vo-tog { display: inline-flex; flex-direction: row; align-items: center; gap: 6px; margin: 0; font-size: 11px; color: var(--muted) }
.main label.vo-tog input { width: auto }
/* Today's two lanes, side by side on a desk and stacked on a phone. */
.hb-lanes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px }
@media (max-width: 899px) { .hb-lanes { grid-template-columns: 1fr } }
/* Over plan. The only coloured figure on the Homebase table, and it is the one
   the screen exists to catch. */
.hb-over { color: var(--bad) }
/* The Payroll week's own two marks: the tint on a tips lane that disagrees by
   more than a dollar, and the bold Week line under the seven days. */
.pr-gap { color: var(--bad) }
/* The week stepper: previous · This Week · next, three buttons in the house
   recipe 4px apart. The words in a panel head would take the eyebrow's capitals
   and tracking; the button says This Week the way the rest of the site does. */
.pr-step { display: inline-flex; gap: 4px }
.pr-step .btn { text-transform: none; letter-spacing: normal }
/* The month stepper — the Payments board and the Missing screen (Chris,
   2026-09-30: "Show the current month only with the ability to go back month
   by month"). Payroll's arrows, with the month's name between them in place of
   This Week. The name keeps one width so the → does not move as the months'
   names change length under a pointer clicking it. */
.mo-step { display: inline-flex; align-items: center; gap: 4px }
.mo-step b { min-width: 9.5em; text-align: center; font-weight: 650; white-space: nowrap }
.pr-week td { border-top: 1px solid var(--muted-2); font-weight: 700; color: var(--text) }
/* The owner's correction: a night, an amount, a note and a button on one line,
   wrapping on a phone rather than squeezing the note box to nothing. */
.pr-fix { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px }
/* No bottom margin: `.main label`'s 9px left the fields standing 9px above the
   Add button they share a line with (style review, Payroll minor). */
.pr-fix .sm-field { min-width: 140px; margin: 0 }
.pr-fix .pr-fix-note { flex: 1; min-width: 200px }
.pr-fix span { font-size: 11px; color: var(--muted) }
/* ── the Bar Checklist ────────────────────────────────────────────────────
   Three pages behind one rail item (web/barchecklist.js). Every colour here is
   a token, and the pill states borrow the show page's recipe further up this
   file rather than inventing a second one, so "signed off" reads like every
   other good state on the site and a miss reads like every other bad one. */

/* FOUR tiles, not three: Opening, Closing, Issues Flagged and Missed — Last 7
   Days are the desktop's own four, and folding one of them onto a second row
   would hide the number this screen exists to show. This screen wears
   `.tiles four`, which is declared once, with its stacking rule, in the Clover
   block above — and it was THIS block's `@media (max-width: 900px)` two-column
   rule that won on every screen using the class, so that is the rule the one
   declaration carries. */

/* The strip of page controls under the sub-line. `.grow` pushes the Manager
   view pill to the far end, the way a panel title's tag sits at the end of its
   row. */
.bc-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px }
/* What this screen is and is not, said once at the top. Quiet — it is a
   standing note, not a warning — and it carries the 4 AM rule, which is the one
   fact a manager needs to read a 2 AM check-off correctly. */
.bc-banner { font-size: 12px; color: var(--muted); background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--accent-2); border-radius: var(--ctl-r); padding: 8px 12px; margin-bottom: 12px }
/* The floor's own instructions, quoted on the flag panel. Italic and muted
   because they are somebody else's words, not this app's. */
.bc-quote { font-size: 11px; color: var(--muted); font-style: italic; margin-bottom: 6px }

/* One area of a phase, and one task inside it. The tick column is a fixed width
   so every title starts on the same line whether or not it is ticked. */
.bc-area { margin-top: 8px }
.bc-area-name { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted-2); font-weight: 700; margin-bottom: 2px }
.bc-task { display: flex; align-items: flex-start; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--line-soft) }
.bc-task:last-child { border-bottom: 0 }
.bc-tick { flex: none; width: 14px; color: var(--accent); font-weight: 700; line-height: 1.5 }
.bc-task.done .bc-what b { color: var(--muted) }
.bc-what { flex: 1; min-width: 0 }
.bc-what b { display: block; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.bc-what small { display: block; color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
/* A ⚑ the shaper attached to a task by its title. Amber, the app's own colour
   for something that wants attention without being a failure. */
.bc-flagged { color: var(--warn) }
.bc-chip { flex: none; align-self: center }

/* An open flag on the board's panel: the same two-line shape as a task, with
   its Resolve button at the far end. */
.bc-flag { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--line-soft) }
.bc-flag:last-child { border-bottom: 0 }

/* The Result column of the log. The misses are listed UNDER the pill rather
   than beside it: each one carries either a ruling chip or the two buttons that
   make one, and a row of those beside a pill would not survive a narrow
   window. */
.bc-misses { margin-top: 4px }
.bc-miss { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; padding: 2px 0 }
/* The opened-out night. It is a row of the same table, so it gets the panel's
   own padding back to sit inside the row rather than on top of it. */
.bc-detail > td { background: var(--panel-2); padding: 10px 12px }
/* The manager's note under a flag's status, and the name and date of whoever
   resolved it. One line each, muted, like the Missing screen's reason. */
.bc-why { display: block; color: var(--muted); font-size: 11px }
/* The controls column of the two tables: the boxes and buttons stay on one
   line and the box takes what is left. */
.bc-act { white-space: nowrap }
.bc-act input[type="text"], .bc-act input:not([type]) { width: 180px; max-width: 40vw }

/* The five states this screen prints, all on the shared pill recipe. Green for
   a night that was signed off and for a ruling that excused a miss; red for a
   night with misses in it and for a ruling that let one count; muted for the
   ordinary open case, which is not a problem. */
#bc-log .pill.status.s-signed { --lc: var(--accent) }
#bc-log .pill.status.s-missed { --lc: var(--bad) }
#bc-log .pill.status.s-open { --lc: var(--muted-2) }
#bc-log .pill.status.s-excused { --lc: var(--accent) }
#bc-log .pill.status.s-counts { --lc: var(--bad) }
#bc-flags .pill.status.s-open { --lc: var(--warn) }
#bc-flags .pill.status.s-signed { --lc: var(--accent) }

/* ── Today's bar group ────────────────────────────────────────────────────
   A FOURTH column on the Today row, painted only when the server sent the group
   (server/routes/barchecklist.js barGroup). The row has ONE TRACK PER COLUMN
   DRAWN — `data-cols`, written by web/today.js — so a site without the Bar
   Checklist keeps its three, and a role that does not hold every tab behind the
   show lists (Chris, 2026-09-24 for Missing, 2026-09-25 for Money and Waiting)
   closes up rather than leaving a hole where a column would have sat. Three is
   the base rule above. */
.today-cols[data-cols="1"] { grid-template-columns: minmax(240px, 1fr) }
.today-cols[data-cols="2"] { grid-template-columns: repeat(2, minmax(240px, 1fr)) }
.today-cols[data-cols="4"] { grid-template-columns: repeat(4, minmax(240px, 1fr)) }
/* The bar rows are not clearable — a miss goes away by being ruled on and a
   flag by being resolved — so they carry no button cluster and the whole row is
   the link. Same 51px as every other Today row, which is what the column's
   five-row height is measured from. */
.today-bar-item { display: flex; align-items: center; gap: 10px; padding: 8px 0; min-height: 51px; box-sizing: border-box; border-bottom: 1px solid var(--line-soft); color: var(--text); text-decoration: none }
.today-bar-item:last-child { border-bottom: 0 }
.today-bar-item:hover { color: var(--accent-2) }
.today-bar-item .who { flex: 1; min-width: 0 }
.today-bar-item b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.today-bar-item small { display: block; color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.today-bar-item .today-amt { flex: none; font-size: 12px; font-weight: 600; color: var(--muted) }
/* ── Ordering / Inventory (one page, 2026-10-15) ──────────────────────────────
   No colour of its own: every rule reads tokens every skin already redefines,
   so there is no [data-skin="neon"] counterpart to write. The page is built of
   house parts — the page name and its `.sub`, Payroll's `.pr-step`, the
   `.tiles.four`, panels, `.data-table` with `th.num`/`td.num` — and these are
   only what the one page adds: a supplier section's fold, its links row, the
   Order box, the low Monday count, the edit and add forms, the Total row. */
#scr-inventory .tag.go { color: var(--accent) }
#scr-inventory .tag.warn { color: var(--warn) }
/* "Monday count — Not done" in amber: a fact still owed, not a refusal. */
#scr-inventory .tile.warn .tile-value { color: var(--warn) }
/* A supplier's name is the fold: the Unsorted box's disclosure (web/pe.js) — a
   `.lnk` in the panel title's own type with the drawn chevron turned a quarter
   while shut. A shut section is its one title line. */
#scr-inventory .ord-sec > .panel-title { flex-wrap: wrap; row-gap: 4px }
#scr-inventory .ord-tog { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; letter-spacing: inherit; text-transform: inherit; font-weight: inherit; color: var(--text) }
#scr-inventory .ord-tog:hover { color: var(--accent-2) }
#scr-inventory .ord-tog .ico { transition: transform .15s; transform: rotate(-90deg) }
#scr-inventory .ord-tog[aria-expanded="true"] .ico { transform: none }
@media (prefers-reduced-motion: reduce) { #scr-inventory .ord-tog .ico { transition: none } }
#scr-inventory .ord-body[hidden] { display: none }
#scr-inventory .ord-sec .panel-title:has(+ .ord-body[hidden]) { margin-bottom: 0 }
/* The order website / Email the order / Add … row over the table. */
#scr-inventory .ord-links { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px }
#scr-inventory .ord-link-box { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; flex: 1 1 280px; min-width: 0 }
#scr-inventory .ord-link-box input { flex: 1 1 200px; min-width: 0 }
#scr-inventory a.btn { text-decoration: none }
/* The Order box sits inside its cell without changing the row's height. */
#scr-inventory .ord-qty { width: 72px; padding: 2px 6px; text-align: right }
/* On a phone the table scrolls sideways in its `.tbl-wrap`; the name keeps a
   readable width and a figure with its unit ("4 cs") stays on one line. */
#scr-inventory .ord-tbl td:first-child { min-width: 150px }
#scr-inventory .ord-tbl td.num { white-space: nowrap }
/* A row whose item is on no count reads quieter; a Monday count under par is amber. */
#scr-inventory tr.ord-nc td { color: var(--muted) }
#scr-inventory td.ord-low { color: var(--warn); font-weight: 700 }
/* A section's Total row (the Show page's table-totals look). */
#scr-inventory tfoot td { border-top: 1px solid var(--border); border-bottom: 0; font-weight: 700; color: var(--text) }
#scr-inventory .ord-ordered-words { font-size: 13px; color: var(--accent) }
/* A box's "✓ Par saved" once its save lands (2026-11-02): the saved line's
   accent green, small. In a row it is its own line under the item's name — the
   name cell's 150px floor is wider than the longest note, so no column moves;
   in a supplier's head it sits beside the day picker. */
#scr-inventory .ord-saved { font-size: 11px; font-weight: 600; color: var(--accent); white-space: nowrap; text-transform: none; letter-spacing: normal }
#scr-inventory .ord-tbl .ord-saved { display: block; line-height: 13px }
#scr-inventory .ord-copy-box { width: 100%; min-height: 64px; margin-top: 8px; font: inherit; font-size: 12.5px }
/* The edit row and the Add item form: the house field labels in a grid that
   folds to two across on a phone. */
#scr-inventory tr.ord-edit:hover { background: none }
#scr-inventory .ord-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px 10px; align-items: end }
#scr-inventory .ord-form label { margin-bottom: 0 }
#scr-inventory .ord-fixed { display: flex; flex-direction: column; gap: 4px; font-size: 13px; min-width: 0 }
#scr-inventory .ord-add { border-top: 1px solid var(--line-soft); margin-top: 8px; padding-top: 10px }
@media (max-width: 899px) {
  #scr-inventory .ord-form { grid-template-columns: repeat(2, minmax(0, 1fr)) }
}
/* Round 2 (2026-10-16). Download order / Send current order sit in a `.cv-bar`
   of their own under the tiles. In a supplier's head, the Order day / Arrives
   pickers and the search box are FIELDS in a title row that is set in eyebrow
   capitals, so each takes the field's own case and spacing back; the head
   wraps (above) rather than squeezing them on a phone. */
#scr-inventory .ord-docbar[hidden] { display: none }
#scr-inventory .ord-days { display: inline-flex; flex-wrap: wrap; gap: 4px 10px; align-items: center }
#scr-inventory .ord-day { display: inline-flex; align-items: center; gap: 6px }
#scr-inventory .ord-daysel { width: auto; padding: 2px 26px 2px 8px; font-size: 12px; text-transform: none; letter-spacing: normal }
#scr-inventory .ord-find { width: 200px; text-transform: none; letter-spacing: normal; font-weight: 400 }
#scr-inventory .ord-find input { padding-top: 4px; padding-bottom: 4px; font-size: 12.5px }
#scr-inventory .ord-find .ico { top: 7px }
/* The boxes in a row: Pack and Count area type or pick, Par and Case $ are
   figures right-aligned under their headings. They sit inside the row without
   changing its height, as the Order box does. */
#scr-inventory .ord-in { padding: 2px 6px }
#scr-inventory .ord-in-text { width: 130px }
#scr-inventory .ord-in-num { width: 78px; text-align: right }
/* The Pack / Count area choices: the house .sug list (the send box's To list),
   placed under the box with the focus. Fixed, because the table scrolls
   sideways inside its own box and would clip it (Chris, 2026-10-16). */
#scr-inventory .ord-sug { position: fixed; z-index: 60; margin: 0; min-width: 160px; max-height: 220px; box-shadow: 0 10px 30px rgba(0,0,0,.45) }
#scr-inventory .ord-sug li.cur { background: var(--panel) }
/* A sortable heading over a right-aligned figure keeps to the right. */
#scr-inventory .ord-tbl th.num .th-sort { justify-content: flex-end }
@media (max-width: 899px) {
  #scr-inventory .ord-find { width: 100%; flex: 1 1 100% }
  /* On a phone the two paper buttons share the row, equal, rather than one
     pushed right and the other wrapped under it. */
  #scr-inventory .ord-docbar .grow { display: none }
  #scr-inventory .ord-docbar .btn { flex: 1 1 140px; justify-content: center }
}
/* ── Bar Station ──────────────────────────────────────────────────────────────
   The phone on the bar top (web/bar.js). PHONE FIRST and then let a desktop
   have the extra width, which is the opposite way round from every other screen
   in this file — this is the one screen whose real device is a phone, held in
   one hand while the other hand is holding a keg.

   TOKENS ONLY, and no colour of its own: the four keg classes read four tokens
   this file already carries, which is why there is no [data-skin="neon"]
   counterpart below. A skin switch repaints this screen with everything else.

   Every control here is at least 44px tall. That is not a preference — it is
   the size of a thumb, and the taps this screen takes are made standing up.

   ON THE HOUSE SCALE (style wave, 2026-09-23). The screen's buttons are its own
   thumb-sized family, but their corners and type are the house's: the control
   radius on every button, the pill's 20px on a chip, the panel's 12px on a sheet;
   a button's words at the control size (`--ctl-fs`, 16px on a phone), a heading
   at the 14px Title, the tip figure at the 23px Headline. The review found 7px,
   12px, 16px and 999px corners and 15–30px type that no other screen used, and a
   red-FILLED armed Remove where danger everywhere else is red words and a red
   border. */

/* THE BAR PHONE HAS NO RAIL (web/app.js paintRail): an account with nothing but
   the bar and Settings gets the whole width, and the two buttons that would have
   been a rail are a page it can already reach. */
body.bar-only .rail { display: none }
body.bar-only .main { padding: 0 }

.bar { max-width: 760px; margin: 0 auto; padding: 14px 14px 90px }
.bar-top { padding-top: 6px }
/* The date is the page name's sub-line (`.sub`, straight under it), in this
   screen's own slightly larger size. */
.bar-date { text-align: center; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px }

/* The four tabs. A grid rather than a scroller: four is the whole set, and a
   strip that scrolls hides the fourth one on a narrow phone. */
.bar-tabs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-bottom: 12px }
.bar-tab {
  display: flex; flex-direction: column; gap: 2px; min-height: 52px; padding: 8px 6px;
  background: var(--panel); color: var(--muted); border: 1px solid var(--border); border-radius: var(--ctl-r);
  font-size: 12px; font-weight: 700; cursor: pointer; text-align: center; line-height: 1.15;
}
.bar-tab .sub { font-size: 10px; font-weight: 500; letter-spacing: .04em; text-transform: none; color: var(--muted-2) }
.bar-tab.on { color: var(--text); background: var(--panel-2); border-color: var(--accent-2) }
/* The lit tab is Raised Navy, where Dim Slate measures 4.17:1 (audit I3, the
   Measured Muted Rule): its small line steps up to Slate Lavender. */
.bar-tab.on .sub { color: var(--muted) }

/* Signing off as — on every tab, because the name IS the session. */
.bar-who { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 10px; margin-bottom: 12px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--ctl-r) }
.bar-who .lbl { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted) }
.bar-who .hint { font-size: 11px; color: var(--warn) }
/* SCOPED TO `.bar-who`, and that is not tidiness (2026-09-16, the merged
   Playwright run). `.who` is not this screen's word alone: `web/today.js` has
   used it since Wave C for the name half of a Today row, and the bar column
   Lane B added uses it too. Unscoped, these two rules gave every row on the
   TODAY screen a capsule border, a Raised Navy fill, 14px 600 text and a 40px
   floor — which took a row from the 51px the five-row list height is measured
   from to 67px, and the walk that pins that height is what caught it. The chips
   only ever live inside `.bar-who` (web/bar.js `whoHtml`), so the scope costs
   nothing and the collision cannot come back. */
.bar-who .who { min-height: 40px; padding: 8px 14px; border-radius: 20px; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font-size: 14px; font-weight: 600; cursor: pointer }
.bar-who .who.on { background: var(--accent); color: var(--on-accent); border-color: var(--accent) }

/* ── keg board ── */
.keg-sw { display: flex; gap: 8px; margin-bottom: 10px }
.barbtn { flex: 1; min-height: 46px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel); color: var(--muted); font-size: 14px; font-weight: 700; cursor: pointer }
.barbtn span { display: block; font-size: 10px; font-weight: 500; color: var(--muted-2) }
.barbtn.on { background: var(--panel-2); color: var(--text); border-color: var(--accent-2) }
.keg-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; font-size: 11px; color: var(--muted) }
.keg-legend .lg { display: inline-flex; align-items: center; gap: 5px }
.keg-legend .sw { width: 10px; height: 10px; border-radius: 4px; display: inline-block }
.keg-legend .sw.house, .keg .chip.house { background: var(--accent-2) }
.keg-legend .sw.guest, .keg .chip.guest { background: var(--accent) }
.keg-legend .sw.wine, .keg .chip.wine { background: var(--bad) }
.keg-legend .sw.na, .keg .chip.na { background: var(--muted) }
/* The house search box (`.tbl-search`, the drawn glass inside it) at the full
   width of the column. It carried a typed magnifier emoji until the style wave. */
.bar-find { display: block; width: 100%; margin-bottom: 12px }
.bar-find input { width: 100% }
.keg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px }
.keg { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 10px }
.keg.empty { border-style: dashed }
.keg-top { display: flex; align-items: center; gap: 6px; margin-bottom: 6px }
.keg .tapno { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted) }
.keg .tapno b { color: var(--text); font-size: 13px }
.keg .chip { margin-left: 4px; padding: 1px 7px; border-radius: 20px; font-size: 10px; font-weight: 700; color: var(--on-accent) }
.keg .sz { margin-left: auto; font-size: 10px; color: var(--muted-2) }
.keg .beer { font-size: 14px; font-weight: 700; line-height: 1.2 }
.keg .prod { font-size: 11px; color: var(--muted); margin-bottom: 6px; min-height: 14px }
.keg .meter { height: 7px; border-radius: 4px; background: var(--panel-2); overflow: hidden; margin: 4px 0 }
.keg .meter i { display: block; height: 100%; background: var(--accent) }
.keg .meter.low i { background: var(--warn) }
.keg .meter.out i { background: var(--bad) }
.keg .meta { font-size: 11px; color: var(--muted) }
.keg .meta.dim { color: var(--muted-2) }
.kegbtn { width: 100%; min-height: 44px; margin-top: 8px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font-size: 13px; font-weight: 700; cursor: pointer }

/* ── the heads the checklist, tips and count share ── */
.cl-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; margin-bottom: 12px }
.cl-head h2 { font-size: 14px; font-weight: 650; margin: 0 }
.cl-head .sub { margin: 2px 0 0; text-transform: none; letter-spacing: 0; font-size: 12px }
.cl-head .cl-on { font-size: 11px; color: var(--muted) }
.cl-head .cl-on b { color: var(--text) }
.cl-prog { margin-left: auto; text-align: right }
.cl-prog b { display: block; font-size: 14px; color: var(--accent-2) }
.cl-prog span { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted) }
.cl-phase { margin: 16px 0 6px; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700; color: var(--accent-2) }
.cl-area { display: flex; align-items: baseline; gap: 8px; margin: 12px 0 6px }
.cl-area h3 { margin: 0; font-size: 13px; font-weight: 700 }
.cl-area .note { font-size: 11px; color: var(--muted) }
.cl-note { margin-top: 18px; font-size: 11px; line-height: 1.5; color: var(--muted-2) }

/* ── the sign-off rows ── */
.cl-rows { display: flex; flex-direction: column; gap: 6px }
.cl-row { display: flex; align-items: center; gap: 10px; min-height: 52px; padding: 8px 10px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--ctl-r); cursor: pointer }
.cl-row .box { flex: none; width: 26px; height: 26px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel-2); color: transparent; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700 }
.cl-row.done .box { background: var(--accent); border-color: var(--accent); color: var(--on-accent) }
.cl-row .tc { flex: 1; min-width: 0; display: flex; flex-direction: column }
.cl-row .t { font-size: 13px; line-height: 1.3 }
.cl-row.done .t { color: var(--muted) }
/* The flag line is drawn on every row and shown only on a flagged one, so a row
   does not change height the moment somebody flags it. */
.cl-row .issue { display: none; font-size: 11px; color: var(--warn) }
.cl-row.flagged .issue { display: block }
.cl-row .flagbtn { flex: none; width: 44px; height: 44px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel-2); color: var(--warn); font-size: 14px; cursor: pointer }
.cl-row .when { flex: none; font-size: 11px; color: var(--muted); text-align: right; min-width: 74px }

/* ── tips ── */
.tip-big { display: flex; align-items: center; gap: 8px; padding: 14px; background: var(--panel); border: 1px solid var(--border); border-radius: 12px }
.tip-big .cur { font-size: 23px; font-weight: 700; color: var(--muted) }
.tip-big input { flex: 1; font-size: 23px; font-weight: 700; padding: 6px 8px }
.bar-send { width: 100%; min-height: 52px; margin-top: 10px; border-radius: var(--ctl-r); border: 0; background: var(--accent); color: var(--on-accent); font-size: var(--ctl-fs); font-weight: 700; cursor: pointer }
.tip-hist { display: flex; flex-direction: column; gap: 4px }
.tiph { display: flex; align-items: center; gap: 10px; padding: 9px 10px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--ctl-r); font-size: 12px }
.tiph .ppl { font-weight: 700 }
.tiph .st { margin-left: auto; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--accent) }

/* ── the count ── */
.cnt-rows { display: flex; flex-direction: column; gap: 6px }
.cnt-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 9px 10px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--ctl-r) }
.cnt-row.counted { border-color: var(--accent) }
.cnt-row .nm { flex: 1 1 140px; min-width: 0; display: flex; flex-direction: column }
.cnt-row .nm b { font-size: 13px; font-weight: 650 }
.cnt-row .pk { font-size: 11px; color: var(--muted) }
.cnt-row .entry { display: flex; align-items: flex-end; gap: 6px }
.cnt-row .entry label { display: flex; flex-direction: column; gap: 2px; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted) }
.cnt-row .entry input { width: 68px; min-height: 44px; text-align: center }
/* Expected (2026-10-15; as boxes since 2026-11-08: "make the expected amount
   match the case and loose box. but make it uneditable"): a second grid row of
   the same boxes, locked and greyed, under Case / Loose; "Expected" under Save.
   Shown on every row with a figure, counted or not (ruled). */
.cnt-row .entry.dual { display: grid; grid-template-columns: repeat(2, 68px) auto auto; align-items: end }
.cnt-row .entry.single { display: grid; grid-template-columns: 68px auto auto; align-items: end }
.cnt-row .entry input.exp-box { min-height: 34px; background: transparent; border-style: dashed; color: var(--muted); opacity: 1; cursor: default }
.cnt-row .exp-lab { grid-column: span 2; align-self: center; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted) }
.savebtn { min-height: 44px; padding: 0 14px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font-size: 13px; font-weight: 700; cursor: pointer }
.savebtn.saved { background: var(--accent); color: var(--on-accent); border-color: var(--accent) }
.rmbtn { width: 44px; min-height: 44px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel); color: var(--muted); font-size: 14px; cursor: pointer }
/* Armed ("Sure?"): the house danger look — red words and a red border on the
   button's own fill — and not a red fill. */
.rmbtn[data-armed="1"] { width: auto; padding: 0 12px; color: var(--bad); border-color: var(--bad); font-weight: 700 }
/* The drawn close mark alone in the square: no word after it to space from. */
.rmbtn > .ico { margin: 0 }
.additem { width: 100%; min-height: 46px; margin-top: 8px; border-radius: var(--ctl-r); border: 1px dashed var(--border); background: transparent; color: var(--muted); font-size: 13px; cursor: pointer }

/* The order-request box, inside the count. */
.req { margin-top: 24px; padding: 14px; background: var(--panel); border: 1px solid var(--border); border-radius: 12px }
.req h3 { margin: 0; font-size: 14px }
.req .sub { margin: 4px 0 10px; text-transform: none; letter-spacing: 0; font-size: var(--note-fs); line-height: 1.5 }
.req .frm { display: flex; flex-direction: column; gap: 8px }
.req .frm input { width: 100% }
.req .hist { margin-top: 12px; display: flex; flex-direction: column; gap: 4px }
.reqh { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 7px 0; border-top: 1px solid var(--line-soft) }
.reqh .it { flex: 1; min-width: 0 }
.reqh .by { color: var(--muted); font-size: 11px }
.reqh .d { color: var(--muted-2); font-size: 11px }

/* The Send bar, pinned to the foot of the count tab and of nothing else. */
.bar-bot { position: sticky; bottom: 0; display: flex; align-items: center; gap: 10px; margin: 18px -14px -90px; padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); background: var(--panel-2); border-top: 1px solid var(--border) }
.bar-bot .st { font-size: 11px; color: var(--muted) }
/* "Counted on" (2026-11-08): a day box beside Send; beats `.main label`. The bar
   wraps so a phone keeps Send whole. */
.bar-bot { flex-wrap: wrap }
.bar-bot label.bar-counted { flex-direction: row; align-items: center; gap: 6px; margin: 0; font-size: 12px; letter-spacing: 0; color: var(--muted) }
.bar-bot label.bar-counted input { width: auto; min-height: 44px }
.bar-bot .submit { margin-left: auto; min-height: 48px; padding: 0 18px; border-radius: var(--ctl-r); border: 0; background: var(--accent); color: var(--on-accent); font-size: 14px; font-weight: 700; cursor: pointer }

/* ── the sheets ── */
.bar-ovl { position: fixed; inset: 0; z-index: 60; background: rgba(0, 0, 0, .55); display: flex; align-items: flex-end; justify-content: center }
.bar-ovl[hidden] { display: none }
.bar-sheet { width: 100%; max-width: 520px; max-height: 88vh; overflow-y: auto; padding: 16px 16px calc(16px + env(safe-area-inset-bottom)); background: var(--panel); border: 1px solid var(--border); border-radius: 12px 12px 0 0 }
.bar-sheet:focus { outline: none }
.bar-sheet h3 { margin: 0 0 4px; font-size: 14px }
.bar-sheet p { margin: 0 0 12px; font-size: 12.5px; color: var(--muted); line-height: 1.5 }
.bar-sheet .dim { color: var(--muted-2) }
.bar-sheet .big { display: block; width: 100%; min-height: 54px; margin-bottom: 8px; border-radius: var(--ctl-r); border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font-size: var(--ctl-fs); font-weight: 650; cursor: pointer }
.bar-sheet .big.primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent) }
.bar-sheet .big.danger { color: var(--bad); border-color: var(--bad) }
.bar-sheet .big.ghost { background: transparent; color: var(--muted) }
.bar-sheet .bar-inp, .bar-sheet .bar-search { width: 100%; margin-bottom: 10px }
.bar-sheet .tbl-search { width: 100%; margin-bottom: 10px }
.bar-sheet .tbl-search .bar-search { margin-bottom: 0 }
.bar-sheet .picker { max-height: 46vh; overflow-y: auto; margin-bottom: 10px; border: 1px solid var(--border); border-radius: var(--ctl-r) }
.bar-sheet .pgrp { padding: 7px 10px; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); background: var(--panel-2) }
.bar-sheet .pick { display: block; width: 100%; min-height: 46px; padding: 0 10px; border: 0; border-bottom: 1px solid var(--line-soft); background: transparent; color: var(--text); font-size: 14px; text-align: left; cursor: pointer }
.bar-sheet .chiprow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 10px; font-size: 11px; color: var(--muted) }
.chipb { min-height: 40px; padding: 0 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font-size: 13px; cursor: pointer }
.chipb.on { background: var(--accent); color: var(--on-accent); border-color: var(--accent) }
.bar-sheet .ordiv { margin: 4px 0 8px; text-align: center; font-size: 11px; color: var(--muted-2) }
.bar-sheet .ok-line { font-size: 14px; color: var(--accent); margin-bottom: 8px }
.bar-sheet .foot { font-size: var(--note-fs); line-height: 1.5; color: var(--muted-2); margin-bottom: 14px }

/* A wide screen gets a centred sheet rather than a phone's bottom sheet. */
@media (min-width: 900px) {
  .bar-ovl { align-items: center }
  .bar-sheet { border-radius: 12px }
}
/* Settings › Integrations — the per-table row counts under Bar data, and from
   stage 2 under Show data as well (web/settings-showdata.js reuses this recipe
   rather than writing a second one; the `bd-` prefix says where it was first
   used, not who owns it).
   Fifteen small figures there and twenty-seven here, and they are a READING
   rather than a table: what the owner wants off them is "is anything obviously
   empty", so they wrap as chips on the panel's own ground instead of taking a
   grid's rules and lines. Every colour, radius and gap is a token; nothing here
   is this panel's own. */
.bd-grid { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 }
.bd-cell {
  display: inline-flex; align-items: baseline; gap: 6px; padding: 4px 8px;
  border: 1px solid var(--line-soft); border-radius: var(--ctl-r);
  background: var(--panel-2); color: var(--muted); font-size: 11px;
}
.bd-cell b { color: var(--text); font-size: 12px }
/* The Test answers print a location per line, and a sentence with a list under
   it must keep its newlines — the box is otherwise the house .err line. */
.panel .err[data-msg] { white-space: pre-wrap }

/* ── Stage 2 ───────────────────────────────────────────────────────────────
   The show page's Tasks panel (web/show-tasks.js). Two row tones and nothing
   else: a DONE task steps back so the open ones read first, and a task past its
   due date takes the app's own `--bad`. Both are scoped to `#show-tasks`,
   because `.st-done` and `.st-late` are ordinary-enough names that a bare rule
   would be waiting to repaint somebody else's table — the lesson `.who` taught
   in stage 1, kept. Every colour is a token; nothing here is this panel's own. */
#show-tasks .st-done td { color: var(--muted) }
/* The DUE cell, which is the thing that has gone wrong — not the whole row. A
   row painted red end to end says the task is bad; what is bad is the date. */
#show-tasks .st-late td:nth-child(5) { color: var(--bad) }
/* ── To-Do's ────────────────────────────────────────────────────────────────
   One row shape for every tab — a person's, the Inbox's and the Checklist's:
   a checkbox, then the words. It is a <label>, so the whole row is the
   checkbox's tap target — the crew reads this on a phone and a 44px target is
   the point of the padding.

   SCOPED TO THE SCREEN on purpose. `.td-row` is only ever painted inside
   `#scr-todos`, and stage 1's lesson (the bar's bare `.who` rule repainting
   every row on Today) is that a screen's block may not style a bare class.
   Every colour, radius and gap below is a token. */
/* THE LINE BETWEEN ROWS IS THE WRAPPER'S, not the tick row's (walk fixes round
   two, 2026-09-21). An open editor is a sibling of the tick row inside one
   `.td-item`, so a border on the row itself would draw a line THROUGH the thing
   it belongs to.

   AND IT IS `+`, NOT `:first-of-type` (round three): the list is broken into the
   desktop's five sections now, and a `.chk-h` heading is a div among the rows —
   so `:first-of-type` matched the first DIV in the panel, which is a heading, and
   stopped removing anything. A line BETWEEN two rows is what was wanted all
   along, and this way there is none under a heading either, where a line would
   have read as a second underline. */
#scr-todos .td-item + .td-item { border-top: 1px solid var(--line-soft) }
/* A <label> HAS TO ANSWER THE FIELD-LABEL RULE PROPERTY BY PROPERTY (walk fixes
   round three, 2026-09-21. Chris: "what is with the terrible formatting in the
   inbox and the actual to do items. change it to match the rest of mission
   control").

   `.main label` — the field label Setup and Settings write — is
   `flex-direction: column` at 11px Slate Lavender with .04em tracking and 9px
   under it, and it reached this row, because a <label> is what this row is. The
   rule below already outranked it on the properties it MENTIONED and on no
   others: a selector does not beat a declaration it never makes. So the tick,
   the words, the date box, the lock and ⋯ each took a line of their own, in
   field-label type — a row carrying all five measured 201px at 1180px wide.
   Every one of those six properties is
   now stated here. It is the same answer `.main label.tog` and `.main
   label.chk` already give, and the row stays a <label> because the whole of it
   being the checkbox's tap target is the point of it on a phone.

   THE SHAPE IS THE APP'S OWN LIST ROW — the Bar Checklist's `.bc-task` and
   Today's `.today-item`: a fixed tick, the words with an 11px note under the
   title, and the row's own controls at the far end sitting on the middle of the
   line. Measured: 44px with a title alone, 46px with a note under it. */
#scr-todos .td-row {
  display: flex; flex-direction: row; align-items: flex-start; gap: 10px;
  padding: 6px 2px; min-height: 44px; box-sizing: border-box; cursor: pointer;
  margin: 0; font-size: inherit; letter-spacing: normal; color: var(--text);
}
#scr-todos .td-row input[type="checkbox"] { width: 20px; height: 20px; margin-top: 1px; flex: none }
/* `.grow` is a spacer only inside `.att-row` and `.cal-row` (see the note on
   `.panel-title .grow`), so a row that wants one says so itself. Without this
   the words shrank to their own width and the date box sat against them
   mid-row instead of at the end of it. */
#scr-todos .td-row .grow { flex: 1; min-width: 0 }
#scr-todos .td-title { font-weight: 600 }
/* The note UNDER the title, not a paragraph after it. `.muted-note` carries 9px
   over it, which is the air under a field; inside a list row that gap makes one
   row read as two. The same zero `.cal-tools`, `.cal-layers` and `.sm-bar`
   already write, and what `.bc-what small` and `.today-item small` have by
   being their own elements. */
#scr-todos .td-row .muted-note { margin-top: 0 }
/* And the same, for the show name over its line on "Waiting on a dated step".
   Scoped to the note INSIDE a row's `.grow`, so the ＋Add panel's own inline
   message — a direct child of its `.att-row` — is untouched. */
#scr-todos .att-row .grow .muted-note { margin-top: 0 }
/* The editor that opens under a row. It takes the panel's second tone, the way
   the Checklist tab's own edit row does (`.st-edit > td` below), so the two
   places a task is edited look like one thing. */
#scr-todos .td-edit { padding: 2px 8px 6px; background: var(--panel-2) }
#scr-todos .td-edit .att-row:last-child { border-bottom: 0 }
#scr-todos .att-row { flex-wrap: wrap; row-gap: 6px }
/* ── PreKindle (stage 2, Lane K) ──────────────────────────────────────────────
   The candidate lines are a plain stack inside a table cell that spans the
   row. (The Pace sparkline and its `.pk-spark` rule went with the Pace
   column, 2026-09-29.) */
.pk-cand { display: flex; align-items: center; gap: 8px; padding: 4px 0 }
.pk-cand + .pk-cand { border-top: 1px solid var(--line-soft) }

/* ── Walk fixes 2026-09-21, Lane A ── */
/* The six-week table's layer switches (web/today.js). They are the Calendar's
   own `.cal-layers` row, so everything about how they LOOK is already decided
   further up this file; what changes inside a panel is only the spacing — the
   caption above them owns the gap over, and the table under them owns the gap
   below. */
#today-layers { margin: 8px 0 10px }
/* The drag that moves a show (web/calendar.js). The night under the pointer
   wears the same inset accent ring a picked night wears, because it means the
   same thing — this is the night the gesture is about — and drawn inside the
   cell so a 96px box does not grow and push the week about. `grab` on a chip a
   member may move; a chip they may not is `draggable="false"` and keeps the
   pointer a link has. */
.cal-day.drop { outline: 2px solid var(--accent-2); outline-offset: -2px; background: color-mix(in srgb, var(--accent-2) 10%, var(--panel)) }
.cal-ev[draggable="true"] { cursor: grab }
.cal-ev[draggable="true"]:active { cursor: grabbing }
/* The question the drop asks, over the grid. A panel like Add show's, and the
   same red line under it, so the two questions this screen asks look alike. */
.cal-move { margin-bottom: 10px }
.cal-move .cal-row { margin-bottom: 0 }
#cal-move-say { font-size: 13px; font-weight: 600 }
/* ── Walk fixes 2026-09-21, Lane B ────────────────────────────────────────────
   The To-Do screen's tab strip and its Checklist tab (Chris, 2026-09-21: "To do
   page needs to be formatted like MC desktop, with each individual person that
   has a login along the top"). Every rule is scoped to `#scr-todos` and every
   colour, radius and gap is a token — the rule the Bar Station chip taught
   (test/hygiene.test.js): a screen's block may not style a bare class. */
#scr-todos .td-tabs { row-gap: 6px }
#scr-todos .td-chk { border-top: 1px solid var(--line-soft) }
#scr-todos .td-chk:first-of-type { border-top: 0 }
#scr-todos .td-chk-head {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 44px; padding: 8px 2px; text-align: left;
  background: none; border: 0; color: var(--text); font: inherit; cursor: pointer;
}
#scr-todos .td-chk-head:hover { color: var(--accent-2) }
#scr-todos .td-chk-head:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 1px }
/* The same spacer the tick row needs, and for the same reason: `.grow` is only
   a flex child inside `.att-row` and `.cal-row`, so without `flex: 1` the show's
   name shrank to its own width and the progress bar and its count sat against
   the name in the middle of the row instead of at the end of it — beside the
   `.att-row` directly above, where `.grow` does push its controls to the edge. */
#scr-todos .td-chk-head .grow { flex: 1; min-width: 0 }
#scr-todos .td-chk-when { flex: none; width: 58px; color: var(--muted); font-size: 12px }
/* The bar says what the numbers beside it say, for an eye that is not reading
   them. It carries the count as an aria-label, so a screen reader is told the
   figure rather than the picture. */
#scr-todos .td-chk-bar {
  flex: none; width: 84px; height: 6px; border-radius: 3px;
  background: var(--panel-2); overflow: hidden;
}
#scr-todos .td-chk-bar i { display: block; height: 100%; background: var(--accent) }
#scr-todos .td-chk-body:empty { display: none }
/* Walk round 2 (2026-09-28): the head and the archive control share one line.
   The head keeps the width; the tick or Unarchive sits at the end of it. The
   question under the row is the house att-row, indented to the name. */
#scr-todos .td-chk-line { display: flex; align-items: center; gap: 10px }
#scr-todos .td-chk-line .td-chk-head { flex: 1; min-width: 0 }
#scr-todos .td-chk-arch { flex: none }
#scr-todos .td-arch-ask { padding: 0 2px 8px }
/* A phone is where this is read: the date column and the bar are the two things
   that can go without the row losing its meaning. */
@media (max-width: 560px) {
  #scr-todos .td-chk-when { width: 46px }
  #scr-todos .td-chk-bar { width: 48px }
}
/* The show-task editor, drawn by web/show-tasks.js. Since the walk fixes of
   2026-09-21 it is the TO-DO SCREEN'S alone — the show page's Tasks panel is
   tick-only (Chris: "the should be on the to-do screen and not in the individual
   show screen"). The rules stay UNSCOPED all the same, and for a reason that is
   not laziness: the Checklist tab draws that panel INSIDE `#scr-todos`, so a
   `#show-tasks` scope would leave the tab's own tables unstyled. Every class here
   is written by `web/show-tasks.js` alone since round three — the person tabs'
   titles are words now, not buttons — and every one carries an `st-` prefix,
   which is the rule the Bar Station chip taught: a block may not style a bare
   class like `.who`. */
.st-rename {
  padding: 0; background: none; border: 0; color: inherit; font: inherit;
  text-align: left; cursor: pointer; border-bottom: 1px dashed var(--line-soft);
}
.st-rename:hover { color: var(--accent-2) }
.st-rename:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
.st-acts { white-space: nowrap }
.st-acts > * + * { margin-left: 4px }
.st-edit > td, .st-add > td { background: var(--panel-2) }
.st-edit .att-row, .st-add .att-row { border-bottom: 0 }
/* ── Walk fixes 2026-09-21, Lane C ──────────────────────────────────────────
   A LIST THAT SCROLLS UNDER ITS OWN HEAD. `.tbl-wrap` is the shell's sideways
   scroller and stays exactly that; `.tbl-scroll` is its vertical twin, written
   as a second class beside it so a table can take one, the other or both. The
   two PreKindle lists take both: the eligibility window is sixty days back plus
   everything ahead, and two tables of that length one under the other is a page
   a reader scrolls past rather than reads.

   60vh, not a pixel count: the pane is a share of the window, so it is right on
   a laptop and on the phone. A sticky head needs an opaque background or the
   rows read through it, and it needs its rule drawn as an inset shadow because
   a sticky cell's own border scrolls away from it. Both are tokens. */
.tbl-scroll { max-height: 60vh; overflow-y: auto }
.tbl-scroll thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--panel); border-bottom: 0; box-shadow: inset 0 -1px 0 var(--border);
}
/* The line between the two halves of a PreKindle list — "Already played".
   SCOPED TO THE SCREEN, the lesson `.who` taught in stage 1: `.pk-divider` is
   only ever painted inside `#scr-prekindle`. It wears the head's own type, at
   the row heights of the rows around it, because that is what it is: a heading
   inside the list. It is not a row of data and never sorts — web/prekindle.js
   paints it in front of the first already-played row on screen. */
#scr-prekindle .pk-divider td {
  background: var(--panel-2); color: var(--muted); font-weight: 700;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase; padding: 5px 8px;
}
#scr-prekindle .data-table tbody tr.pk-divider:hover { background: none }

/* ── Bar Checklist, Edit Task List: the columns get the width their content
   needs (Chris, 2026-09-21: "expand the task box to be wider since it has the
   most information in it; area and sequence should be the same size as the
   phase box").

   Nothing sized these fields before this rule — every one of them, including a
   300-character Task and a two-digit Seq, took the browser's default input
   width of about twenty characters. So Task, which carries the sentence the
   whole screen is about, was the same size as the number beside it.

   ONE token for the three narrow fields, which is what "the same size as the
   phase box" means: the select sets the width and the other two read it, so
   they cannot drift apart. Everything is border-box (the `*` rule at the top of
   this file), so the select's extra chevron room does not make it wider than
   the inputs. Task then takes what is left: `width: 100%` of a cell that is
   asked for all the spare width, with a floor under it for a narrow window. */
#bc-edit { --bc-edit-narrow: 150px }
#bc-edit select[data-bc-f="phase"],
#bc-edit input[data-bc-f="area"],
#bc-edit input[data-bc-f="seq"] { width: var(--bc-edit-narrow); min-width: var(--bc-edit-narrow) }
/* The narrow columns ask for nothing, so the table hands the slack to the one
   cell that asks for all of it. */
#bc-edit th:nth-child(1), #bc-edit td:nth-child(1),
#bc-edit th:nth-child(2), #bc-edit td:nth-child(2),
#bc-edit th:nth-child(3), #bc-edit td:nth-child(3) { width: 1% }
#bc-edit th:nth-child(4), #bc-edit td:nth-child(4) { width: 100% }
#bc-edit input[data-bc-f="task"] { width: 100%; min-width: 320px }

/* ── Walk fixes 2026-09-21, Lane TODOS: the To-Do screen laid out as the desktop
   is (Chris: "Could you please make the to-dos page the same we have it in MC
   desktop? each person with a login should have a section").

   SAME STRUCTURE, THIS APP'S OWN SYSTEM. The desktop's own CSS file is not
   copied: its `.chk-h`, `.chk-badge` and `.todo-h-bad` are another design
   system's spellings of things this one already has. A section heading here IS
   `.chk-h`, which is what the show documents and the importers head their groups
   with; the red is `--bad`, through a modifier scoped to the screen. Everything
   below is scoped to `#scr-todos` and every colour, radius and gap is a token —
   the rule stage 1's bare `.who` taught. */
/* THE STRIP IS UPPERCASE, as the desktop's `.nav button` is (:310-314). The
   letters are a CSS transform, so the tab's TEXT is still the person's name
   exactly as the venue spells it — which is what a walk reads, and what a screen
   reader says. */
#scr-todos .td-tabs button { text-transform: uppercase; letter-spacing: .06em }
/* The strip is the house one in its wrapping form (`.settabs.wrap`, style wave
   2026-09-23); the venue's open and overdue counts ride its right-hand end, on
   the middle of the line, without the note's own top margin. */
#scr-todos .td-tabs #td-counts { margin: 0 0 0 auto; align-self: center }
/* The panel title over the strip: "<NAME> — <ROLE>", with Add task and the
   refresh button at the right-hand end. `.panel-title` is already the app's small uppercase head
   and already a flex row; what this adds is the gap under it, because it sits
   above the strip rather than inside a panel. */
#scr-todos .td-head { margin: 0 0 8px }
/* OVERDUE IS RED IN TWO PLACES, and it is the same red: the heading, and the
   title of every row under it. The desktop paints both (`.chk-h.todo-h-bad`
   :237, `.todo-overdue .chk-task-title` :236) and the pair is what makes a
   section scannable — a heading alone can be scrolled past. */
#scr-todos .chk-h.td-h-bad { color: var(--bad) }
#scr-todos .td-late .td-title { color: var(--bad); font-weight: 600 }
/* The month label inside Upcoming. `.muted-note`'s own type, with the gap over
   it that says it belongs to the rows under it and not to the one above. */
#scr-todos .td-month { margin: 10px 0 2px }
/* THE APP PILL carries `show_task.app` VERBATIM — "Gmail / Drive / Prism" is one
   cell in the crew sheet — so it is the house pill's `verbatim` case, with no
   capitals: a value read off a row is printed as it was written. (It was the
   private `.td-app` until the style wave, 2026-09-23.) */
/* The two controls on a to-do's own row, and ⋯ on every row. They keep their
   width when the title is long, and they sit on the MIDDLE of the row's line —
   `align-self: center`, which is what `.bc-chip` does inside a Bar Checklist
   task and what `.today-right` does inside a Today row, so a control at the end
   of a list row sits in the same place wherever the app draws one.

   THE DATE BOX TAKES THE COMPACT CONTROL PADDING, which is that token's own
   stated purpose — "inside tables and dense rows" — and the rule DESIGN.md
   states for every control in a row. At the normal padding the box stood 35px
   tall against a 34px line of words and set the row's height by itself. */
#scr-todos .td-row .td-date { flex: none; width: 140px; align-self: center; padding: var(--ctl-pad-c) }
#scr-todos .td-row .ib { flex: none; align-self: center }
/* TICKED, AND STILL ON THE SCREEN FOR AN INSTANT. The row is struck through
   under the finger and gone on the paint that follows the answer — neither screen
   has a Done section, so without the line a row would simply vanish and leave a
   person unsure which one they had ticked. */
#scr-todos .td-item.td-done .td-row { text-decoration: line-through; color: var(--muted) }
#scr-todos .td-item.td-done .td-title { color: var(--muted) }
/* A PHONE, WHICH IS WHERE THIS SCREEN IS READ. Measured at 375px: the tick, a
   140px date box, the lock and ⋯ leave about 60px for the words, and a to-do
   wrapped to four lines. So the date box drops to a line of its own and the
   words keep the first line with the tick, the lock and ⋯ — the shape the rest
   of the app's list rows hold at every width. Measured after, at 375px: 71px
   for a row with no date, 87px for one with a date and a line under its
   title. */
@media (max-width: 560px) {
  #scr-todos .td-row { flex-wrap: wrap; row-gap: 4px }
  /* The break itself is a zero-height item that fills its line, ordered between
     the words and the date box. A `max-width` on the box would NOT have done it:
     the clamp is applied before the line is broken, so the box simply stayed on
     the first line at its clamped width. */
  #scr-todos .td-row::after { content: ''; order: 1; flex-basis: 100%; height: 0 }
  /* 30px is the tick's 20px and the row's own 10px gap, so the box starts under
     the first letter of the title rather than under the tick. */
  #scr-todos .td-row .td-date { order: 2; width: 128px; margin-left: 30px }
}

/* ══ Private events, Lane 0 ═══════════════════════════════════════════════════
   The two screens' blocks, headers only: Lane C fills the Event Calendar's and
   Lane B the Client Book's, and Lane E's event page rides inside `#scr-pe`
   because it is painted into that section.

   EVERY RULE IN BOTH BLOCKS IS SCOPED TO ITS SCREEN, and that is not tidiness.
   Stage 1 shipped a bare `.who` rule from one screen's stylesheet and it
   repainted Today; a bare `.pe-row` would do it again. Tokens only — no literal
   colour, no literal font — so the venue's skin reaches these screens the way it
   reaches every other one. */
/* ── Event Calendar (#scr-pe) — Lane C's grid and pipeline, Lane E's event page ── */

/* `--pe`, the private-event hue, and `--market`, the market's violet
   (2026-10-03), are tokens in the two blocks at the top of this file (`--pe`
   moved there by the style wave, 2026-09-23). */

/* The two new layer switches and the two new chips, on the SHARED recipes — not
   scoped to a screen, because both grids draw them and `.cal-ev.l-shows` beside
   them is unscoped for exactly that reason. The class names are specific enough
   that nothing else in this app can match them. */
.lay.l-private { --lc: var(--pe) }
.cal-ev.l-private { --lc: var(--pe) }
.lay.l-market { --lc: var(--market) }
.cal-ev.l-market { --lc: var(--market) }
/* DEPOSIT UNPAID IS DASHED (css:5147-5150) — the same colour, a different edge,
   because the fact is "this date is not protected" and not "this is another kind
   of booking". A market is dashed by its own rule below. */
.cal-ev.l-private-pend { border-left-style: dashed }
/* A market takes its OWN HUE, violet (Chris, 2026-10-03), and keeps its own
   double left edge (css:4953-4956): the hue is what tells the two apart on a
   phone, where the chip is a dot with no edge, and the edge is a second cue on
   a desktop for anybody who cannot tell violet from pink. */
.cal-ev.l-market { border-left-style: double; border-left-width: 4px }
/* A MARKET IS DASHED UNTIL IT IS CONFIRMED (Chris, 2026-10-03): its own 4px
   edge, dashed instead of double, so "not confirmed" reads on a market without
   borrowing the private event's "deposit unpaid" class. */
.cal-ev.l-market.l-market-pend { border-left-style: dashed }
/* THE CLASH PILL — a market in the back room on a booked show's night (Chris,
   2026-10-03). It flags and it never blocks, so it is a warning and not an
   error: Rental Amber, dashed. It sits above the chips in the cell because it is
   about the NIGHT rather than about any one booking on it. The paid-deposit
   `.held` variant went with the HELD pill (same ruling). */
.cal-clash { margin-top: 3px; font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--warn); border: 1px dashed var(--warn); border-radius: 4px; padding: 0 3px; line-height: 1.5;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis }

/* ── and everything below is scoped to #scr-pe ─────────────────────────────── */
/* The banner is a NOTE and not a warning: it explains the screen, so it wears the
   panel and the accent's own edge rather than the error line's red. */
#scr-pe .pe-banner { font-size: 12px; color: var(--muted); background: var(--panel); border: 1px solid var(--line-soft);
  border-left: 3px solid var(--accent-2); border-radius: var(--ctl-r); padding: 8px 12px; margin: 0 0 12px; line-height: 1.5 }
/* An OPEN night: the green tint AND the word, because a state told only in colour
   cannot be read by everyone looking at it. */
#scr-pe .cal-day.pe-free { background: color-mix(in srgb, var(--accent) 9%, var(--panel)); border-color: color-mix(in srgb, var(--accent) 35%, var(--line-soft)) }
#scr-pe .pe-open-tag { font-size: 10px; font-weight: 800; letter-spacing: .14em; color: var(--accent); margin-top: 2px }
/* The day drawer. Its own accent edge, so it reads as an answer to the click that
   opened it rather than as another panel that was always there. */
#scr-pe .pe-drawer { border-left: 3px solid var(--accent-2); margin-top: 12px }
#scr-pe .pe-day-list { display: flex; flex-direction: column; gap: 6px; margin-top: 6px }
#scr-pe .pe-day-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12.5px;
  padding: 5px 0; border-bottom: 1px solid var(--line-soft) }
#scr-pe .pe-day-row:last-child { border-bottom: 0 }
#scr-pe .pe-day-row .grow { flex: 1; min-width: 120px }
/* PE · DEPOSIT UNPAID wears the chip's own dashed edge, so the pill in the drawer
   and the chip on the grid say the same thing the same way. It is the house
   status pill (`.pill.status`) in the private-event hue: this line sets the
   colour and the edge's style and draws nothing else (style wave, 2026-09-23). */
#scr-pe .pill.status.pe-pend { --lc: var(--pe); border-style: dashed }
/* A market's drawer pill and the Event page's kind pill wear the market's
   violet; a private event's kind pill wears --pe (2026-10-03). */
#scr-pe .pill.status.pe-pend.k-market, #scr-pe .pill.status.pev-kind.k-market { --lc: var(--market) }
#scr-pe .pill.status.pev-kind.k-private { --lc: var(--pe) }
#scr-pe .pe-del-box, #scr-pe .pe-lost-box { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px }
#scr-pe .pe-del-box input, #scr-pe .pe-lost-box input { flex: 1; min-width: 180px }
#scr-pe .pe-new { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--line-soft) }
/* A labelled box: the house field label (`.main label` — 11px Slate Lavender,
   sentence case) stacked over its box, on every box of the create form (style
   wave, 2026-09-23; audit A-I1: the names had been placeholders, which go the
   moment somebody types). It was a private 10px capitals copy of the eyebrow on
   three of them, and "What is the market? (shown to the public)" in capitals
   would break the Sentence Rule. Inline here, because the boxes share a row. */
#scr-pe .pe-fld { display: inline-flex; flex-direction: column; gap: 3px }
#scr-pe .pe-fld input { min-width: 120px }
/* The pipeline. A panel per stage, stacked, each carrying its own cards — five
   columns side by side would give every card a 19%-wide box on a laptop, and a
   card is a paragraph. */
#scr-pe .pe-panel { margin-bottom: 10px }
/* A CARD, with its stage stripe down the left edge. The stripe is an inset shadow
   and not a border, for Today's own reason: a border would move the text sideways
   on the toned rows only. */
#scr-pe .pe-card { --st: var(--muted-2); background: var(--panel-2); border: 1px solid var(--line-soft); border-radius: var(--ctl-r);
  padding: 8px 10px; margin-top: 8px; box-shadow: inset 3px 0 0 var(--st) }
#scr-pe .pe-card.st-new { --st: var(--bad) }
#scr-pe .pe-card.st-responded { --st: var(--warn) }
#scr-pe .pe-card.st-proposal { --st: var(--accent-2) }
#scr-pe .pe-card.st-deposit { --st: var(--warn) }
#scr-pe .pe-card.st-booked { --st: var(--accent) }
#scr-pe .pe-card.st-settled { --st: var(--accent-2) }
#scr-pe .pe-card.st-lost { --st: var(--muted) }
#scr-pe .pe-card-top { display: flex; align-items: center; gap: 8px; font-size: 13px }
#scr-pe .pe-card-top .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
#scr-pe .pe-card-sub { font-size: var(--note-fs); color: var(--muted); margin-top: 3px; line-height: 1.4 }
#scr-pe .pe-card-pills { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px }
#scr-pe .pe-card-acts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px }
#scr-pe .pe-card-acts select { font-size: var(--ctl-fs); padding: var(--ctl-pad-c) }
#scr-pe .pe-card .err { margin-top: 6px }
/* THE REPLY on a card, and the Reply templates panel (the owner's ruling,
   2026-09-25). The boxes are the house field label stacked over its box (`.main
   label`, via `.pe-fld`); `.pe-wide` lets Subject, Message and a template's Text
   take the card's whole width, because an email line cut at 420px is a line
   nobody can check. The preview is the send box's own `.doc-preview`, shorter,
   because it sits inside a card rather than a dialog. */
#scr-pe .pe-reply, #scr-pe .pe-tpl-edit { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line-soft) }
#scr-pe .pe-fld.pe-wide { display: flex; width: 100% }
#scr-pe .pe-fld.pe-wide input, #scr-pe .pe-fld.pe-wide textarea { max-width: none; width: 100% }
#scr-pe .pe-reply .cal-row .pe-fld.grow input { max-width: none }
#scr-pe .pe-reply-prev { height: 360px; min-height: 0 }
/* THE HISTORY FOLD at the foot of every card (Chris, 2026-09-29). The Show
   P&L's disclosure recipe — a `.lnk` with the drawn chevron, turned a quarter
   while shut — at the note size, muted until hovered. Each line is the stamp
   and the name in the muted note type over the sentence in the card's own. */
#scr-pe .pe-hist { margin-top: 8px }
#scr-pe .pe-hist-open { font-size: var(--note-fs); color: var(--muted); font-weight: 600 }
#scr-pe .pe-hist-open:hover { color: var(--accent-2) }
#scr-pe .pe-hist-open .ico { transition: transform .15s; transform: rotate(-90deg) }
#scr-pe .pe-hist-open[aria-expanded="true"] .ico { transform: none }
/* THE UNSORTED BOX, collapsed (Chris, 2026-10-03: "the unsorted section. we
   need to be able to delete those and collapse the box"). Its title is the same
   disclosure — a `.lnk` with the drawn chevron turned a quarter while shut — in
   the panel title's own type, so a shut box is the one title line and nothing
   under it. The bulk bar is the Mail tab's: Select all, the count, Delete
   selected, and the refusal on a line of its own. */
#scr-pe #pe-unsorted > .panel-title { margin-bottom: 0 }
#scr-pe .pe-un-toggle { display: inline-flex; align-items: center; letter-spacing: inherit; text-transform: inherit; font-weight: inherit }
#scr-pe .pe-un-toggle:hover { color: var(--accent-2) }
#scr-pe .pe-un-toggle .ico { transition: transform .15s; transform: rotate(-90deg) }
#scr-pe .pe-un-toggle[aria-expanded="true"] .ico { transform: none }
#scr-pe .pe-un-body { margin-top: 9px }
#scr-pe .pe-un-bulk { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; margin: 0 0 8px }
#scr-pe .pe-un-bulk [hidden] { display: none }
#scr-pe .pe-un-selcount { font-size: 12px; font-weight: 600 }
#scr-pe .pe-un-bulk .err { flex-basis: 100% }
#scr-pe .pe-un-bulk .err:empty { display: none }
#scr-pe .pe-un-pick { width: auto; margin: 0; flex: none }
/* The disclosure chevrons turn without moving for a person who asked for less
   motion (audit M8): the History fold's, the Unsorted box's and Show P&L's
   `.spl-open`. */
@media (prefers-reduced-motion: reduce) { #scr-pe .pe-hist-open .ico, #scr-pe .pe-un-toggle .ico, #scr-showpnl .spl-open .ico { transition: none } }
#scr-pe .pe-hist-body { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line-soft) }
#scr-pe .pe-hist-row { display: flex; flex-direction: column; gap: 1px; padding: 4px 0; font-size: 12.5px; line-height: 1.4 }
#scr-pe .pe-hist-row + .pe-hist-row { border-top: 1px solid var(--line-soft) }
#scr-pe .pe-hist-when { font-size: var(--note-fs); color: var(--muted) }
/* THE EMAIL UNDER A CARD (Chris, 2026-10-01). The subject is a `.lnk` button in
   the card's sub-line, a weight heavier so it reads as something to press; the
   reader is the house one (`.mail-read-*`, below) at the card's foot behind the
   History fold's rule, its frames shorter than Mail's because they sit in a
   card. */
#scr-pe .pe-read-open { font-weight: 600 }
#scr-pe .pe-read { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-soft) }
#scr-pe .pe-read .mail-read-frame { height: 320px }
#pe-tpl { margin-top: 10px }
#scr-pe .pe-tpl-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 6px 0; border-bottom: 1px solid var(--line-soft); font-size: 12.5px }
#scr-pe .pe-tpl-row:last-child { border-bottom: 0 }
#scr-pe .pe-tpl-row .grow { flex: 1; min-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
#scr-pe .pe-tpl-ph { line-height: 1.6 }
/* THE AGE PILL'S THREE TONES — quiet under four hours, amber from four, red from
   twenty-four (peAge, flight-director.js:4315-4324). Quiet is the pill at rest,
   and the two states somebody acts on are the house tones `warn` and `bad`
   (style wave, 2026-09-23; they were private `t-warn` / `t-bad` copies), so red
   keeps meaning what it means. No rule of this screen's draws them. */
/* Four tiles at every width, the row scrolling inside itself when they do not fit
   — the document itself never scrolls sideways (the 2026-09-09 walk's rule). */
#scr-pe .tiles.four { grid-template-columns: repeat(4, minmax(150px, 1fr)); overflow-x: auto }
@media (max-width: 720px) {
  #scr-pe .tiles.four { grid-template-columns: repeat(2, minmax(140px, 1fr)) }
  #scr-pe .pe-fld input { min-width: 96px }
}
/* ── Client Book (#scr-pebook) — Lane B ── */
/* ── The Event Page (inside #scr-pe) — Lane E ──────────────────────────────────
   EVERY RULE IS SCOPED TO `#scr-pe`, for the reason the block header above gives:
   stage 1's bare `.who` rule repainted Today from another screen's stylesheet, and
   a bare `.pev-cat` would do it again. Tokens only, so the venue's skin reaches
   this page the way it reaches every other one.

   Measured at 375px (the width a bar lead reads this on): the charge row's five
   controls do not fit on one line, so `.pev-cat` wraps and the amount takes the
   end of the second line — the same shape the To-Do rows hold. The two-column
   field grid becomes one column at the same width. */
#scr-pe .pev-head-row { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 12px; flex-wrap: wrap }
#scr-pe .pev-titlecol { min-width: 0 }
/* The event's name is the house `h2` (style wave, 2026-09-23; review M9) — it was
   a private 19px copy. */
#scr-pe .pev-sub { font-size: 12px; color: var(--muted); margin-top: 2px }
/* The kind is the house pill as drawn; it only sits on the head row's middle. The
   status pill's four tones are the house tones (`go` · `warn` · `bad` · `sky`). */
#scr-pe .pev-kind { align-self: center }
/* The unsaved-edits question (the owner's ruling, 2026-09-23): a question before
   a loss, in the arm's own amber, and nothing at all while there is none. */
#scr-pe .pev-ask { font-size: 12px; color: var(--warn); margin: -6px 0 10px }
#scr-pe .pev-ask:empty { display: none }
#scr-pe .pev-newbanner { border-color: var(--warn) }
#scr-pe .pev-tabs { margin-bottom: 12px }
#scr-pe .pev-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px }
#scr-pe .pev-fld { display: flex; flex-direction: column; gap: 3px; font-size: 12px }
#scr-pe .pev-fl { font-size: 11px; color: var(--muted-2); letter-spacing: .04em }
#scr-pe .pev-fn { font-size: 11px; color: var(--muted); letter-spacing: .04em }
/* Tax exempt is a tick and its words ON ONE LINE, inside the panel title — and
   the title's eyebrow (capitals, .16em) and `.main label`'s stacked column both
   reached it, so it drew the box above an uppercase word (review item 3). */
#scr-pe .pev-tog { display: inline-flex; flex-direction: row; align-items: center; gap: 6px; margin: 0; font-size: 11px;
  color: var(--muted); text-transform: none; letter-spacing: normal }
#scr-pe .pev-tog input { width: auto }
#scr-pe .pev-type { max-width: 170px }
/* The checklist. `.on` is the ticked line and `.waived` the one that prints and
   adds nothing — both are stated in the row's own text as well as its colour, so
   the state reads to somebody who cannot see the colour. */
#scr-pe .pev-cat-g { margin-bottom: 12px }
#scr-pe .pev-cat-g h4 { margin: 0; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted-2) }
#scr-pe .pev-cat { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--line-soft); flex-wrap: wrap }
/* The tick and the Waived box are `<label>`s, and `.main label` stacks a label
   over its box, in Slate Lavender at .04em — so both were drawn with
   the box ABOVE its words (review item 2). Each states its own row, and the
   line's name reads in Lamp White. */
#scr-pe .pev-cat-tick { display: flex; flex-direction: row; align-items: center; gap: 8px; flex: 1; min-width: 180px; margin: 0; font-size: 12px;
  color: var(--text); text-transform: none; letter-spacing: normal }
#scr-pe .pev-cat-tick input { width: auto; flex: none }
#scr-pe .pev-cat-tick .nm small { display: block; font-size: 10px; color: var(--muted) }
#scr-pe .pev-cat.on .pev-cat-tick .nm { font-weight: 700 }
#scr-pe .pev-cat.waived .pev-cat-tick .nm { color: var(--muted) }
#scr-pe .pev-cat .pev-n { flex: none; width: 86px; text-align: right }
#scr-pe .pev-cat .pev-cat-nm { flex: 1; min-width: 140px }
#scr-pe .pev-cat-waive { display: inline-flex; flex-direction: row; align-items: center; gap: 5px; flex: none; margin: 0; font-size: 11px;
  color: var(--muted); text-transform: none; letter-spacing: normal }
#scr-pe .pev-cat-waive input { width: auto }
#scr-pe .pev-cat-amt { flex: none; min-width: 92px; text-align: right; font-variant-numeric: tabular-nums; font-size: 12px }
/* The totals block, the terms block and the invoice all use one row shape, so a
   figure sits in the same column on all three. */
#scr-pe .pev-sums { margin-top: 10px }
#scr-pe .pev-sum { display: flex; justify-content: space-between; gap: 14px; padding: 5px 0;
  border-bottom: 1px solid var(--line-soft); font-size: 12px; font-variant-numeric: tabular-nums }
#scr-pe .pev-sum.total { border-top: 2px solid var(--border); border-bottom: 0; margin-top: 4px; padding-top: 9px; font-weight: 700 }
#scr-pe .pev-sum.dep span:last-child { color: var(--accent-2) }
#scr-pe .pev-banner { margin-top: 9px; padding: 8px 10px; border: 1px solid var(--warn); border-radius: var(--ctl-r);
  background: color-mix(in srgb, var(--warn) 12%, transparent); font-size: 11.5px; line-height: 1.45 }
#scr-pe .pev-guard { margin-top: 9px; font-size: 11px; color: var(--muted); line-height: 1.5; letter-spacing: .02em }
#scr-pe .pev-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 8px }
#scr-pe .pev-cell { border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 7px 9px; font-size: 12px }
#scr-pe .pev-cell .k { display: block; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-2) }
#scr-pe .pev-cell .v { display: block; margin-top: 2px; font-variant-numeric: tabular-nums }
#scr-pe .pev-cell.good { border-color: var(--accent) }
#scr-pe .pev-cell.warn { border-color: var(--warn) }
#scr-pe .pev-ros { display: flex; gap: 10px; padding: 4px 0; border-bottom: 1px solid var(--line-soft); font-size: 12px }
#scr-pe .pev-ros .t { flex: none; width: 82px; font-variant-numeric: tabular-nums; color: var(--muted-2) }
#scr-pe .pev-ros .what { flex: 1; min-width: 0 }
#scr-pe .pev-ros .who { flex: none; color: var(--muted); font-size: 11px }
/* The three notes boxes (the client's Notes & Package Details, the crew's notes,
   and a market's description and links) take the panel's width. `.chk-field` has
   no rule of its own and its box sits BESIDE its label rather than inside it, so
   no width rule reached it and it drew at the browser's 157px (review item 4). */
#scr-pe .chk-field textarea { display: block; width: 100%; box-sizing: border-box }
/* The preview frame. It is `sandbox` with no allow-list — an opaque origin — so a
   document rendered out of ledger text cannot script the app; its `srcdoc` is set
   as a DOM property and never as markup, because the document carries its own
   <style> block and injecting that into the page would restyle the app itself. */
#scr-pe .pev-doc { width: 100%; height: 520px; border: 1px solid var(--border); border-radius: var(--ctl-r); background: #fff }
#scr-pe .pev-mkt-flyer { display: block; max-width: 100%; margin-top: 8px; border-radius: var(--ctl-r) }
#scr-pe .pev-mkt-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px }
@media (max-width: 560px) {
  #scr-pe .pev-grid { grid-template-columns: 1fr }
  #scr-pe .pev-cat-amt { min-width: 0; margin-left: auto }
  #scr-pe .pev-doc { height: 380px }
}
/* EVERY RULE BELOW IS SCOPED TO `#scr-pebook`, tokens only. `.tile`, `.pill`,
   `.data-table` and `.att-row` are the house's shared recipes and are NOT
   redefined here — what this block does is the five-across tile row, the tick
   column, the badge cell, the four status colours the book's own vocabulary
   needs, and the guard paragraph under the table. */

/* FIVE TILES, not the house's three or four. The book's row is Clients ·
   Repeat · Lost · Scout prospects · Lifetime booked, and all five belong on one
   line because the fifth is the sum of the first. Under 1180px they fall to
   three and then to two rather than shrinking a 34px figure into a column
   narrower than the word above it. */
#scr-pebook .tiles.peb-tiles { grid-template-columns: repeat(5, minmax(0, 1fr)) }
@media (max-width: 1180px) { #scr-pebook .tiles.peb-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)) } }
@media (max-width: 760px) { #scr-pebook .tiles.peb-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)) } }

/* The segment tabs are the house tab strip (`.settabs`, style wave 2026-09-23;
   review item 19). They were `.btn`s with the current one `.btn.primary`, which
   put a second green on the screen at all times beside Compose to selected.
   The strip's own rule draws them; nothing here. */

/* The tick column is as wide as a tick and no wider, and it never wraps: the
   name beside it is what the row is for. */
#scr-pebook .data-table td.peb-tick, #scr-pebook .data-table th:first-child { width: 34px; padding-right: 0 }
#scr-pebook .data-table td.peb-tick input { margin: 0; cursor: pointer }
/* The name is a button that reads as the row's link, and the organisation rides
   beside it a size down — one line, ellipsised, because a row is a reading. */
#scr-pebook .peb-open { font-weight: 600; color: var(--text) }
#scr-pebook .peb-open:hover { color: var(--accent-2) }
#scr-pebook .data-table .muted-note { margin-top: 2px; max-width: 62ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
#scr-pebook .peb-badges { white-space: nowrap }
#scr-pebook .peb-badges .pill { margin-right: 4px }

/* THE BOOK'S OWN FOUR STATUSES, in the house's `.pill.status` recipe — a 16%
   tint of one token with a matching border — so a badge here reads exactly as a
   status pill does on the Calendar and the show page. Scoped, because `client`,
   `lost` and `prospect` are this screen's words and nobody else's. */
#scr-pebook .pill.status.s-client { --lc: var(--accent) }
#scr-pebook .pill.status.s-lost { --lc: var(--bad) }
#scr-pebook .pill.status.s-prospect { --lc: var(--accent-2) }
/* RESEARCHED · UNVERIFIED is deliberately the quietest of the four: it is a
   provenance mark rather than a state, and it must not out-shout the badge it
   sits beside. */
#scr-pebook .pill.status.s-scout { --lc: var(--muted-2) }
/* The drawer's event history wears the EVENT's four statuses, which are a
   different vocabulary from the book's (pe-guard.js: a lead is `booked` and an
   event is `confirmed`, and neither word is in the other set). */
#scr-pebook .pill.status.s-proposal { --lc: var(--warn) }
#scr-pebook .pill.status.s-confirmed { --lc: var(--accent) }
#scr-pebook .pill.status.s-settled { --lc: var(--accent-2) }

/* The drawer. A panel, not a slide-over: this app has no drawer recipe and a
   second overlay for one screen is a second thing to keep in step. */
#scr-pebook #peb-drawer .peb-facts { margin: 8px 0 }
#scr-pebook #peb-drawer .peb-notes { font-size: 12px; color: var(--muted); white-space: pre-wrap }
#scr-pebook #peb-drawer .peb-evs td { border-bottom: 0 }
#scr-pebook #peb-drawer .sm-field { max-width: 420px }

/* The guard, under the table. It is the one paragraph on this screen that
   describes what the screen CANNOT do, so it is set as a note rather than as a
   panel — a box would make it look like something to act on. */
#scr-pebook .peb-guard { font-size: var(--note-fs); line-height: 1.5; color: var(--muted); margin: -4px 0 12px; max-width: 92ch }
#scr-pebook .peb-guard b { color: var(--text); font-weight: 600 }
/* ── Private events, Lane M — landing on an enquiry from Today ──────────────
   ONE rule, and it is scoped to the Event Calendar's own screen for the reason
   the block above states: stage 1's bare `.who` rule repainted Today from
   another screen's stylesheet. `.pe-landed` is put on a pipeline card for two
   and a half seconds by web/today.js when somebody clicks an unanswered-enquiry
   row, so the card they came for is the one they see — a screen full of similar
   cards scrolled to roughly the right place is not an answer. Tokens only, and
   it fades where the reader has not asked for less motion. */
#scr-pe .pe-landed { outline: 2px solid var(--accent); outline-offset: 2px; transition: outline-color .4s ease; }
@media (prefers-reduced-motion: reduce) { #scr-pe .pe-landed { transition: none; } }

/* ── Settings › Private events (Lane S) ────────────────────────────────────────
   Scoped to the panel's own id, which is the rule stage 1's bare `.who` broke:
   an unscoped `.pe-set-rules` would be a class name two screens could grow. The
   panel is otherwise the house recipe exactly — panels, labels, `.att-row`s and
   one `.err` line — and this is the one box that needs more than `<label>` gives
   it: a list of domains is read down the left edge, so it wants the tabular type
   and more width than the 420px a text field takes.

   The type stack is `#st-feed-url`'s and `.sm-prints`', spelled out the same way
   for the reason they spell it out: this file has no `--mono` token, and declaring
   one in a lane that may not re-flow the palette block would put it somewhere
   nobody would look for it. */
#st-pe .pe-set-rules { max-width: 560px; font-family: Consolas, 'Segoe UI Mono', monospace; font-size: 12px; line-height: 1.6; white-space: pre; resize: vertical }

/* ══ Stage 3+4, Lane 0 ════════════════════════════════════════════════════════
   The six screens' blocks, headers only — each lane fills its own. EVERY RULE IN
   EACH BLOCK IS SCOPED TO ITS SCREEN (`#scr-<key> …`), and that is not tidiness:
   stage 1 shipped a bare `.who` rule from one screen's stylesheet and it
   repainted Today, and a bare `.tw-row` or `.mk-tile` would do it again. Tokens
   only — no literal colour, no literal font — so the venue's skin reaches these
   screens the way it reaches every other one. The Correspondence panel is
   painted on the show page, so Lane M scopes it under `#scr-show` (the show
   page's own section; the calendar's rail item stays lit while it is up). */
/* ── Show P&L (#scr-showpnl) — Lane P ── */
/* The range bar and its date boxes are Clover's own `.cv-bar` / `.cv-dates`
   (the same control, the desktop's same six presets); only what is this
   screen's is below. A whole row opens its show, so it says so with the
   pointer. Colour is on a FIGURE only: a negative net in Refusal Red, a settled
   show and a fully paid one in Settled Green — the desktop's `pnl-neg` / `pos`. */
#scr-showpnl .spl-row { cursor: pointer }
#scr-showpnl .data-table td.num { font-variant-numeric: tabular-nums; white-space: nowrap }
#scr-showpnl .spl-neg { color: var(--bad) }
#scr-showpnl .spl-pos { color: var(--accent) }
#scr-showpnl tfoot td { border-top: 1px solid var(--border) }
/* ── Weekly Shortage (#scr-shortage) — Lane S ── */
/* The sub-line is the house `.sub` under the name, and Pull bank sits in a
   `.cv-bar` row under it (style wave, 2026-09-23); the private head row is gone. */
#scr-shortage .sh-banner { margin: 8px 0 12px; font-size: 12px; line-height: 1.5; letter-spacing: 0; color: var(--muted) }
#scr-shortage .sh-note { line-height: 1.5; letter-spacing: 0 }
/* A panel's sentence, moved out of its title into the note straight under it
   (the Sentence Rule; review item 11): it sits on the title's own 9px. */
#scr-shortage .sh-lede { margin-top: 0 }
#scr-shortage .sh-bad { color: var(--bad) }
/* The split is the Summary Row recipe (DESIGN.md "The Summary Row"), scoped. */
#scr-shortage .sh-sum { display: flex; flex-direction: column }
#scr-shortage .sh-sr { display: flex; align-items: baseline; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--line-soft) }
#scr-shortage .sh-sr:last-child { border-bottom: 0 }
#scr-shortage .sh-sl { flex: 1; color: var(--muted); display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap }
#scr-shortage .sh-ss { flex-basis: 100%; font-size: 11px; color: var(--muted-2) }
#scr-shortage .sh-sa { min-width: 110px; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text) }
#scr-shortage .sh-sr.go .sh-sa, #scr-shortage .sh-sr.net.go .sh-sa { color: var(--accent) }
#scr-shortage .sh-sr.decl .sh-sa { color: var(--accent-2) }
#scr-shortage .sh-sr.net .sh-sl { font-weight: 650; color: var(--text) }
#scr-shortage .sh-sr.net.bad .sh-sa { color: var(--bad) }
/* Pills are the house pill and its tones (`pill dim` at rest, `go` · `sky` ·
   `warn` · `bad`); nothing here draws or tints one. */
#scr-shortage .sh-counts { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums }
#scr-shortage .sh-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--muted-2) }
#scr-shortage .sh-dot.on { background: var(--accent) }
#scr-shortage tr.sh-paused td { color: var(--muted) }
#scr-shortage .sh-name { font-weight: 600 }
#scr-shortage .data-table .muted-note { margin-top: 2px }
#scr-shortage tfoot td { border-bottom: 0 }
/* Arrears: owed-stock, set apart from the flow in the one red this page uses. */
#scr-shortage .sh-arr { border-color: var(--bad) }
#scr-shortage .sh-arr-cap { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted) }
/* The Headline's size, on the type ramp (the detector baseline flagged 28px). */
#scr-shortage .sh-arr-big { font-size: 23px; font-weight: 800; color: var(--bad); font-variant-numeric: tabular-nums }
#scr-shortage .sh-arr-break { font-size: 12.5px; color: var(--text) }
/* The obligation form: two columns of stacked fields on a desk, one on a phone. */
#scr-shortage .sh-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 12px; margin: 8px 0 12px }
#scr-shortage .sh-form .sh-wide { grid-column: 1 / -1 }
#scr-shortage .sh-form-acts { display: flex; gap: 8px; align-items: center; margin-top: 4px }
@media (max-width: 899px) {
  #scr-shortage .sh-form { grid-template-columns: minmax(0, 1fr) }
}
/* ── Day of Show (#scr-dayofshow) — Lane D ── */
/* Every rule below is scoped to the screen: a bare .dos-* or .pill rule here
   would reach any other screen that happened to use the name (stage 1's bare
   .who repainted Today). Tokens only; no colour is spelled in this block. */
/* The header: the LIVE mark, the show's name (the house `h2` — it was a private
   18px at 800, style wave 2026-09-23) and its line of facts, the stamp, and the
   refresh icon button. */
#scr-dayofshow .dos-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap }
#scr-dayofshow .dos-lede { margin-bottom: 12px }
#scr-dayofshow .dos-facts { font-size: 12px; color: var(--muted); margin-top: 2px }
#scr-dayofshow .grow { flex: 1; min-width: 0 }
#scr-dayofshow .dos-stamp { font-size: 11px; color: var(--muted-2); letter-spacing: .04em; font-variant-numeric: tabular-nums; white-space: nowrap }
/* The LIVE mark is Settled Green and pulses; a person who has asked for less
   motion gets the mark without the pulse. */
#scr-dayofshow .dos-live { display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 800; letter-spacing: .16em; color: var(--accent) }
#scr-dayofshow .dos-live .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); animation: dos-pulse 1.6s ease-in-out infinite }
@keyframes dos-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
@media (prefers-reduced-motion: reduce) { #scr-dayofshow .dos-live .dot { animation: none } }
/* The warning banner — a door that went quiet, the figures beside it frozen.
   Rental Amber on the left rule, the house's "look at this" colour; red is
   reserved for a refusal. */
#scr-dayofshow .dos-banner { font-size: 12px; color: var(--text); background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--warn); border-radius: var(--ctl-r); padding: 8px 12px; margin-bottom: 12px }
/* Six tiles: Today's recipe, three across, two rows (and one column on a
   phone, from the shared .tiles rule). The figure after the slash is small —
   the body's 13px, on the type ramp (it was 16px, off it). */
#scr-dayofshow .dos-tiles .tile:hover { border-color: var(--border) }
#scr-dayofshow .dos-tiles .tile-value small { font-size: 13px; font-weight: 700; color: var(--muted) }
#scr-dayofshow .dos-tiles .tile.dos-go .tile-value { color: var(--accent) }
#scr-dayofshow .dos-tiles .tile.dos-warm .tile-value { color: var(--warn) }
#scr-dayofshow .dos-meter { height: 4px; border-radius: 4px; background: var(--panel-2); margin-top: 8px; overflow: hidden }
#scr-dayofshow .dos-meter i { display: block; height: 100%; background: var(--accent) }
/* Two panels side by side: the two charts, then the two tables. */
#scr-dayofshow .dos-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px }
@media (max-width: 899px) { #scr-dayofshow .dos-two { grid-template-columns: 1fr } }
/* The two charts. The element takes the viewBox's own aspect ratio, so the bars
   fill the panel edge to edge and the type is never stretched. Colour is
   currentColor on every mark, set here from a token: the door in Settled Green,
   the bar in Rental Amber. */
#scr-dayofshow .dos-chart { display: block; width: 100%; height: auto; aspect-ratio: 460 / 120 }
#scr-dayofshow .dos-chart rect { fill: currentColor }
#scr-dayofshow .dos-chart .ax { stroke: var(--border) }
#scr-dayofshow .dos-chart text { fill: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums }
#scr-dayofshow .dos-chart text.vl { fill: currentColor; font-weight: 700 }
#scr-dayofshow .dos-chart.dos-go { color: var(--accent) }
#scr-dayofshow .dos-chart.dos-warm { color: var(--warn) }
#scr-dayofshow td.num, #scr-dayofshow th.num { text-align: right; font-variant-numeric: tabular-nums }
#scr-dayofshow .dos-dim { color: var(--muted-2); font-size: 11px }
/* The guest list: a name and its pill, wrapping. */
#scr-dayofshow .dos-guests { display: flex; flex-wrap: wrap; gap: 8px }
#scr-dayofshow .dos-guest { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 4px 8px }
#scr-dayofshow .dos-guest-count { font-size: 13px; font-variant-numeric: tabular-nums }
#scr-dayofshow .dos-next { color: var(--text) }
/* A TAG THAT IS A PHRASE IS SENTENCE CASE (the Sentence Rule; style review X8).
   The panel-title tags on the private-event and money screens are phrases —
   "per 15 min, from PreKindle scans", "tick what applies · prices are per
   event", "bank half + declared half" — and the tag's capitals shouted them.
   They keep the tag's size, weight and colour and read as written, the way
   `#calw-msg` does on the calendar. Written in the case each should read.
   Mail's two are phrases too — "3 conversations", "last 30 days" (final
   review of mail auto-sort, S-M4). */
#scr-pe .panel-title .tag, #scr-pebook .panel-title .tag, #scr-showpnl .panel-title .tag,
#scr-shortage .panel-title .tag, #scr-dayofshow .panel-title .tag, #scr-mail .panel-title .tag { text-transform: none; letter-spacing: .02em }
/* ── Mail (#scr-mail) — Lane M ── */
/* The thread count beside a subject: the rail badge's shape, quieter. */
#scr-mail .ml-count { display: inline-block; min-width: 18px; padding: 0 6px; margin-left: 6px; border: 1px solid var(--border); border-radius: 20px; background: var(--panel-2); color: var(--muted); font-size: 10px; font-weight: 700; text-align: center }
#scr-mail .ml-when { white-space: nowrap }
#scr-mail .ml-from { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
#scr-mail .ml-act { white-space: nowrap; text-align: right }
/* AN ARMED BUTTON WRAPS (final review S-M3). The arm puts its question in the
   button's label — Undo's is fifty-odd characters — and the cell does not
   wrap, so the row widened while it was armed and a phone's table jumped
   sideways under the second click. Armed, the question wraps inside a
   220px cap, so the column grows to that at most and the page never does.
   Every armed button on the screen takes it: the sender list's Remove and
   Delete selected ask their questions on a phone's width too. */
#scr-mail .btn[data-armed="1"] { white-space: normal; max-width: 220px; text-align: left }
/* And the row's two buttons may stack while one of them asks: measured at
   1280px, an armed Undo beside Move made the Auto-filed table 59px wider than
   its panel; stacked, it fits. At rest they stay side by side. */
#scr-mail .ml-act:has(.btn[data-armed="1"]) { white-space: normal }
#scr-mail .ml-act .btn { vertical-align: middle }
/* THE SUBJECT KEEPS A READABLE WIDTH (the owner's phone walk, 2026-09-23). The
   table already sat in the house `.tbl-wrap`, but When, From and the two
   buttons do not wrap, so at 375px they took the width and the Subject was
   squeezed to a sliver — two or three characters a line, a dozen lines tall —
   while Payments, the Sent log and Contacts scroll sideways. A floor under the
   two columns that wrap makes the table wider than the phone, and the wrap
   scrolls it. */
#scr-mail .ml-subj { min-width: 200px }
#scr-mail .ml-show { min-width: 140px }
#scr-mail tr.ml-sub, #scr-mail tr.ml-sub:hover { background: var(--panel-2) }
/* The Review picker and the Skip question sit in a row of their own under the
   thread, full width, wrapping at a phone's width rather than scrolling. */
#scr-mail .ml-review, #scr-mail .ml-ask { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center }
#scr-mail .ml-field { display: inline-flex; gap: 8px; align-items: center; font-size: 12px; color: var(--muted) }
#scr-mail .ml-field[hidden] { display: none }
/* The destination strip sits in the review row, which owns the spacing. */
#scr-mail .ml-dest { margin: 0 }
#scr-mail .ml-field select, #scr-mail .ml-field input { max-width: 320px }
#scr-mail .ml-review .att-row, #scr-mail .ml-review .muted-note { flex-basis: 100% }
#scr-mail .ml-ask span { flex: 1 1 320px; font-size: 13px }
/* The bulk bar (2026-09-25). The tick column is the box and nothing else; the
   bar sits between the sweep line and the table, the count first in the row
   text's weight, then the three buttons, then the refusal sentence, wrapping on
   a phone. File selected's picker opens under it on the panel's second shade,
   the Review picker's own row. `[hidden]` is restated because a flex rule
   outranks the attribute. */
#scr-mail .ml-pick { width: 1%; padding-right: 0 }
#scr-mail .ml-bulk { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; margin: 8px 0 }
#scr-mail .ml-bulk[hidden], #scr-mail .ml-bulk [hidden] { display: none }
#scr-mail .ml-selcount { font-size: 12px; font-weight: 600 }
#scr-mail .ml-bulk .err { flex-basis: 100% }
#scr-mail .ml-bulk .err:empty { display: none }
#scr-mail .ml-bulkfile { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 10px 12px; margin: 0 0 8px }
/* Auto-sort (2026-10-01): the owner's switch is the house checkbox line
   (`label.tog`), set off from the sweep line above it and sitting on the
   sentence that says whether it is on; the view strip takes the gap under. */
#scr-mail label.ml-auto-switch { margin: 10px 0 2px }
#scr-mail #ml-views { margin: 10px 0 8px }
/* On a phone the table is wider than the screen (the floor above), and a row
   under a thread spans the whole table — so the Review picker, the Skip
   question and the reader would start on screen and run off it. Each is held
   to the width the phone shows and stays put while the table scrolls under it:
   100vw less the column's 14px sides, the panel's and the cell's padding. */
@media (max-width: 899px) {
  #scr-mail tr.ml-sub > td > div { position: sticky; left: 8px; max-width: calc(100vw - 70px) }
}
/* THE HOUSE EMAIL READER (`LoadIn.emailReader`, web/app.js) — Mail's reader,
   the show page's Correspondence and an enquiry card's reader (fx-read,
   2026-10-01). A stranger's HTML in a SANDBOXED frame; the frame is the page an
   email is written for, so it is paper, as the send box's document preview is.
   A conversation is one header and one frame per message, oldest first. */
.mail-read-head { font-size: 12px; color: var(--muted); margin-bottom: 6px }
/* An enquiry's conversation (2026-11-08): each item says In (the person wrote,
   green) or Out (we sent it, dim), with a stripe down its left edge to match. */
.mail-read-msg.mail-read-in { border-left: 3px solid var(--accent); padding-left: 10px }
.mail-read-msg.mail-read-out { border-left: 3px solid var(--muted-2); padding-left: 10px }
.mail-read-dir { margin-right: 2px }
/* Our own record in the conversation: plain text, sized to its words. */
.mail-read-own { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; line-height: 1.5; color: var(--text); background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 8px 10px }
#scr-pe .pe-card-in { font-size: var(--note-fs); color: var(--accent); margin-top: 3px; font-weight: 600 }
/* The Conversation button on a card and the events@ list in the side panel (2026-11-08). */
#scr-pe .pe-card-cv { margin-top: 6px }
#scr-pe .pe-card-cv .btn { min-height: 32px; padding: 0 12px; font-size: 12px }
#scr-pe .panel-title .pe-mailbox-open { margin-left: auto; min-height: 32px; font-size: 12px }
.pe-cv-count { margin-bottom: 8px }
.pe-cv-list { display: flex; flex-direction: column; gap: 8px }
.pe-cv-row { border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--ctl-r); padding: 8px 10px; background: var(--panel-2) }
.pe-cv-row.unfiled { border-left-color: var(--warn) }
.pe-cv-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; overflow-wrap: anywhere }
.pe-cv-where { font-size: 12px; margin-top: 2px }
.pe-cv-acts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px }
.pe-cv-acts select { min-width: 0; flex: 1 1 220px; max-width: 420px }
/* Reply from the Conversation panel (2026-11-08): under the messages, the card's fields. */
.pe-cv-reply { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px }
.pe-cv-rh { font-size: 13px; font-weight: 650; margin: 0 }
.pe-cv-f { display: flex; flex-direction: column; gap: 3px; font-size: 11px; letter-spacing: .04em; color: var(--muted) }
.pe-cv-f input, .pe-cv-f select, .pe-cv-f textarea { width: 100%; max-width: 100%; color: var(--text); font-size: 13px }
.pe-cv-f textarea { min-height: 140px; resize: vertical; font-family: inherit; line-height: 1.5 }
.mail-read-frame { display: block; width: 100%; height: 420px; border: 1px solid var(--border); border-radius: var(--ctl-r); background: #fff }
.mail-read-atts { margin-top: 4px }
.mail-read-msg + .mail-read-msg { margin-top: 12px }
/* The Correspondence panel, on the show page. */
#scr-show #corr-panel .corr-note { font-size: 11px; font-weight: 400; color: var(--muted-2); margin-left: 6px; letter-spacing: .02em; text-transform: none }
#scr-show #corr-panel tr.corr-sub, #scr-show #corr-panel tr.corr-sub:hover { background: var(--panel-2) }
/* Mail's own column recipe on Correspondence (polish, 2026-10-01): the stamp
   never wraps, and the two columns that do wrap keep a readable width, so on a
   phone the table scrolls sideways inside its `.tbl-wrap` instead of squeezing
   the Subject and the Attachments to two words a line. */
#scr-show #corr-panel .corr-when { white-space: nowrap }
#scr-show #corr-panel .corr-subj { min-width: 200px }
#scr-show #corr-panel .corr-atts { min-width: 220px }
/* ── Tour Watch (#scr-tourwatch) — Lane T ── */
/* Four tiles, not Today's three: the desktop's NEW · WATCHING · OFFER SENT ·
   BOOKED VIA TW, figure only (the desktop prints no sub-line under them). They
   are the house `.tiles.four`; a private copy with its own breakpoint went in
   the style wave. */
#scr-tourwatch .tw-status { margin: 0 0 0 8px }
#scr-tourwatch .tw-badge { display: inline-block; margin-left: 6px; min-width: 16px; padding: 0 5px; border-radius: 20px; background: var(--bad); color: var(--on-accent); font-size: 10px; font-weight: 700 }
#scr-tourwatch .tw-badge[hidden] { display: none }
/* The five filters are THE tab strip (`.settabs`); they were a copy of the
   segment and clipped "All" at 375px until the style wave. */
#scr-tourwatch .tw-filterrow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 4px 0 10px }
#scr-tourwatch .tw-filterrow .muted-note { margin: 0 }
#scr-tourwatch tr.tw-row { cursor: pointer }
#scr-tourwatch tr.tw-row.flash { background: color-mix(in srgb, var(--warn) 18%, transparent) }
/* The home check and the draw are ONE line until clicked (Chris, 2026-08-08). */
#scr-tourwatch .tw-one { max-width: 220px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer }
#scr-tourwatch .tw-one.open { white-space: normal; overflow: visible }
/* THE COLUMNS THAT WRAP KEEP A READABLE WIDTH (the owner's phone walk,
   2026-09-23). The board sat in the house `.tbl-wrap`, but the one-line cells
   and the status controls do not wrap, so at 375px they took the width and
   "TX dates seen" was squeezed to a 40px sliver, a date and a venue broken to
   a word a line. A floor under the three columns that wrap makes the board wider
   than the phone, and the wrap scrolls it. The rows stay dense (the owner's
   ruling, 2026-09-23: Tour Watch keeps its dense rows); only the columns' width
   is held — a two-date row measured 153px tall before and 85px after. */
#scr-tourwatch .tw-band { min-width: 150px }
#scr-tourwatch .tw-tx { min-width: 180px }
#scr-tourwatch .tw-agent-col { min-width: 150px }
/* The status is the house status pill in a tone (web/tourwatch.js TONE); the
   priority prefix is a red one under it. */
#scr-tourwatch .tw-prio { margin-top: 4px }
/* The band's name is a button that opens the drawer (a keyboard's way in). */
#scr-tourwatch .tw-open { font-weight: inherit }
#scr-tourwatch .tw-quicks { display: inline-flex; gap: 4px; margin-right: 6px }
#scr-tourwatch .tw-quick { padding: var(--ctl-pad-c) }
#scr-tourwatch .tw-quick.on { border-color: var(--accent-2); color: var(--accent-2) }
#scr-tourwatch .tw-issue { color: var(--bad) }
#scr-tourwatch .tw-sub { margin-top: 14px }
/* The drawer: a panel under the board with its own accent edge, the Client
   Book's reading of "this app has no slide-over". */
#scr-tourwatch .tw-drawer { border-left: 3px solid var(--accent-2) }
#scr-tourwatch .tw-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px }
/* The eyebrow over the band and each section's head are the house group
   heading (`.chk-h`), not private copies; the drawer's spacing is its own. The
   band is a heading at the 14px Title, not a 20px size of its own. */
#scr-tourwatch .tw-drawer .chk-h { margin: 0 0 6px }
#scr-tourwatch .tw-drawer h2 { margin: 4px 0 2px; font-size: 14px; font-weight: 650 }
#scr-tourwatch .tw-sec { margin-top: 14px }
#scr-tourwatch .tw-kv { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; padding: 3px 0; border-bottom: 1px solid var(--line-soft) }
#scr-tourwatch .tw-kv > span:first-child { color: var(--muted) }
#scr-tourwatch .tw-kv.tw-total > span:last-child { font-weight: 800; font-variant-numeric: tabular-nums }
#scr-tourwatch .tw-kv.tw-pass-v > span:last-child { color: var(--accent) }
#scr-tourwatch .tw-kv.tw-issue > span:last-child { color: var(--bad) }
#scr-tourwatch .tw-math { max-width: 520px }
#scr-tourwatch .tw-leg { font-size: 13px; padding: 2px 0 }
#scr-tourwatch .tw-gap { font-size: 13px; padding: 3px 0 }
#scr-tourwatch .tw-gap b { color: var(--accent) }
#scr-tourwatch .tw-gap.held b { color: var(--warn) }
#scr-tourwatch .tw-gap.gone b { color: var(--bad) }
#scr-tourwatch .tw-gap .muted-note { display: inline; margin: 0 }
#scr-tourwatch .tw-snap { font-style: italic }
#scr-tourwatch .tw-agent .nm { font-weight: 700 }
#scr-tourwatch .tw-agent .co, #scr-tourwatch .tw-agent .em { font-size: 12px; color: var(--muted) }
#scr-tourwatch .tw-note-body { font-size: 12px; color: var(--muted); white-space: pre-wrap }
/* The Draft Offer question: the confirmation recipe's amber rule (DESIGN.md › Panels). */
#scr-tourwatch .tw-ask { margin-top: 14px; padding: 10px 12px; border-left: 3px solid var(--warn); background: var(--panel-2); border-radius: var(--ctl-r); font-size: 13px }
#scr-tourwatch .tw-ask p { margin: 6px 0 10px }
#scr-tourwatch .tw-actions { margin-top: 14px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px }
#scr-tourwatch .tw-offer { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px }
#scr-tourwatch .tw-offer input[hidden] { display: none }
/* Settings › Tour Watch: a registry row's boxes are a table's, compact. */
#st-tourwatch .tw-reg-in { width: 110px; padding: var(--ctl-pad-c) }
#st-tourwatch .tw-reg-in.wide { width: 200px }
#st-tourwatch .tw-issue { color: var(--bad) }
#st-tourwatch .nowrap { white-space: nowrap }
/* ── Marketing (#scr-marketing) — Lane K ── */
/* The pipeline's pills and the three count pills are the house pill, `pill` at
   rest or `pill status <tone>` (they were a private copy until the style wave).
   Three tones are the house's own; the fourth is VIOLET, for an announce still
   queued — the desktop's own violet, which this palette has no token for, so it
   is mixed from two that it has (cyan into red lands on a lavender that reads
   apart from both on either skin). It is this screen's alone, so it is scoped,
   and it tints only with `status` beside it. */
#scr-marketing .pill.violet { --lc: color-mix(in srgb, var(--accent-2) 45%, var(--bad)) }
#scr-marketing .mk-pills { display: inline-flex; flex-wrap: wrap; gap: 6px; text-transform: none; letter-spacing: .04em }
#scr-marketing .mk-mono { font-variant-numeric: tabular-nums; white-space: nowrap }
#scr-marketing .mk-note { text-transform: none }
#scr-marketing .mk-note > .ico, #scr-marketing .mk-banner > .ico { vertical-align: -2px }
/* The rate-limit banner sits ABOVE the figures it is about, in the shared
   error line's shape in Rental Amber — a warning, not a refusal, which is what
   red means — with the figures still readable beneath it. */
#scr-marketing .mk-banner { margin-bottom: 12px; color: var(--warn); border-color: var(--warn) }
#scr-marketing #mk-meta { margin-top: 0 }

/* ── Offer parity, Lane K — expense categories on screen (DESIGN.md › Stage 2 —
   the shows › Expense categories on the show page) ─────────────────────────── */
/* The Internal tab's category subhead: a read-only line over its rows (never a
   row the table owns — `data-computed`), the category in the table head's own
   voice and its three subtotals in the columns they total, tabular. It does not
   take the rows' hover: nothing on it is editable. */
.sm-tbl tr.sm-cathead th, .sm-tbl tr.sm-cathead td { padding-top: 12px; border-bottom: 1px solid var(--border); background: transparent }
.sm-tbl tr.sm-cathead th { text-align: left; font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--text) }
.sm-tbl tr.sm-cathead td.num { font-size: 12.5px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums }
.data-table tbody tr.sm-cathead:hover { background: transparent }
/* The double-count warning: its row's second line, in Rental Amber — a warning,
   not a refusal, which is what red means — led by the drawn warn mark. */
.sm-tbl tr.sm-warnrow td { padding-top: 0; border-bottom: 1px solid var(--border) }
.data-table tbody tr.sm-warnrow:hover { background: transparent }
.sm-warn { display: flex; align-items: center; font-size: 12px; color: var(--warn) }
/* On a phone the Offer tab's Expenses table is wider than the screen and the
   warning's row spans all of it, so its sentence would run past the panel's
   edge until the table was scrolled. It is held to the width the phone shows
   and stays put while the table scrolls under it — the Mail screen's rule for
   its rows under a thread, the same 70px (review 2026-09-25, M6). */
@media (max-width: 899px) {
  .sm-tbl tr.sm-warnrow td > .sm-warn { position: sticky; left: 8px; max-width: calc(100vw - 70px) }
}
/* Show P&L: the Production figure is a disclosure (the house `.lnk`), its
   chevron turned to the side while closed; the category lines under the row
   are its children — muted, one step smaller, figures under the Production
   column they add up to. */
#scr-showpnl .spl-open { font-variant-numeric: tabular-nums; white-space: nowrap }
#scr-showpnl .spl-open .ico { transition: transform .15s; transform: rotate(-90deg) }
#scr-showpnl .spl-open[aria-expanded="true"] .ico { transform: none }
#scr-showpnl tr.spl-cat td { padding-top: 4px; padding-bottom: 4px; font-size: 12px; color: var(--muted); border-bottom: 1px solid var(--line-soft) }
#scr-showpnl tr.spl-cat td.num { font-variant-numeric: tabular-nums }
#scr-showpnl tbody tr.spl-cat:hover { background: transparent }
/* Settings › Venue › Expense categories: one line a category, the built-ins
   tagged, the venue's own with three icon buttons at the right end. */
#st-costcats .cc-list { margin: 8px 0 }
#st-costcats .cc-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--line-soft); font-size: 13px }
#st-costcats .cc-row:last-child { border-bottom: 0 }
#st-costcats .cc-name { flex: 1; min-width: 0; overflow-wrap: anywhere }
#st-costcats .cc-ctl { display: inline-flex; gap: 4px; flex: none }
#st-costcats .ib:disabled { opacity: .4; cursor: default }

/* ── Offer parity, Lane E ── the Show page's variable expenses and promoter profit.
   A box a line's kind leaves to the rate says so under itself, in the cell
   note's shape (web/show-sections.js rateCell). The desktop notice (§11) is the
   confirmation recipe's amber rule (DESIGN.md › Panels): a warning about where
   this show can be settled, not a refusal. */
.sm-tbl td .sm-byrate { margin-top: 2px; text-align: right }
.sm-parity-note { margin-bottom: 12px; padding: 10px 12px; border-left: 3px solid var(--warn); background: var(--panel-2); border-radius: var(--ctl-r); font-size: 13px }
.sm-parity-note[hidden] { display: none }

/* ── Menu screens, Lane S1 ── Menu items, the Item library and the one item
   editor both open (web/menu-items.js, web/menu-library.js, web/menu-edit.js).
   Matched to the approved concept boards Desk-Menu and Desk-Library (owner
   ruling 2026-10-01, "Match it exactly"). Tokens only. DESIGN.md › Menu items /
   Item library. */

/* Menu items: ONE toolbar row — tabs · ⋯ · new section · spacer · pill · green. */
.mi-bar, .ml-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px }
.mi-bar .grow, .ml-tools .grow { flex: 1 }
.mi-tabsbox { min-width: 0; max-width: 100% }
.mi-tabsbox .settabs { margin: 0 }
.mi-addsec { display: inline-flex; gap: 6px; align-items: center; min-width: 0 }
.mi-addsec input { width: 140px; max-width: 100% }
.mi-bar #mi-secopt { width: 32px; height: 32px; border-radius: var(--ctl-r); color: var(--text) }
.mi-bar #mi-secopt > .ico { width: 16px; height: 16px }
.mi-secmenu { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px }
.mi-secmenu[hidden] { display: none }
.mi-secmenu label { margin-bottom: 0; min-width: 200px }
.mi-secmenu .err { flex-basis: 100% }
.mi-secacts { display: flex; gap: 6px }
/* The list and the editor's 360 px column, always side by side on a laptop. */
.mi-body { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 12px; align-items: stretch }
.mi-body > .panel { margin-bottom: 0; min-width: 0 }
.mi-edit { display: flex; min-width: 0 }
.mi-edit > .me-panel { flex: 1 }
.mi-history { margin-top: 12px }
.mi-history summary { cursor: pointer; font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700 }
.mi-hist { list-style: none; margin: 9px 0 0; padding: 0; font-size: 12.5px; color: var(--text) }
.mi-hist li { padding: 5px 0; border-bottom: 1px solid var(--line-soft) }

/* One row per placement, on the board's fixed grid: grip, logo, name over
   maker · style, ABV, prices, and the Status cell (up, down, Pouring / 86'd). */
.menu-head, .menu-row { display: grid; grid-template-columns: 22px 40px minmax(0, 1fr) 56px 150px 176px; gap: 10px; align-items: center }
.menu-head { padding: 0 6px 6px; font-size: 11px; color: var(--muted-2); letter-spacing: .04em; border-bottom: 1px solid var(--line-soft) }
.menu-list { list-style: none; margin: 0; padding: 0 }
.menu-list > .empty { padding: 10px 6px }
.menu-row { padding: 8px 6px; border-bottom: 1px solid var(--line-soft); border-radius: 8px; cursor: pointer }
.menu-row.sel { background: var(--panel-2) }
.menu-row[draggable="true"] .menu-grip { cursor: grab }
.menu-grip, .me-pour .menu-grip { justify-self: center; width: 10px; height: 16px; flex: none; color: var(--muted-2);
  background-image: radial-gradient(currentColor 1.2px, transparent 1.5px); background-size: 5px 5px; background-position: 0 0 }
.menu-logo { width: 36px; height: 36px; flex: none; box-sizing: border-box; border-radius: 8px; object-fit: contain; background: var(--panel-2); border: 1px solid var(--border) }
.menu-logo.none { display: inline-flex; align-items: center; justify-content: center; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; font-weight: 700; color: var(--muted) }
.menu-what { display: flex; flex-direction: column; gap: 1px; min-width: 0 }
.menu-name { background: none; border: 0; padding: 0; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--text); text-align: left; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100% }
.menu-name:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
.menu-meta { display: block; font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.menu-abv { font-size: 12.5px; font-weight: 600; color: var(--text) }
.menu-prices { font-size: 12px; color: var(--muted); min-width: 0 }
.menu-pour { display: block }
.menu-status { display: flex; gap: 6px; align-items: center }
.menu-status .ib { width: 26px; height: 26px; border-radius: var(--ctl-r) }
.menu-status .ib > .ico { width: 12px; height: 12px }
.menu-86 { width: 78px; padding: var(--ctl-pad-c) }
.menu-86[aria-pressed="true"] { border-color: var(--bad); color: var(--bad) }
/* An 86'd row, as drawn: its name and meta dimmed, its toggle in Refusal Red
   saying 86'd in words. It stays on the list — staff still see it; the TVs drop it. */
.menu-row.out .menu-what { opacity: .45 }
/* A header row (lane items2, owner ruling 2026-10-03 "A smaller sub-heading"): the
   grip, a HEADER tag where the logo sits, its words in a box across the name,
   ABV and price cells, and up/down, Save, Delete in the Status cell. */
.mi-sub { cursor: default }
.mi-sub-tag { font-size: 10px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700; color: var(--muted) }
.mi-sub-what { grid-column: 3 / 6; min-width: 0 }
.mi-sub-what input { width: 100%; font-weight: 700 }
/* Delete arms by putting its question on the button ("Delete the header …?"); in the
   176px Status cell that ran ~200px wide, over the editor's Type box, so the second
   click landed on the select and the header never went (nightly e2e red 2026-10-04..08).
   The armed button takes the cell's full width and wraps inside it. */
.mi-sub .menu-status { flex-wrap: wrap; min-width: 0 }
.mi-sub .menu-status .btn[data-armed="1"] { flex: 1 1 100%; min-width: 0; white-space: normal; text-align: left }
.mi-subform { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; margin-bottom: 8px }
.mi-subform[hidden] { display: none }
.mi-subform label { margin-bottom: 0; min-width: 160px }
.mi-subform .err { flex-basis: 100% }
/* The Pour sizes fold (lane items2, owner ruling 2026-10-03 "A managed list of sizes
   with default prices"): name · regular · show night · up, down, Save, Delete. */
.mi-sizes { margin-top: 12px }
.mi-sizes summary { cursor: pointer; font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700 }
.mi-sizes-list { list-style: none; margin: 0; padding: 0 }
.mi-size { display: grid; grid-template-columns: minmax(0, 1fr) 96px 96px auto; gap: 8px; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--line-soft) }
.mi-size input { min-width: 0; width: 100% }
.mi-size-head { margin-top: 9px; font-size: 11px; color: var(--muted-2) }
.mi-size-acts { display: flex; gap: 6px; align-items: center }
.mi-size-acts .ib { width: 26px; height: 26px; border-radius: var(--ctl-r) }
.mi-size-acts .ib > .ico { width: 12px; height: 12px }
.mi-sizes .err:empty { display: none }
.me-addsize { align-self: flex-start; max-width: 100% }

/* The editor panel: the board's column, top to bottom, Save at the foot. */
.me-panel { display: flex; flex-direction: column; gap: 10px; margin-bottom: 0; min-width: 0 }
.me-panel > .panel-title { margin-bottom: 0 }
.me-panel label, .me-panel .me-two label { margin-bottom: 0 }
.me-panel label input, .me-panel label select, .me-panel label textarea { max-width: none }
.me-namerow { display: flex; gap: 10px; align-items: flex-end }
.me-namerow > label { flex: 1; min-width: 0 }
.me-logo { display: contents }
.me-logo-btn { width: 56px; height: 56px; flex: none; padding: 0; border-radius: var(--ctl-r); border: 1px dashed var(--muted-2); background: var(--panel-2);
  color: var(--muted); font: inherit; font-size: 10px; font-weight: 600; cursor: pointer; overflow: hidden }
.me-logo-btn.has { border-style: solid; border-color: var(--border) }
.me-logo-btn .menu-logo { width: 100%; height: 100%; border: 0; border-radius: 0 }
.me-logo-btn:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 1px }
.me-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px }
.me-typeabv { grid-template-columns: minmax(0, 1.3fr) minmax(0, .7fr) }
.me-secs { border: 0; margin: 0; padding: 0; min-width: 0 }
.me-secs legend { padding: 0; margin-bottom: 4px; font-size: 11px; letter-spacing: .04em; color: var(--muted) }
.me-ticks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 8px }
.me-ticks label.chk { min-height: 24px; gap: 8px; font-size: 13px; letter-spacing: 0 }
/* Pours picked per section (lane pours, owner ruling 3 Oct 2026): one small,
   indented, muted line per ticked section under the ticks — its name, then a tick
   per pour. Wraps on a narrow column; never wider than the editor. */
.me-secpours { display: flex; flex-direction: column; gap: 2px; margin-top: 4px }
.me-secpours:empty { display: none }
.me-secpour { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; padding-left: 14px; font-size: 11px; color: var(--muted); min-width: 0 }
.me-secpour-name { font-weight: 600; letter-spacing: .02em }
.me-secpour-name::after { content: ':' }
.me-secpour label.chk.me-pchip { min-height: 22px; gap: 4px; font-size: 11px; color: var(--muted) }
.me-secpour-warn { flex-basis: 100%; color: var(--warn); font-size: 11px }
.me-panel textarea { height: 54px; resize: none }
.me-pourbox { display: flex; flex-direction: column; gap: 4px }
.me-pours { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px }
/* A pour row: three boxes, the small up/down pair (two 20 px arrows, 2 px apart),
   and the 30 px ×. */
.me-pour { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr) 42px 30px; gap: 6px; align-items: center }
.me-pour input { min-width: 0; width: 100% }
.me-pour .ib { width: 30px; height: 30px; border-radius: var(--ctl-r) }
.me-pour .ib > .ico { width: 12px; height: 12px }
.me-pour-move { display: flex; gap: 2px }
.me-pour .me-pour-move .ib { width: 20px; padding: 0 }
.me-pour .me-pour-move .ib > .ico { width: 11px; height: 11px }
.me-pour .me-pour-move .ib:disabled { opacity: .4; cursor: default }
.me-ph { font-size: 11px; letter-spacing: .04em; color: var(--muted) }
.me-addpour { align-self: flex-start; border: 0; background: none; color: var(--text); font: inherit; font-size: var(--ctl-fs); font-weight: 600; padding: 4px 0; cursor: pointer }
.me-addpour:hover { color: var(--accent-2) }
.me-addpour:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
.me-hint { margin-top: 0 }
.me-actions { margin-top: auto; display: flex; flex-wrap: wrap; gap: 8px }
.me-actions #me-save { flex: 1 1 120px }
.me-actions [data-me-act="takeall"] { margin-left: auto }
.me-panel .err { margin: 0 }

/* The library: a bare tool row, then the table under the board's plain heads. */
.ml-tools input[type="search"] { width: 340px; max-width: 100% }
.ml-tools #ml-filterbox { min-width: 0; max-width: 100% }
.ml-tools .settabs { margin: 0 }
.ml-div { width: 1px; height: 24px; background: var(--border); flex: none }
.ml-typesel { display: inline-flex }
.ml-typesel select { width: auto; font-weight: 600; cursor: pointer }
.ml-typesbtn { display: inline-flex; align-items: center; gap: 6px }
/* The floor (940px since the look walk of 2026-10-26 — lane C block at the foot)
   leaves the Item column ~150px; narrower, the table scrolls sideways
   inside its .tbl-wrap (at 900px the fixed columns left Item 71px — names cut). */
.ml-table { table-layout: fixed; min-width: 1100px }
.ml-table th { font-size: 11px; font-weight: 400; letter-spacing: .04em; text-transform: none; color: var(--muted-2); padding: 0 6px 6px; border-bottom: 1px solid var(--line-soft) }
/* The heads sort (the house table rule) but look as drawn: no mark until one is the sorted column. */
.ml-table .th-sort[aria-sort="none"] .s { display: none }
.ml-table th:nth-child(1) { width: 52px }
.ml-table th:nth-child(3) { width: 102px }
.ml-table th:nth-child(4) { width: 15% }
.ml-table th:nth-child(5) { width: 122px }
.ml-table th:nth-child(6) { width: 182px }
.ml-table th:nth-child(7) { width: 236px }
.ml-table td { padding: 8px 6px; border-bottom: 1px solid var(--line-soft); font-size: 12px }
.ml-table td.ml-logo .menu-logo { display: flex }
.ml-table .ml-name { display: block; font-size: 12.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.ml-table .menu-meta { white-space: normal }
.ml-table td.ml-maker { white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.ml-table tr.sel { background: var(--panel-2) }
.ml-table td.ml-prices { color: var(--muted) }
.ml-table td.ml-acts { text-align: right }
.ml-table td.ml-acts .btn { padding: var(--ctl-pad-c) }
.ml-nosec { font-size: 12px; color: var(--muted) }
.ml-types[hidden] { display: none }
.ml-typelist { list-style: none; margin: 9px 0; padding: 0 }
.ml-typelist li { display: flex; align-items: center; gap: 6px; padding: 3px 0 }
.ml-typelist input { width: 200px; max-width: 100% }
.ml-newtype { display: flex; align-items: flex-end; gap: 6px }
.ml-newtype label { margin-bottom: 0 }
/* The editor on the library: a drawer over the page's right edge — the board
   draws no editor here, so it covers the list rather than reflowing it. Under
   the toast (z-index 5), so a toast is never hidden behind it. */
.ml-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: 360px; max-width: 100vw; z-index: 4; box-sizing: border-box; overflow-y: auto;
  padding: 12px; background: var(--bg); border-left: 1px solid var(--border); box-shadow: 0 18px 48px rgba(0, 0, 0, .55) }
.ml-drawer[hidden] { display: none }
.ml-drawer > .me-panel { min-height: 100%; box-sizing: border-box }

/* On a laptop the list panels run to the foot of the window, as the boards draw
   them (the page name, sub-line and tool row above take 126px; the main column's
   foot padding 30px). Change history sits under Menu items' list, below the fold. */
@media (min-width: 900px) {
  .mi-body { min-height: calc(100vh - 156px) }
  .ml-listpanel { min-height: calc(100vh - 156px); box-sizing: border-box; margin-bottom: 0 }
}
@media (max-width: 899px) {
  .mi-body { grid-template-columns: minmax(0, 1fr) }
  .mi-addsec { flex: 1 1 100% }
  .mi-addsec input { flex: 1; width: auto }
  .menu-86 { width: auto }
  .mi-size { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) }
  .mi-size > input[data-sz="label"], .mi-size > #mi-sznew-label, .mi-size > .mi-size-acts { grid-column: 1 / -1 }
  .mi-size-head { display: none }
}
/* A narrow list: grip, logo, name, Status on top; ABV and prices under the name.
   Measured on the list panel itself, not the window — on an iPad turned sideways
   (1180px) the editor beside it left the one-line row's name cell 7px wide, so
   every item name showed as one letter (Chris, 2026-10-03). Below 600px the row
   stacks, on a phone and on a tablet alike; a 1280px laptop (615px) keeps one line. */
.mi-listpanel { container-type: inline-size }
@container (max-width: 599px) {
  .menu-head { display: none }
  .menu-row { grid-template-columns: 12px 36px minmax(0, 1fr) auto; gap: 4px 8px }
  .menu-row > .menu-grip { grid-row: 1 / span 2 }
  .menu-row > .menu-logo { grid-row: 1 / span 2; width: 34px; height: 34px }
  .menu-row > .menu-what { grid-column: 3; grid-row: 1 }
  .menu-row > .menu-status { grid-column: 4; grid-row: 1 }
  .menu-row > .menu-abv { grid-column: 3; grid-row: 2; font-size: 12px }
  .menu-row > .menu-prices { grid-column: 3 / span 2; grid-row: 3 }
  .menu-row > .mi-sub-what { grid-column: 3; grid-row: 1 }
  .menu-row.mi-sub > .menu-status { grid-column: 3 / span 2; grid-row: 2 }
}
/* ── Menu screens, Lane S2 ── Menu screens and Menu layouts (DESIGN.md › Menu
   screens — Menu screens; Menu layouts). Rebuilt to the approved boards
   (Desk-Screens, Desk-Layouts) by Lane C2, 2026-10-01: "Match it exactly". ── */
/* THE PREVIEW: a box the TV renderer draws into (web/menu-tv/tv-render.js
   `fit` + `draw`, the file the TVs run). The canvas inside it is scaled to fit
   the box by `--k`, letterboxed on the box's own dark ground. Its default shape
   is the TV's — 16:9 for a Wide layout, 9:16 for a Tall one, drawn upright as
   the room sees it; the two screens below size it as their boards draw it. */
.mn-prev { position: relative; overflow: hidden; width: 100%; aspect-ratio: 16 / 9; background: var(--bg); border: 1px solid var(--border); border-radius: var(--ctl-r) }
.mn-prev.tall { aspect-ratio: 9 / 16; width: 42%; margin: 0 auto }

/* Menu screens: the price row on top; the TVs panel over Upcoming shows on the
   left, the 360px TV panel on the right, both as tall as the window allows. */
#scr-menuscreens .ms-price { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 12px }
#scr-menuscreens .ms-price-lab { font-size: 11px; letter-spacing: .04em; color: var(--muted) }
#scr-menuscreens .ms-price .settabs { margin: 0 }
#scr-menuscreens .ms-when { font-size: var(--note-fs); color: var(--muted-2); letter-spacing: .04em }
#scr-menuscreens .ms-price .grow { flex: 1 }
#scr-menuscreens .ms-grid { display: flex; gap: 12px; align-items: stretch; min-height: calc(100vh - 160px) }
#scr-menuscreens .ms-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 12px }
#scr-menuscreens .ms-main > .panel { margin: 0 }
#scr-menuscreens .ms-tvs { flex: 1 0 auto; display: flex; flex-direction: column }
/* Three across, as drawn, while a third of the panel is at least 220px; two,
   then one, as the window narrows — no breakpoint of its own. */
#scr-menuscreens .ms-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(max(220px, calc((100% - 20px) / 3)), 1fr)); grid-auto-rows: min-content; gap: 10px }
#scr-menuscreens .ms-card { display: flex; flex-direction: column; gap: 7px; min-width: 0; padding: 8px 10px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px; cursor: pointer }
#scr-menuscreens .ms-card:hover { border-color: var(--muted-2) }
#scr-menuscreens .ms-card.open { border-color: var(--accent-2) }
#scr-menuscreens .ms-card .mn-prev { aspect-ratio: auto; height: 74px; width: 100%; margin: 0; align-self: center; border-radius: 6px }
#scr-menuscreens .ms-card .mn-prev.tall { width: 66px }
#scr-menuscreens .ms-card-body { display: flex; justify-content: space-between; align-items: center; gap: 8px }
#scr-menuscreens .ms-card-body .pill { flex: none }
#scr-menuscreens .ms-card-what { display: flex; flex-direction: column; gap: 1px; min-width: 0 }
#scr-menuscreens .ms-card-name { padding: 0; border: 0; background: none; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--text); text-align: left; cursor: pointer; overflow-wrap: anywhere }
#scr-menuscreens .ms-card-name:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
#scr-menuscreens .ms-card-lay { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
#scr-menuscreens .ms-card-meta { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
/* Lane nocode (2026-10-03): a TV set up before its code — the card's Pair this TV, and the panel's code box with Pair beside it. */
#scr-menuscreens .ms-card-pair { align-self: flex-start; padding: 3px 9px; font-size: 11.5px }
#scr-menuscreens .ms-pair-row { display: flex; align-items: flex-end; gap: 8px }
#scr-menuscreens .ms-pair-row label { flex: 1 1 auto; min-width: 0 }
/* The dashed tile, last in the grid: Signal Cyan, the thing to do next. */
#scr-menuscreens .ms-addtile { min-height: 140px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 8px 10px;
  background: transparent; border: 1px dashed var(--accent-2); border-radius: 10px; color: var(--accent-2); font: inherit; cursor: pointer }
#scr-menuscreens .ms-addtile:hover, #scr-menuscreens .ms-addtile.on { background: var(--panel-2) }
#scr-menuscreens .ms-addtile:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
#scr-menuscreens .ms-addtile .ico { width: 22px; height: 22px; margin: 0 }
#scr-menuscreens .ms-addtile-name { font-size: 13px; font-weight: 600 }
#scr-menuscreens .ms-addtile-line { font-size: 11px; color: var(--muted); text-align: center; padding: 0 10px }
/* TV BRAND, the TVs panel's foot: its words, then two dashed drop zones. */
#scr-menuscreens .ms-brand { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--line-soft); display: flex; flex-wrap: wrap; align-items: center; gap: 12px }
#scr-menuscreens .ms-tvs .ms-cards { margin-bottom: 12px }
#scr-menuscreens .ms-brand-what { display: flex; flex-direction: column; gap: 2px }
#scr-menuscreens .ms-brand-title { font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700 }
#scr-menuscreens .ms-brand-line { font-size: 11px; color: var(--muted-2) }
#scr-menuscreens .ms-slot { display: flex; align-items: center; gap: 6px }
#scr-menuscreens .ms-drop { height: 44px; min-width: 190px; display: inline-flex; align-items: center; justify-content: center; padding: 0 12px; border-radius: var(--ctl-r);
  border: 1px dashed var(--muted-2); background: var(--panel-2); color: var(--muted); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer }
#scr-menuscreens .ms-drop:hover, #scr-menuscreens .ms-drop.over { border-color: var(--accent-2); color: var(--text) }
#scr-menuscreens .ms-drop:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
/* A held picture is judged on the TV's own ground (the --tv-swatch token). */
#scr-menuscreens .ms-drop.has { background: var(--tv-swatch); padding: 4px 12px }
#scr-menuscreens .ms-drop.has img { max-height: 34px; max-width: 166px; object-fit: contain }
#scr-menuscreens .ms-slot-clear { padding: var(--ctl-pad-c) }
/* The QR code's website (lane qr): its own line under the two pictures. */
#scr-menuscreens .ms-site { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px }
#scr-menuscreens .ms-site label { flex: 0 1 320px; min-width: 0 }
#scr-menuscreens .ms-site .ms-brand-line { flex-basis: 100% }
#scr-menuscreens .ms-brand .err { flex-basis: 100% }
#scr-menuscreens .ms-brand .err:empty { display: none }
/* Upcoming shows: night · name · Talent/Rental · the tick, one grid per row. */
#scr-menuscreens .ms-shows { flex: none }
#scr-menuscreens .ms-shows .panel-title { margin-bottom: 6px }
#scr-menuscreens .ms-show { display: grid; grid-template-columns: 110px minmax(0, 1fr) 90px 150px; gap: 12px; align-items: center; padding: 5px 6px; border-bottom: 1px solid var(--line-soft) }
#scr-menuscreens .ms-night { font-size: 12px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums }
#scr-menuscreens .ms-show-name { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
#scr-menuscreens .ms-show.hid .ms-show-name { color: var(--muted-2) }
#scr-menuscreens .ms-show .pill { justify-self: start }
#scr-menuscreens .ms-show label.chk { justify-self: end; gap: 7px; cursor: pointer }
#scr-menuscreens .ms-shows .muted-note { margin-top: 7px }
/* The TV panel: one column of fields, the raised note, Save and Remove at its foot. */
#scr-menuscreens .ms-side { width: 360px; flex: none; box-sizing: border-box; margin: 0; display: flex; flex-direction: column; gap: 10px }
#scr-menuscreens .ms-side .panel-title { margin: 0 }
#scr-menuscreens .ms-side .panel-title .btn { letter-spacing: 0; text-transform: none; padding: 4px 8px }
#scr-menuscreens .ms-side label { margin: 0 }
#scr-menuscreens .ms-side label input, #scr-menuscreens .ms-side label select { max-width: none }
#scr-menuscreens .ms-field { display: flex; flex-direction: column; gap: 4px }
#scr-menuscreens .ms-lab { font-size: 11px; letter-spacing: .04em; color: var(--muted) }
#scr-menuscreens .ms-link { margin-top: 2px; font-size: var(--ctl-fs); font-weight: 600; color: var(--accent-2); text-decoration: none }
#scr-menuscreens .ms-link:hover { text-decoration: underline }
#scr-menuscreens .ms-turn { margin: 0 }
#scr-menuscreens .ms-turn button { flex: 1 1 0 }
#scr-menuscreens .ms-hint { font-size: var(--note-fs); color: var(--muted-2); letter-spacing: .04em }
#scr-menuscreens .ms-note { padding: 10px 12px; border-radius: var(--ctl-r); background: var(--panel-2); border: 1px solid var(--border); font-size: 12px; color: var(--muted); line-height: 1.5 }
#scr-menuscreens .ms-side .muted-note, #scr-menuscreens .ms-side .err { margin: 0 }
#scr-menuscreens .ms-side .err:empty { display: none }
/* The foot wraps: on a phone (16px control text) the saved receipt "✓ Saved · 4:12 PM"
   beside "Remove this TV" is wider than the panel, and an unwrapped row pushed
   .main sideways (gate 36928583304, Linux fonts). On a desktop both fit on one line. */
#scr-menuscreens .ms-foot { margin-top: auto; display: flex; flex-wrap: wrap; gap: 8px }
#scr-menuscreens .ms-foot .btn.primary { flex: 1 1 auto }

/* Menu layouts: the 300px list, then the editor with its fixed preview. */
#scr-menulayouts .ml-grid { display: flex; gap: 12px; align-items: stretch; min-height: calc(100vh - 116px) }
#scr-menulayouts .ml-grid > .panel { margin: 0; box-sizing: border-box }
#scr-menulayouts .ml-list { width: 300px; flex: none; display: flex; flex-direction: column; gap: 10px }
#scr-menulayouts .ml-list .panel-title { margin: 0 }
#scr-menulayouts .ml-listacts { display: flex; flex-wrap: wrap; gap: 6px }
#scr-menulayouts .ml-listacts .btn { flex: 1 1 auto; padding: 7px 10px }
#scr-menulayouts .ml-rows { display: flex; flex-direction: column; gap: 2px }
#scr-menulayouts .ml-row { display: flex; flex-direction: column; gap: 1px; width: 100%; padding: 7px 10px; background: transparent; border: 0; border-radius: var(--ctl-r); font: inherit; text-align: left; cursor: pointer }
#scr-menulayouts .ml-row:hover { background: var(--panel-2) }
#scr-menulayouts .ml-row.active { background: var(--panel-2); box-shadow: inset 2px 0 0 var(--accent-2) }
#scr-menulayouts .ml-row:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 1px }
#scr-menulayouts .ml-row-name { font-size: 12.5px; font-weight: 600; color: var(--muted); overflow-wrap: anywhere }
#scr-menulayouts .ml-row.active .ml-row-name { color: var(--text) }
#scr-menulayouts .ml-row-used { font-size: 11px; color: var(--muted-2) }
/* The Measured Muted Rule: an idle row sits on Desk Navy, where Dim Slate is
   4.65:1; a hovered or picked row is Raised Navy, where Dim Slate is 4.17:1, so
   the "On TV …" line steps up to Slate Lavender (5.01:1) there. Held by
   test/unit/style-gate.test.js (12h). */
#scr-menulayouts .ml-row:hover .ml-row-used, #scr-menulayouts .ml-row.active .ml-row-used { color: var(--muted) }
#scr-menulayouts .ml-editor { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 12px }
#scr-menulayouts .ml-edhead { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 12px }
#scr-menulayouts .ml-edhead .panel-title, #scr-menulayouts .ml-editor > .panel-title { margin: 0 }
#scr-menulayouts .ml-where { font-size: var(--note-fs); color: var(--muted-2); letter-spacing: .04em }
/* The fields and the fixed preview side by side while the fields keep 360px;
   on a narrower window the preview wraps under them — no breakpoint of its own. */
#scr-menulayouts .ml-edit { display: flex; flex-wrap: wrap; gap: 18px; align-items: flex-start }
#scr-menulayouts .ml-fields { flex: 1 1 360px; min-width: 0; display: flex; flex-direction: column; gap: 12px }
#scr-menulayouts .ml-fields label { margin: 0 }
#scr-menulayouts .ml-fields label input { max-width: none }
#scr-menulayouts .ml-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: end }
#scr-menulayouts .ml-field { display: flex; flex-direction: column; gap: 4px; min-width: 0 }
#scr-menulayouts .ml-lab { font-size: 11px; letter-spacing: .04em; color: var(--muted) }
#scr-menulayouts .ml-seg { margin: 0 }
#scr-menulayouts .ml-seg button { flex: 1 1 0 }
/* "Shows, in this order": a ticked list; the ticked rows drag, and their
   up/down pair (the keyboard's way) shows on hover or focus — always on a
   touch screen, which has no hover. */
#scr-menulayouts .ml-shows { display: flex; flex-direction: column }
#scr-menulayouts .ml-show { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; min-height: 30px; border-radius: 6px }
#scr-menulayouts .ml-show.on { cursor: grab }
#scr-menulayouts .ml-show label.chk { flex: 1 1 auto; min-width: 0; gap: 10px; font-size: 13px; color: var(--text); cursor: pointer }
#scr-menulayouts .ml-show input[type="checkbox"] { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--accent-2) }
#scr-menulayouts .ml-show-name { min-width: 0; overflow-wrap: anywhere }
#scr-menulayouts .ml-photos-btn { flex: none; padding: 4px 10px }
/* The door tick (2 Oct 2026): a small indented line under the Upcoming shows row,
   on a line of its own below the row's buttons; dimmed while the row is unticked. */
#scr-menulayouts .ml-show label.chk.ml-show-sub { order: 1; flex: 1 0 100%; padding: 0 0 4px 26px; gap: 8px; font-size: 12px; color: var(--muted) }
#scr-menulayouts .ml-show .ml-show-sub input[type="checkbox"] { width: 14px; height: 14px }
#scr-menulayouts .ml-show:not(.on) .ml-show-sub { opacity: .55; cursor: default }
/* Its two modes (3 Oct 2026): two smaller lines under the tick, indented to the
   tick's text (26px + the 14px box + its 8px gap); dimmed while the tick is off. */
#scr-menulayouts .ml-show .ml-show-modes { order: 1; flex: 1 0 100%; display: flex; flex-direction: column; gap: 2px; padding: 0 0 4px 48px }
#scr-menulayouts .ml-show .ml-show-modes label.chk { flex: none; gap: 8px; font-size: 12px; color: var(--muted) }
#scr-menulayouts .ml-show .ml-show-modes input[type="radio"] { width: 14px; height: 14px; margin: 0; flex: none; accent-color: var(--accent-2) }
#scr-menulayouts .ml-show .ml-show-modes.off { opacity: .55 }
#scr-menulayouts .ml-show .ml-show-modes.off label.chk { cursor: default }
#scr-menulayouts .ml-show .ml-ctl { display: inline-flex; gap: 3px; flex: none; opacity: 0 }
#scr-menulayouts .ml-show:hover .ml-ctl, #scr-menulayouts .ml-show:focus-within .ml-ctl { opacity: 1 }
@media (hover: none) { #scr-menulayouts .ml-show .ml-ctl { opacity: 1 } }
/* The drop zones: the background's, and the photo dialog's. */
#scr-menulayouts .ml-bgrow { display: flex; align-items: center; gap: 8px }
#scr-menulayouts .ml-drop { flex: 1 1 auto; min-width: 0; height: 48px; display: flex; align-items: center; justify-content: center; gap: 10px; padding: 0 12px;
  border-radius: var(--ctl-r); border: 1px dashed var(--muted-2); background: var(--panel-2); color: var(--muted); font: inherit; font-size: 12px; cursor: pointer }
#scr-menulayouts .ml-drop:hover, #scr-menulayouts .ml-drop.over { border-color: var(--accent-2); color: var(--text) }
#scr-menulayouts .ml-drop:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px }
#scr-menulayouts .ml-drop img { width: 64px; height: 36px; object-fit: cover; border-radius: 4px; background: var(--bg) }
#scr-menulayouts .ml-bgrow .btn { flex: none }
/* The preview, at the board's fixed size; the frame arrows sit small under it. */
#scr-menulayouts .ml-prevcol { width: 420px; flex: none; display: flex; flex-direction: column; gap: 4px }
#scr-menulayouts #ml-prev { width: 420px; height: 236px; aspect-ratio: auto; margin: 0; box-sizing: border-box }
#scr-menulayouts .ml-prevcol .muted-note { margin: 0; line-height: 1.5 }
#scr-menulayouts .ml-pager { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted-2); font-variant-numeric: tabular-nums }
#scr-menulayouts .ml-editor > .muted-note, #scr-menulayouts .ml-editor > .err { margin: 0 }
#scr-menulayouts .ml-editor > .err:empty { display: none }
#scr-menulayouts .ml-foot { margin-top: auto; display: flex; flex-wrap: wrap; gap: 8px }
#scr-menulayouts .ml-foot .btn.primary { width: 200px }
/* The photo dialog: the house popup (`.imp-overlay` / `.imp-popup`). */
#scr-menulayouts .ml-dlg .imp-popup { width: min(640px, 92vw); display: flex; flex-direction: column; gap: 10px }
#scr-menulayouts .ml-dlg .imp-popup:focus { outline: none }
#scr-menulayouts .ml-dlg .panel-title { margin: 0 }
#scr-menulayouts .ml-dlg .panel-title .btn { margin-left: auto; flex: none; padding: 2px 9px; line-height: 1.2 }
#scr-menulayouts .ml-dlg .muted-note { margin: 0 }
#scr-menulayouts .ml-dlg .ml-drop { flex: none }
#scr-menulayouts .ml-acts { display: flex; justify-content: flex-end; gap: 8px }
#scr-menulayouts .ml-photo { display: grid; grid-template-columns: auto 64px minmax(0, 1fr) auto; align-items: center; gap: 6px 8px; padding: 6px 8px; margin-bottom: 6px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--ctl-r); font-size: 13px; cursor: grab }
#scr-menulayouts .ml-photo-name { min-width: 0; overflow-wrap: anywhere }
#scr-menulayouts .ml-grip { flex: none; width: 6px; height: 14px; border-left: 2px dotted var(--muted-2); border-right: 2px dotted var(--muted-2) }
#scr-menulayouts .ml-photo img, #scr-menulayouts .ml-thumb { width: 64px; height: 36px; object-fit: cover; background: var(--bg); border-radius: 4px }
#scr-menulayouts .ml-photo .ml-ctl { display: inline-flex; gap: 4px; margin-left: auto }
#scr-menulayouts .ml-dates { grid-column: 2 / -1; display: flex; flex-wrap: wrap; gap: 8px }
#scr-menulayouts .ml-dates label { flex: 1 1 130px; margin: 0 }

@media (max-width: 899px) {
  #scr-menuscreens .ms-grid, #scr-menulayouts .ml-grid { flex-direction: column; min-height: 0 }
  #scr-menuscreens .ms-side, #scr-menulayouts .ml-list { width: auto }
  #scr-menuscreens .ms-show { grid-template-columns: 76px minmax(0, 1fr) auto; row-gap: 4px }
  #scr-menuscreens .ms-show label.chk { grid-column: 2 / -1; justify-self: start }
  #scr-menuscreens .ms-drop { min-width: 0; flex: 1 1 auto }
  #scr-menuscreens .ms-slot { flex: 1 1 100% }
  #scr-menulayouts .ml-two { grid-template-columns: minmax(0, 1fr) }
  #scr-menulayouts .ml-prevcol { width: 100% }
  #scr-menulayouts #ml-prev { width: 100%; height: auto; aspect-ratio: 16 / 9 }
}

/* Totals INSIDE their table's panel (Chris, 2026-10-14: the Actuals total in
   "the same box as ticket actuals", the Expenses resolved in "the same box as
   expenses internal"): a block under the save bar, set off by a rule, with a
   sub-title in the panel title's own letter. */
.sm-summary.sm-inbox { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line-soft) }
.sm-subtitle { font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); font-weight: 700; margin-bottom: 4px; display: flex; align-items: center; gap: 8px }
/* The artist payout row in the internal expenses table (above the first
   category): read-only figures, the row's name in the table's text colour. */
.sm-tbl tr.sm-payout th { text-align: left; font-weight: 600; color: var(--text) }
.sm-tbl tr.sm-payout td.num { font-weight: 600; font-variant-numeric: tabular-nums }
.sm-tbl tr.sm-payout th, .sm-tbl tr.sm-payout td { border-bottom: 1px solid var(--line-soft) }

/* ── Run Sheet (slice 1) ─────────────────────────────────────────────────────
   The show page's second face, behind Settings › This browser › Show page
   (web/runsheet.js; Chris, 2026-10-16: "Concept B"). Mission Control's own
   sizes and tokens (his Q-MC4: "Mission Control's sizes"): the layout is the
   change. Every colour is a token; nothing here applies on Classic, because
   every selector starts at a Run Sheet class. */
.rs-crumb { display: flex; align-items: baseline; gap: 6px; margin: 0 0 6px }
.rs-crumb .show-back { margin: 0 }
.rs-month { font-size: 12px; color: var(--muted) }
.rs-head .rs-top { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: flex-end; justify-content: space-between }
.rs-head .rs-who { min-width: 0 }
.rs-head .show-meta { margin-bottom: 0 }
.rs-head .rs-acts { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; margin-left: auto }
.rs-head .rs-row { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; align-items: center }
.rs-head .sh-holds, .rs-head #sh-confirm-day { margin-top: 8px }
.rs-with { color: var(--muted) }

/* The step strip: today's tab strip drawn as five cards. */
.show-tabs.rs-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 8px; overflow: visible }
.rs-steps button {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; white-space: normal; text-align: left;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; color: var(--text)
}
.rs-steps button.active { background: var(--panel-2); border-color: transparent; box-shadow: inset 0 0 0 2px var(--accent-2) }
.rs-step-n { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted-2); font-weight: 700 }
/* The active card is Raised Navy: Dim Slate is 4.17:1 there, so its eyebrow
   steps up to Slate Lavender (the Measured Muted Rule; critique 2026-10-19). */
.rs-steps button.active .rs-step-n { color: var(--muted) }
.rs-step-t { font-size: 14px; font-weight: 650 }
.rs-step-s { font-size: 12px; font-weight: 400; color: var(--muted) }
.rs-step-s.ok { color: var(--accent) }
.rs-step-s.settled { color: var(--accent-2) }

/* The column and the rail. The rail stays in view inside the app's one
   scroller (.main) and scrolls itself when it is taller than the window. */
.rs-body { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 14px; align-items: start }
.rs-col { min-width: 0 }
.rs-rail { position: sticky; top: 0; display: flex; flex-direction: column; gap: 12px; max-height: calc(100vh - 44px); overflow-y: auto; scrollbar-width: thin }
.rs-rail .panel { margin-bottom: 0 }
/* The big figure: the house Tile figure (.tile-value), measured in its own box. */
.rs-hero { container-type: inline-size; padding-bottom: 8px; margin-bottom: 4px; border-bottom: 1px solid var(--border) }
/* The Internal step's settlement switch sits above the big figure; its label
   had touched the switch (Chris, 2026-10-26: "The Potential/Estimated/Actual
   Boxes ar sitting right ontop of the words Net-The House Keeps"). */
.sm-basis + .rs-hero { margin-top: 10px }
.rs-hero-l { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted) }
.rs-hero-s { font-size: 12px; color: var(--muted) }
.rs-hero.rental .rs-hero-l { color: var(--warn) }
.rs-hero .tile-value.neg { color: var(--bad) }
/* No deal yet (critique 2026-10-19, P3-10): the label and one quiet line where
   the giant $0.00 stood — the line in the body size, so it reads as words. */
.rs-hero.rs-hero-none .rs-hero-s { margin-top: 4px; font-size: 13px }
/* The documents card. */
.rs-docs .rs-go, .rs-docs .rs-alt { display: block; width: 100%; text-align: center }
.rs-docs .rs-go { font-size: 14px; padding: 10px 14px }
.rs-docs .rs-alt { margin-top: 6px }
.rs-sent { font-size: 12px; color: var(--accent); margin-bottom: 8px }
.rs-docrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 7px 0; border-top: 1px solid var(--border) }
.rs-docrow:first-of-type { border-top: 0 }
.rs-docrow b { flex: 1 1 140px; font-size: 12px }
.rs-docrow .rs-sent { margin: 0; flex: 1 0 100% }
/* The booking questions row (0057, 0058): a document row above the three
   documents — name left, the Completed day box and Open right; the slot is
   empty (and takes no room) when no link is set. */
.rs-bq:empty { display: none }
/* Beats `.main label` (a column of 11px words over a full-width box). */
.rs-docrow label.rs-bq-done { flex-direction: row; align-items: center; gap: 6px; font-size: 12px; letter-spacing: 0; margin: 0 }
.rs-docrow label.rs-bq-done input { width: auto; min-height: 32px }
.rs-bq-link { text-decoration: none }
/* The confirmation panels' own chips and send line give way to the rail's card. */
.rs-col .sends-as { display: none }
/* A role that may not send reads the state, not an offer (slice 3 §3). */
.rs-sent.rs-unsent { color: var(--muted) }
/* Read-only text (slice 3 §3): a box painted as the value the paper prints,
   in its place; a text area keeps its lines. */
.rs-ro { display: inline-block; min-height: 1.4em; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere }

/* "Show ▾": every head action but the next one. */
.rs-menuw { position: relative; display: inline-block }
.rs-menu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 6; min-width: 230px; display: flex; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 6px; box-shadow: 0 18px 48px rgba(0, 0, 0, .55)
}
.rs-menu > .btn { width: 100%; text-align: left; border-color: transparent; background: transparent }
.rs-menu > .btn:hover { background: var(--panel-2) }
.rs-menu > .btn.danger { color: var(--bad) }
.rs-menu-sep { height: 1px; background: var(--border); margin: 4px 2px }
.rs-menu .muted-note { margin: 2px 10px 4px }

/* The drawers: one side panel at a time, over a scrim; below the send modal (20)
   and the toast (5), so a file's toast and a send opened from Files show over it. */
.rs-scrim { position: fixed; inset: 0; z-index: 3; background: color-mix(in srgb, var(--bg) 70%, transparent) }
.rs-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 4; width: min(960px, calc(100vw - 80px)); max-width: 100%; overflow-y: auto;
  background: var(--panel); border-left: 1px solid var(--border); box-shadow: 0 18px 48px rgba(0, 0, 0, .55); padding: 14px
}
.rs-drawer-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px }
.rs-drawer-title { font-size: 14px; font-weight: 650 }
.rs-drawer-title:focus { outline: none }
.rs-drawer .panel { margin-bottom: 10px }
/* Files on a phone (the 2026-10-19 critique, P3-4: the filename broke over four
   lines and the date over three). The date never breaks; at 560px and under the Version
   and Date columns fold into the row's other cells — "Offer · v2" and the date
   under the filename — so the filename has the width and Open stays on screen.
   Wider, the folded copies are hidden and the columns stand. */
.files-day { white-space: nowrap }
.files-sub { display: none }
@media (max-width: 560px) {
  .files-table .files-col { display: none }
  .files-table .files-sub { display: inline; white-space: nowrap }
  .files-table .files-sub.files-day { display: block; margin-top: 2px }
}
/* Wide enough to read an email without scrolling sideways (Chris, 2026-10-20: "it opens up pretty small. lets make it larger so we dont have to scroll to the right to read the emails"), and the email taller inside it. */
.rs-drawer .mail-read-frame { height: 65vh }
@media (max-width: 899px) { .rs-drawer { width: 100% } }

/* ── Run Sheet slice 2 — autosave (spec 2026-10-16 §3) ─────────────────────
   No Save buttons: each block saves itself, and its message line says what
   happened (`data-state`, web/runsheet.js). Every colour is a token. */
.rs-body [data-smsave], .rs-body #sh-notes-save, .rs-body #sh-pp-save, .rs-body #sh-tax-save, .rs-body #sh-rental-save, .rs-body #sm-artists-save { display: none }
.rs-body [data-smmsg][data-state="saved"] { color: var(--accent) }
.rs-body [data-smmsg][data-state="saving"] { color: var(--muted) }
.rs-body [data-smmsg][data-state="held"], .rs-body [data-smmsg][data-state="held-emptied"], .rs-body [data-smmsg][data-state="note"] { color: var(--warn) }
.rs-body [data-smmsg][data-state="error"], .rs-body [data-smmsg][data-state="error-network"], .rs-body [data-smmsg][data-state="conflict"], .rs-body [data-smmsg][data-state="signed-out"] { color: var(--bad) }
.rs-body .rs-as-act { margin-left: 6px }
.rs-pageline { margin: 0 0 10px; padding: 8px 12px; border: 1px solid var(--warn); border-radius: var(--ctl-r); color: var(--warn); font-size: 13px }
.rs-settled-note { margin-top: 6px }
.rs-body .sm-tbl tr.sm-locked td { color: var(--muted) }

@media (max-width: 899px) {
  .rs-head .rs-acts { align-items: flex-start; margin-left: 0 }
  .rs-head .rs-row { justify-content: flex-start }
  .rs-menu { right: auto; left: 0 }
  .show-tabs.rs-steps { display: flex; overflow-x: auto }
  .rs-steps button { flex: 0 0 auto; min-width: 128px }
  .rs-body { grid-template-columns: minmax(0, 1fr) }
  .rs-rail { order: -1; position: static; max-height: none; overflow: visible }
}
/* ── Run Sheet slice 3 — blocks (spec 2026-10-16) ───────────────────────────
   Today's panels re-dressed as blocks (web/runsheet-blocks.js): the name, the
   tag, a one-line summary, and a fold for the rarely used ones. The title's
   own letter-case rule stays for the name; the summary reads as a sentence. */
/* The title wraps: name, tag, stamp and fold on the first line, the summary
   on a line of its own under them (measured 2026-10-16 at 1024px: inline, a
   long tag left the summary one character wide). */
.rs-body .panel[data-rs-block] > .panel-title { flex-wrap: wrap; row-gap: 4px }
.rs-body .panel-title .rs-sum { order: 10; flex: 1 0 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-transform: none; letter-spacing: 0; font-weight: 400; font-size: var(--note-fs); color: var(--muted-2) }
.rs-body .panel-title .rs-sum:empty { display: none }
/* Every box's name is bold (Chris, 2026-10-20: "all boxes should have bold
   font for the box name"): the show page's block names read in the text
   colour at 12px; the tag beside a name stays the quiet eyebrow. */
.rs-body .panel-title { font-size: 12px; color: var(--text); letter-spacing: .1em }
.rs-body .panel-title .tag { font-size: 10px; color: var(--muted-2); letter-spacing: .1em }
.rs-body .panel-title .rs-fold { margin-left: auto; order: 9; text-transform: none; letter-spacing: 0; font-weight: 600 }
/* The saved stamp sits at the title's right edge, just before Open / Close —
   never mid-title (critique 2026-10-19, P3-9: with both the stamp and the fold
   link pushed by `margin-left: auto`, the free space split between them and
   the stamp floated in the middle). The pair travels together: both sit in one
   `.rs-tail` (web/runsheet-blocks.js), so a title too long for both wraps them
   onto the next line together, still right-aligned (2026-10-30: as two flex
   items, a wide font at 375 left Open alone on the next line). */
.rs-body .panel-title .rs-tail { order: 9; margin-left: auto; display: inline-flex; align-items: center; gap: 12px; white-space: nowrap; min-width: 0; max-width: 100% }
/* A stamp longer than the line (a phone, a long name) shortens with "…" and
   keeps Open at the edge; its whole text is its tooltip. */
.rs-body .panel-title .rs-tail .tag.saved-stamp { min-width: 0; overflow: hidden; text-overflow: ellipsis }
.rs-body .panel-title .rs-tail .rs-fold { margin-left: 0 }
.rs-body .panel-title .tag.saved-stamp { order: 8; margin-left: auto; white-space: nowrap }
.rs-body .panel-title .rs-tail .tag.saved-stamp { margin-left: 0 }
.rs-body .panel.rs-folded { padding-bottom: 0 }
.rs-body .panel.rs-folded > .panel-title { margin-bottom: 9px }
.rs-body .rs-conf-note { margin: -4px 0 12px }
.rs-body .sm-field-h .rs-vd-sum { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted-2); font-size: var(--note-fs) }
.rs-body .sm-field-h .rs-vd { margin-left: auto; font-size: var(--note-fs) }
.rs-owes { margin: 0 0 12px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--ctl-r); background: var(--panel) }
.rs-owes .rs-owes-l { font-weight: 700; color: var(--text) }
.rs-owes .muted-note { margin-top: 4px }
/* The Internal tab's Bar ring (2026-11-02): the show's net bar sales as the
   block's figure, the nights' table under it, then the mix and the source line. */
[data-smbarring] .br-total { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; margin-bottom: 8px }
[data-smbarring] .br-fig { font-size: 24px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text) }
[data-smbarring] .br-table td, [data-smbarring] .br-table th { white-space: nowrap }
[data-smbarring] .br-body > .muted-note { margin-top: 6px }
.rs-prints .rs-prints-l { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; font-size: 13px }
.rs-prints .rs-prints-l li.off { color: var(--muted-2); text-decoration: line-through }
/* ── Run Sheet slice 4 — the deal sentence, the break-even bar (spec 2026-10-16)
   The deals table re-laid out as one sentence per act (web/runsheet-deal.js):
   the same rows and boxes, words between them, the option cards on a second
   line, the server's walkout and working under each. */
.rs-body table.rs-sentence, .rs-body table.rs-sentence > tbody { display: block }
.rs-body table.rs-sentence > thead { display: none }
.rs-body table.rs-sentence tr.rs-deal { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; padding: 10px 0 4px; border-top: 1px solid var(--line-soft) }
.rs-body table.rs-sentence tr.rs-deal:first-child { border-top: 0 }
.rs-body table.rs-sentence tr.rs-deal > td { display: flex; align-items: center; gap: 6px; padding: 0; border: 0 }
.rs-body table.rs-sentence tr.rs-deal > td[hidden] { display: none }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-artist { order: 1 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-slot { order: 2 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-guarantee { order: 3 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-dealType { order: 4 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-backendPct { order: 5 }
.rs-body table.rs-sentence tr.rs-deal.rs-d-door > td.rs-c-dealType { order: 3 }
.rs-body table.rs-sentence tr.rs-deal.rs-d-door > td.rs-c-backendPct { order: 4 }
.rs-body table.rs-sentence tr.rs-deal::after { content: ''; order: 6; flex-basis: 100%; height: 0 }
/* The option cards (Chris, 2026-10-20: "the boxes for Escalator, Merch and the
   others do not match the concept. also those boxes should be able to be
   hidden"): four equal cards under the sentence in the concept's order —
   Escalator, Merch split, Deposit, Includes support — a tick and a bold name,
   the figures below while ticked, the card dimmed while not. The × sits at the
   end of the sentence line. */
.rs-body table.rs-sentence tr.rs-deal > td.rs-opt { flex: 1 1 0; min-width: 190px; flex-direction: column; align-items: stretch; gap: 6px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2) }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-escOn, .rs-body table.rs-sentence tr.rs-deal > td.rs-c-merchOn { order: 7 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-depositAmt { order: 8 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-inclusiveSupport { order: 9 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-depositDue { display: none }
.rs-body table.rs-sentence tr.rs-deal > td.rs-opt.rs-opt-off > .rs-opt-h { opacity: .7 }
.rs-body table.rs-sentence tr.rs-deal label.rs-opt-h { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 8px; margin: 0; font-size: 13px; letter-spacing: 0; text-transform: none; color: var(--text); white-space: nowrap; cursor: pointer }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-h input { width: 18px; height: 18px; margin: 0; flex: none }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-h .rs-w-card { font-size: 13px; font-weight: 700; letter-spacing: 0; text-transform: none; color: var(--text) }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-f { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; white-space: normal }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-f[hidden] { display: none }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-f > .sm-grp-lbl { display: none }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-f .sm-grp-f .muted-note:empty { display: none }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-f .sm-grp-f .muted-note { font-size: 13px; letter-spacing: 0; color: var(--muted) }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-note { white-space: normal }
.rs-body table.rs-sentence tr.rs-deal td.rs-c-depositAmt .pk-money, .rs-body table.rs-sentence tr.rs-deal td.rs-c-depositAmt input[data-k="depositAmt"] { width: 100px; min-width: 0 }
.rs-body table.rs-sentence tr.rs-deal > td.rs-c-del { order: 6; margin-left: auto }
.rs-body .rs-yn-sel { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; overflow: hidden }
.rs-body table.rs-sentence tr.rs-deal > td.rs-dline { order: 9; flex-basis: 100%; display: block }
.rs-body table.rs-sentence tr.rs-deal .rs-dw { font-size: 13px; color: var(--muted); white-space: nowrap }
.rs-body table.rs-sentence tr.rs-deal .rs-w-card { font-size: var(--note-fs); text-transform: uppercase; letter-spacing: .08em }
.rs-body table.rs-sentence tr.rs-deal .rs-dl-w { font-size: 13px; color: var(--text) }
.rs-body table.rs-sentence tr.rs-deal .rs-dline .muted-note { margin-top: 2px }
.rs-body table.rs-sentence tr[data-detail] { display: block; padding: 0 0 8px }
.rs-body table.rs-sentence tr[data-detail][hidden] { display: none }
.rs-body table.rs-sentence tr[data-detail] > td { display: block; border: 0; padding: 0 }
.rs-body table.rs-sentence > tfoot { display: block; border-top: 1px solid var(--line-soft); padding-top: 6px }
.rs-body table.rs-sentence > tfoot tr { display: flex; gap: 24px; justify-content: flex-end }
/* The totals under the sentences read as words (critique 2026-10-19, P3-2):
   "Guarantees $2,800.00   Deposits $0.00". There are no column heads above
   them, so the Total cell and the empty cells under the unsummed columns —
   which drew as short dashed stubs — are not shown; each figure carries its
   own word (show-sections.js `sumWord`). */
.rs-body table.rs-sentence > tfoot[hidden] { display: none }
.rs-body table.rs-sentence > tfoot tr > th, .rs-body table.rs-sentence > tfoot tr > td:not([data-sumk]) { display: none }
.rs-body table.rs-sentence > tfoot td[data-sumk] { border: 0; padding: 4px 0 }
.rs-body .sm-tbl tfoot .sum-w { font-weight: 400; color: var(--muted); margin-right: 4px }
.rs-body .rs-rsent { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 14px; margin: 4px 0 8px }
.rs-body .rs-rsent .rs-dw { color: var(--muted) }
/* A box and the words either side of it are one unit that never breaks, so
   the sentence wraps between phrases (critique 2026-10-19, P2-6); the boxes
   are sized to what they hold. */
.rs-body .rs-rsent .rs-ru { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap }
.rs-body .rs-rsent .pk-money { width: 116px }
.rs-body .rs-rsent #sh-rental-door { width: 76px; min-width: 0 }
/* Room rental (2026-10-19): the labels read as words beside their boxes, not
   the app's stacked field labels; "Room rental" itself is in the ink colour. */
.rs-body .rs-rsent label { display: inline; margin: 0; font-size: 14px; font-weight: 400; text-transform: none; letter-spacing: normal; color: var(--muted) }
.rs-body .rs-rsent label.rs-rl { color: var(--text); font-weight: 600 }
.rs-body .rs-rsent #sh-rental-renter { width: 200px; max-width: 100% }
/* A rental's lineup (runsheet-deal.js dressDeals): who plays and in which
   slot. The talent money columns are HIDDEN, never removed: a rental pays no
   artist, and the boxes stay in the row so a save sends the stored figures
   back as they are (Chris, 2026-09-30: a switch keeps the other type's
   figures). The columns are the deals spec's order: Artist, Slot, ... and
   the delete cell last. */
.rs-body table.rs-lineup > thead > tr > th:not(:nth-child(-n+2)):not(:last-child),
.rs-body table.rs-lineup > tbody > tr:not([data-detail]):not([data-computed]) > td:not(:nth-child(-n+2)):not(:last-child),
.rs-body table.rs-lineup > tbody > tr[data-detail],
.rs-body table.rs-lineup > tfoot { display: none }
.rs-body table.rs-lineup td:first-child input { width: 220px; max-width: 100% }
/* The break-even bar under the Offer's hero: the mark is the Offer's
   break-even; after the show the fill is face sales, Settled Green past it and
   Rental Amber short of it. Widths come from server figures (runsheet-deal.js). */
.rs-be { margin: 8px 0 4px }
.rs-be-bar { position: relative; height: 10px; border-radius: 6px; background: var(--bg); border: 1px solid var(--border); overflow: visible }
.rs-be-fill, .rs-be-to { position: absolute; top: 0; bottom: 0; left: 0 }
.rs-be-fill { border-radius: 6px; background: var(--accent) }
.rs-be-to { top: -4px; bottom: -4px; border-right: 2px solid var(--text) }
.rs-be.short .rs-be-fill { background: var(--warn) }
.rs-be-c { margin-top: 6px; font-size: 13px; color: var(--muted) }
.rs-be.past .rs-be-c { color: var(--accent) }
.rs-be.short .rs-be-c { color: var(--warn) }
.rs-body .rs-working { display: flex; flex-direction: column; gap: 4px; margin: 0 0 10px; font-size: 13px }
.rs-body .rs-working .rs-wk span { color: var(--muted) }
/* Inside the sentence the boxes are sized to what they hold, so a deal reads
   on one line at a laptop width. */
.rs-body table.rs-sentence tr.rs-deal td.rs-c-artist input { width: auto; min-width: 150px; max-width: 100% }
.rs-body table.rs-sentence tr.rs-deal td.rs-c-guarantee .pk-money, .rs-body table.rs-sentence tr.rs-deal td.rs-c-guarantee input { width: 110px; min-width: 0 }
.rs-body table.rs-sentence tr.rs-deal td.rs-c-backendPct input { width: 64px; min-width: 0 }
/* ── Run Sheet slice 6 — holds, two nights, a new show (spec 2026-10-17) ────
   A hold's later steps dim and still open; a rental's active step takes the
   Rental Amber ring; the hold's checklist and the new show's chooser sit above
   the blocks; the two-night picker becomes a box over the page. */
/* A waiting step reads as waiting without dimming its words (critique
   2026-10-19, P2-11: at 55% opacity they measured 2.26 / 2.55:1). The card
   sinks to the ground with a dashed edge; its title steps to Slate Lavender;
   every word stays at full opacity and over 4.5:1 (unit: runsheet-layout). */
.rs-steps button[data-rs-dim]:not(.active) { background: var(--bg); border-style: dashed }
.rs-steps button[data-rs-dim]:not(.active) .rs-step-t { color: var(--muted) }
.rs-steps button[data-rs-dim]:not(.active):hover, .rs-steps button[data-rs-dim]:not(.active):focus-visible { border-style: solid }
.rs-steps.rs-rental button.active { box-shadow: inset 0 0 0 2px var(--warn) }
.rs-body .rs-check-l { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 14px }
.rs-body .rs-check-l li { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap }
.rs-body .rs-check-l .rs-dot { width: 1em; text-align: center }
.rs-body .rs-check-l li.done .rs-dot { color: var(--accent) }
.rs-body .rs-check-l li.open .rs-dot, .rs-body .rs-check-l .rs-check-t { color: var(--muted) }
.rs-body .rs-check-l .lnk { margin-left: auto; font-size: 13px }
.rs-body .rs-choose-l { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px }
#sh-confirm-day[data-rs-dialog]:not([hidden]) { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; background: color-mix(in srgb, var(--bg) 70%, transparent); padding: 16px }
#sh-confirm-day[data-rs-dialog] .rs-day-box { background: var(--panel); border: 1px solid var(--border); border-radius: var(--ctl-r); padding: 16px; max-width: 460px; width: 100%; box-shadow: 0 18px 48px rgba(0, 0, 0, .55); display: flex; flex-direction: column; gap: 10px }
#sh-confirm-day[data-rs-dialog] .rs-day-h { font-weight: 700; font-size: 15px }
#sh-confirm-day[data-rs-dialog] .rs-day-l { display: flex; flex-direction: column; gap: 6px; font-size: 14px }
#sh-confirm-day[data-rs-dialog] .rs-day-o { display: flex; gap: 8px; align-items: center; text-transform: none; letter-spacing: 0; color: var(--text) }
#sh-confirm-day[data-rs-dialog] .rs-day-b { display: flex; gap: 8px; justify-content: flex-end }

/* ── Run Sheet slice 7 — phone (spec 2026-10-17) ────────────────────────────
   Under the one breakpoint, Run Sheet only: the steps become chips ("1 ✓
   Offer") in one sideways row, each table block reads as cards with Edit /
   Done (web/runsheet-phone.js), and a dock at the foot holds the step's main
   document button and the save line. Above the breakpoint none of it shows. */
.rs-chip, .rs-dock, .rs-cards { display: none }
@media (max-width: 899px) {
  .show-tabs.rs-steps { gap: 6px; scroll-padding-inline: 16px }
  .rs-steps button { flex-direction: row; align-items: center; min-width: 0; min-height: 44px; padding: 8px 14px; white-space: nowrap }
  .rs-steps .rs-step-n, .rs-steps .rs-step-t, .rs-steps .rs-step-s { display: none }
  .rs-chip { display: inline; font-size: 14px; font-weight: 650 }
  .rs-body .rs-cards { display: block; margin: 0 0 8px }
  .rs-body .rs-cards-hide { display: none }
  .rs-card-l { list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-direction: column; gap: 6px }
  .rs-card-l li { padding: 10px 12px; border: 1px solid var(--line-soft); border-radius: var(--ctl-r); background: var(--panel-2); font-size: 14px; overflow-wrap: anywhere }
  .rs-card-t { min-height: 44px }
  .rs-body [data-smdel], .rs-body [data-paydel] { min-width: 44px; min-height: 44px }
  .rs-body .rs-cards .rs-card-t { margin-right: 8px }
  .rs-dock {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; position: sticky; bottom: 0; z-index: 5; margin-top: 12px;
    padding: 10px 0 calc(10px + env(safe-area-inset-bottom, 0px)); background: var(--bg); border-top: 1px solid var(--border)
  }
  /* A long button ("Preview the internal settlement") and the save line share
     the row when they fit; when they don't (a wider font), the line drops
     under the button rather than spilling past the screen's edge. */
  .rs-dock-go { flex: 0 1 auto; max-width: 100%; min-height: 44px; min-width: 0; overflow: hidden; text-overflow: ellipsis }
  .rs-saveline { flex: 1 1 140px; min-width: 0; overflow-wrap: anywhere; font-size: 13px; color: var(--muted) }
}

/* ── Run Sheet critique fixes, lane A (2026-10-19) ──────────────────────────
   Chris, 2026-10-19: "Rail moves under 1400px", then (measured: 1440 and 1600
   still clipped beside the side rail) "Move it to 1700px". Between the phone
   breakpoint and 1700px the rail leaves the side and sits at the top of the step as a
   slim bar — the step's big figure (and the settlement basis that drives it),
   the owes sentences, and the documents card — so the tables take the whole
   width. "Details" (`[data-rs-railmore]`, web/runsheet.js) opens the rest of
   the summary rows and What prints under the bar; nothing leaves the page.
   At 1700px and wider the rail is the 340px side column, unchanged. */
.rs-rail-more { display: none }
@media (min-width: 900px) and (max-width: 1699.98px) {
  .rs-body { grid-template-columns: minmax(0, 1fr) }
  .rs-rail { order: -1; position: static; max-height: none; overflow: visible; flex-direction: row; flex-wrap: wrap; align-items: flex-start }
  .rs-rail > * { flex: 1 1 300px; min-width: 0 }
  .rs-rail > .rs-owes { margin: 0 }
  .rs-rail > .rs-rail-more { display: block; flex: 0 0 100%; margin-top: -4px; text-align: left; font-size: 13px; font-weight: 600; color: var(--accent-2) }
  .rs-rail:not(.rs-rail-open) > .sm-summary > :not(.panel-title):not(.sm-basis):not(.rs-hero):not(.rs-be),
  .rs-rail:not(.rs-rail-open) > #rs-prints { display: none }
  .rs-rail:not(.rs-rail-open) > .sm-summary > .rs-hero { border-bottom: 0; margin-bottom: 0; padding-bottom: 0 }
}
/* The width a table gains goes to its words. Figures, dates and dropdowns
   keep their column's floor (`mw-<n>`), so an expense, a tier or a payee
   reads in full instead of every column growing alike (critique P1-1:
   "Sc", "Lig", "EARLY BIF"). Only while the rail is the slim bar: there the
   column is the page's width. Beside the 340px rail (1700px and wider) the
   column is narrower than that, and these floors would make the tables scroll
   sideways, so the tables there are as they were. A phone reads cards. */
@media (min-width: 900px) and (max-width: 1699.98px) {
  .rs-body .sm-tbl:not(.rs-sentence) :is(td.num, td[data-byrate], td:has(> select), td:has(> .pk), td:has(> input[type="date"])) { width: 1% }
  /* A dropdown is as wide as its longest answer ("Box office (no one fronted)"). */
  .rs-body .sm-tbl:not(.rs-sentence) td > select.fill { width: auto }
  /* …and the floor holds: the base control rule's `min-width: 0` outweighs a
     bare `.mw-<n>` on a typed input, so a figure box shrank to "6C". */
  .rs-body .sm-tbl:not(.rs-sentence) input.mw-60 { min-width: 60px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-64 { min-width: 64px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-70 { min-width: 60px }
  .rs-body .sm-tbl:not(.rs-sentence) input.mw-76 { min-width: 76px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-78 { min-width: 78px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-80 { min-width: 80px }
  .rs-body .sm-tbl:not(.rs-sentence) input.mw-82 { min-width: 82px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-84 { min-width: 84px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-88 { min-width: 88px }
  .rs-body .sm-tbl:not(.rs-sentence) input.mw-90 { min-width: 90px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-98 { min-width: 98px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-100 { min-width: 100px }
  .rs-body .sm-tbl:not(.rs-sentence) input.mw-104 { min-width: 104px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-110 { min-width: 110px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-112 { min-width: 112px }
  .rs-body .sm-tbl:not(.rs-sentence) input.mw-118 { min-width: 118px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-120 { min-width: 120px } .rs-body .sm-tbl:not(.rs-sentence) input.mw-140 { min-width: 140px }
}
/* A drawer covers the phone's dock (critique P2/P1-2: the dock, z 5, drew over
   an open drawer, z 4, and its Send again took taps through it). */
.rs-dock:has(~ .rs-drawer:not([hidden])) { visibility: hidden }
@media (max-width: 899px) {
  /* The hold's checklist and the new show's chooser come before the rail on a
     phone (critique P2-9: they sat ~1,000px down). The column gives its blocks
     to the page's own column so the two can stand above the rail. */
  .rs-body { display: flex; flex-direction: column; align-items: stretch; gap: 0 }
  .rs-col { display: contents }
  .rs-col > [data-rs-top] { order: -2 }
  .rs-rail { margin-bottom: 14px }
  /* Every Run Sheet button is a 44px target on a phone (critique P2-12):
     the save line's Undo / Retry / Save as $0.00 / Load theirs / Keep mine,
     the drawers' ×, the header's, rail's, blocks' and drawers' buttons. */
  .rs-body .rs-as-act { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; padding: 0 8px; margin-left: 0 }
  .rs-drawer [data-rs-close] { min-width: 44px; min-height: 44px }
  .rs-crumb .btn, .rs-head .btn, .rs-body .btn, .rs-drawer .btn, .rs-menu > .btn { min-height: 44px }
}

/* A row's × stays on screen. A wide table (Internal Expenses, Payments) scrolls
   sideways inside its block on a narrower window, and its × — the last
   column — sat past the edge (measured 2026-10-19 at 1024: 198px out of view).
   The × column sticks to the table's right edge, on the block's own ground. */
.rs-body .sm-tbl td:has(> [data-smdel]), .rs-body .sm-tbl td:has(> [data-paydel]) { position: sticky; right: 0; z-index: 1; background: var(--panel) }
/* The option cards share their line's height; the escalator's ticket count is a short box. */
.rs-body table.rs-sentence tr.rs-deal > td.rs-opt { align-self: stretch; justify-content: flex-start }
.rs-body table.rs-sentence tr.rs-deal .rs-opt-f input[data-k="escAtTickets"] { width: 72px; min-width: 0 }
/* A show-page table heading may take two lines so a money column is as wide as
   its box, not its words (box sizes, 2026-10-20: Internal Expenses ran 41px past
   its block in a wider font). */
.rs-body .sm-tbl:not(.rs-sentence) thead th { white-space: normal; vertical-align: bottom }
/* A name box's floor is its own words (its size, set by web/runsheet-deal.js fitBox). */
.rs-body .sm-tbl:not(.rs-sentence) td > input:is([data-k="name"], [data-k="artist"], [data-k="payee"]) { width: auto; min-width: 100% }
/* An engine line's words (Source, Payee) wrap rather than widen Payments (box sizes, 2026-10-20). */
.rs-body .sm-tbl:not(.rs-sentence) tr.sm-fixed > td { white-space: normal; overflow-wrap: anywhere }

/* ── Look walk 2026-10-26, lane A ──────────────────────────────────────────
   Chris, 2026-10-26, on the look-walk findings page: "fix all 20 and the 6
   things". This lane's spacing items, measured at 1280 and 375 and pinned by
   test/e2e/look-walk-lane-a.spec.js. Kept in one block at the end so the other
   lanes' blocks merge beside it. */

/* Item 1 — Today › Money due soon. In a NARROW money list (the container query
   that already takes Open away: four columns at 1280, every phone) the figure
   and its ✕ move to the end of the SMALL line, so the title — payee and kind —
   has the row's whole width; the payee read "Wes… · Deposit" with the cluster
   beside it. The row is a two-column grid with the link across both, the
   cluster in the right column at the bottom, and the small line keeping clear
   the cluster's measured width (`--lw-right`, web/today.js fitMoney). The ✕
   is taller than the small line, so the cluster reaches 8px down into the row's
   own 8px bottom padding and its top stays under the title line; where the ✕
   is taller still (the phone's target), the title keeps the ✕'s width clear
   (`--lw-title`, measured the same way). An ARMED
   row keeps the old flex row: the question widens the cluster for 2.6 seconds
   and has to sit beside the ✕ the second click needs. The row stays 51px. */
@container (max-width: 299px) {
  #today-money .today-item:not(:has(.today-arm:not([hidden]))) { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; column-gap: 0 }
  #today-money .today-item:not(:has(.today-arm:not([hidden]))) > .today-go { grid-area: 1 / 1 / 2 / 3; align-self: center }
  #today-money .today-item:not(:has(.today-arm:not([hidden]))) > .today-right { grid-area: 1 / 2 / 2 / 3; align-self: end; position: relative; margin-bottom: -8px }
  #today-money .today-item:not(:has(.today-arm:not([hidden]))) .today-go small { padding-right: var(--lw-right, 0px) }
  #today-money .today-item:not(:has(.today-arm:not([hidden]))) .today-go b { padding-right: var(--lw-title, 0px) }
}

/* Item 2 — Today › Bar. The title is a sentence ("Opening · 27 tasks not
   signed off") and says its count once (web/today.js barRow); it may take a
   second line rather than lose its last words. Two lines at most. */
.today-bar-item b { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2 }

/* Item 3 — both calendars slid sideways at 1280. A settled chip's
   screen-reader-only "· Settled" (`.sr-only`, absolutely placed) sat at the end
   of the chip's name, and the chip was not its containing block, so a long name
   in the Saturday column put it past `.main`'s right edge. The chip holds it
   now, and the chip's own `overflow: hidden` clips it. */
.cal-ev { position: relative }

/* Item 4 — the Event Calendar's "Move on to…" picker read "Move on to▾": the
   card's compact padding shorthand (`#scr-pe .pe-card-acts select`, above) put
   the right padding back to 8px, so the select sized itself to its words with
   no room for the chevron (drawn from right 10px to 19px) and the chevron sat on
   the "…". The chevron's own 22px, as `.data-table select` restates it. */
#scr-pe .pe-card-acts select { padding-right: 22px }

/* Item 8 — the event page's name row sat 3px under the page's own heading
   ("EVENT CALENDAR"), so the two read as one block. The record's head stands
   clear of the page name, as the show page's head stands alone at the top. */
#scr-pe .page-name + .pev-head-row { margin-top: 18px }

/* Item E — Show P&L's rules, folded under one line (web/showpnl.js). The
   house's fold for a block of explanation: a muted summary that opens to the
   same muted note. */
#scr-showpnl .spl-how { margin: 0 0 8px }
#scr-showpnl .spl-how > summary { cursor: pointer; width: max-content; max-width: 100%; font-size: 12px; font-weight: 600; color: var(--muted); padding: 2px 0 }
#scr-showpnl .spl-how > summary:hover { color: var(--accent-2) }
#scr-showpnl .spl-how > .muted-note { margin-top: 6px }
/* ── Look walk 2026-10-26, lane B ────────────────────────────────────────────
   Measured on the live site at 1280 / 375 the same day; Chris: "fix all 20
   and the 6 things". */
/* 6. The phone's dock sits flush with the foot of the screen. A sticky box
   stops at the scroller's padding edge, and .main keeps 18px under its
   content on a phone (the `.main` rule in the phone block above), so the dock
   floated 18px up and rows showed through the gap. The negative offset is
   that padding, taken back. */
@media (max-width: 899px) { .rs-dock { bottom: -18px } }
/* 9. Payments at 1280: the board fits its box (it ran 99px past it, Ref half
   hidden, Save off the edge). The show's name and the payee wrap at their
   spaces, and a name with none ("FaceTime/TheLastStarfighter") breaks rather
   than widening the board; Method and Ref are short answers in short boxes;
   the date box and the cells keep less air. Every column stays. Kind may take
   two lines ("Settlement / payout") — the Show cell's own two, its name over
   its date — so no row grows past them (measured in Arial; in a wide face such
   as Verdana a long one-word name may take a third, and the board still fits). */
#pb-rows td.pb-show, #pb-rows td.pb-payee { overflow-wrap: anywhere }
#pb-rows th, #pb-rows td { padding-left: 4px; padding-right: 4px }
#pb-rows input.pb-narrow { width: 70px }
#pb-rows input[data-pb-paid] { width: 112px }
/* 10. Missing: Open · Request · Won't come each keep their place. The ask's
   button and the empty slot that stands in for it are one width, "Send
   again"'s, so Won't come and Reopen start on one line down the column. */
#ms-rows td.ms-act [data-ms-request], #ms-rows td.ms-act .ms-ask-gap { display: inline-block; min-width: 6.6em }
/* 11. Contacts at 1280: a phone number, a date and Edit / Delete never break;
   an address breaks before its @ (web/contacts.js), then anywhere, rather than
   pushing Last doc off the panel. In a wide face (Verdana) twelve columns
   cannot all fit and the table scrolls inside its own box, as before. */
#ct-rows td.ct-nowrap { white-space: nowrap }
#ct-rows td.ct-email { overflow-wrap: anywhere }
#ct-rows th, #ct-rows td { padding-left: 4px; padding-right: 4px }
/* ── Look walk 2026-10-26, lane C ─────────────────────────────────────────────
   Six screens measured on the live site at 1280 and 375 with words cut or a
   table running past its panel (pinned by test/e2e/look-walk-c.spec.js). */

/* 13 TOUR WATCH FITS ITS PANEL AT 1280. The board ran 258px past the panel: the
   home check and the draw held 220px each, Band/TX/Agent kept their phone floors
   and an unbroken agency email set the Agent column's width, so "BA open day"
   was squeezed to three lines and the Draw column was cut. On a laptop the two
   one-line cells hold 110px (still one line until clicked — the owner's
   2026-09-23 ruling: Tour Watch keeps its dense rows), the floors are a laptop's,
   an email breaks anywhere, and the open-day head keeps one line. The phone's
   floors (min-width 150/180/150 above) are unchanged: there the board scrolls
   sideways in its box, as ruled. */
@media (min-width: 900px) {
  #scr-tourwatch #tw-table th, #scr-tourwatch #tw-table td { padding-left: 6px; padding-right: 6px }
  #scr-tourwatch #tw-table th:nth-child(4) { white-space: nowrap }
  #scr-tourwatch .tw-one { max-width: 110px }
  #scr-tourwatch .tw-band { min-width: 110px }
  #scr-tourwatch .tw-tx { min-width: 140px }
  #scr-tourwatch .tw-agent-col { min-width: 110px }
}
#scr-tourwatch .tw-agent-col { overflow-wrap: anywhere }

/* 15 MENU ITEMS: A NAME TAKES TWO LINES RATHER THAN BEING CUT. At 1280 the
   editor's 360px column leaves the name ~116px ("Poggio Costa Pr…"). The grid
   stays the approved board's (22 · 40 · name · 56 · 150 · 176 — owner ruling
   2026-10-01, "Match it exactly"; DESIGN.md › Menu items); the name and its
   maker line wrap to two lines at most instead of one cut line, and a 3px gap
   sits between them (it was 1px). */
.menu-what { gap: 3px }
.menu-name, .menu-meta { white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2 }
/* On the stacked row (the list under 600px wide) the name takes the whole
   first line and the order arrows + status move down beside the ABV: beside
   them the name had ~87px, and in a wide font "Poggio Costa Prosecco Brut" was
   cut even at three lines (nightly 2026-10-30). A sub-row keeps its own place. */
@container (max-width: 599px) {
  .menu-row:not(.mi-sub) > .menu-what { grid-column: 3 / span 2 }
  .menu-row:not(.mi-sub) > .menu-status { grid-row: 2 }
}

/* 16 ITEM LIBRARY: THE ROW BUTTONS FIT, EACH AT ONE PLACE. The table's 1100px
   floor ran 106px past the 994px panel at 1280, cutting "Add to menu" to "Add
   to"; and the buttons were right-aligned, so Edit sat further left on a row
   with Delete. The floor is 940px (Type and Last on menu give up width;
   Item keeps ~200px), the buttons start at the cell's left edge on one line,
   and the second button (Add to menu / Unhide) holds one width so Delete lines
   up too. Add mode (section select, Add, Close) may wrap. The actions column is
   250px (it was 222): in a wide font Edit + Add to menu + Delete ran 13-16px
   past it and the table past its panel (nightly 2026-10-30). */
.ml-table { min-width: 940px }
.ml-table th:nth-child(3) { width: 90px }
.ml-table th:nth-child(5) { width: 100px }
.ml-table th:nth-child(7) { width: 250px }
.ml-table td.ml-acts { text-align: left; white-space: nowrap }
.ml-table td.ml-acts:has(select), .ml-table td.ml-acts:has(.ml-nosec) { white-space: normal }
.ml-table td.ml-acts :is([data-ml-act="add"], [data-ml-act="unhide"]) { min-width: 100px; justify-content: center }

/* 17 ORDERING ON A PHONE: THE PAGE NEVER SLIDES. Each supplier's table already
   scrolls in the house `.tbl-wrap`, but its Edit head's `.sr-only` word is
   position:absolute, so its box was placed against `.main` — past the wrap's
   clip, at the table's right edge — and the whole page scrolled 541px
   sideways at 375. A positioned wrap keeps it inside the box that scrolls. */
#scr-inventory .ord-sec .tbl-wrap { position: relative }

/* 18 SETTINGS › TEMPLATES FITS ITS PANEL. Step and Offset are number boxes,
   which take no `size`, so each was ~150px; with eleven boxes the table ran
   561px past its panel at 1280 and When and Task were cut. Set column shares
   (Task takes what is left), a 900px floor that scrolls in its box on a phone,
   each box its column's width; Group, When, Task and Notes are text areas that
   take a second line when they need it (web/settings-templates.js). Offset is
   68px and Owner 12% (Notes 7%): in a wide font "-14" and "Tiare Austin" were
   cut at 60px and 10% (nightly 2026-10-30). App is 64px: at 6% (50px) a wide
   font cut "Gmail" (nightly 2026-10-31). */
#st-templates [data-tt-phase] table { min-width: 900px }
#st-templates [data-tt-phase] th, #st-templates [data-tt-phase] td { padding-left: 4px; padding-right: 4px }
#st-templates [data-tt-phase] th:nth-child(1) { width: 52px }
#st-templates [data-tt-phase] th:nth-child(2) { width: 10% }
#st-templates [data-tt-phase] th:nth-child(3) { width: 14% }
#st-templates [data-tt-phase] th:nth-child(4) { width: 7% }
#st-templates [data-tt-phase] th:nth-child(6) { width: 68px }
#st-templates [data-tt-phase] th:nth-child(7) { width: 64px }
#st-templates [data-tt-phase] th:nth-child(8) { width: 12% }
#st-templates [data-tt-phase] th:nth-child(9) { width: 7% }
#st-templates [data-tt-phase] th:nth-child(10) { width: 34px }
#st-templates [data-tt-phase] th:nth-child(11) { width: 80px }
#st-templates [data-tt-phase] td > :is(input:not([type="checkbox"]), textarea) { width: 100%; box-sizing: border-box }
#st-templates [data-tt-phase] textarea { resize: none; field-sizing: content; min-height: 0; padding: var(--ctl-pad-c); font: inherit; font-size: var(--ctl-fs); line-height: 1.35; overflow: hidden }

/* 19 SETTINGS › TOUR WATCH FITS ITS PANEL. Every box was 110px (Notes 200), so
   at 1280 the registry ran 182px past its panel, names were cut ("Antone's
   Nightd…") and Last OK wrapped to three lines. Each box is sized to what it
   holds — Name 192px, Market 76, Tier 60, Adapter 62, Handle 80, Notes fills
   from 40px (2026-10-30: in a wide font the row ran 22-32px past the panel and
   "The Studio at the Factory" needed 189px; a longer handle scrolls inside its
   box) — Last OK keeps one line, and Home places takes the row's width. */
#st-tourwatch .tw-reg th, #st-tourwatch .tw-reg td { padding-left: 4px; padding-right: 4px }
#st-tourwatch .tw-reg-in[data-k="name"] { width: 192px }
#st-tourwatch .tw-reg-in[data-k="market"] { width: 76px }
#st-tourwatch .tw-reg-in[data-k="tier"] { width: 60px }
#st-tourwatch .tw-reg-in[data-k="adapter"] { width: 62px }
#st-tourwatch .tw-reg-in[data-k="handle"] { width: 80px }
#st-tourwatch .tw-reg-in[data-k="notes"] { width: 100%; min-width: 40px }
#st-tourwatch .tw-reg td:nth-child(8) { white-space: nowrap }
#st-tourwatch #st-tw-places { flex: 1 1 320px; max-width: 640px; width: auto }

/* THE STORED PDFs' TICK LIST (2026-11-08, web/pe-attach.js): the same box on a
   card's Reply, the Conversation panel's Reply, the template editor and the
   Client Book's send box. A plain fieldset — legend as a field label, one row
   per PDF, its name in the text colour. */
.pe-att-ticks { border: 0; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; min-width: 0 }
.pe-att-ticks legend { padding: 0; margin-bottom: 4px; font-size: 11px; letter-spacing: .04em; color: var(--muted) }
.pe-att-ticks label.chk { display: flex; flex-direction: row; align-items: center; gap: 6px; margin: 0; font-size: 12.5px; color: var(--text); letter-spacing: 0; overflow-wrap: anywhere }
.pe-att-ticks label.chk input { width: auto; flex: none }
.pe-att-none { margin-top: 6px }
#doc-top { margin: 4px 0 8px; display: flex; flex-direction: column; gap: 6px }
#doc-top .pe-fld { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--muted) }
#pe-att .pe-tpl-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 6px 0; border-bottom: 1px solid var(--line-soft); font-size: 12.5px }
#pe-att .pe-tpl-row:last-child { border-bottom: 0 }
#pe-att { margin-top: 10px }
#pe-att .pe-tpl-row .grow { flex: 1; min-width: 160px; overflow-wrap: anywhere; white-space: normal }
#pe-att .pe-tpl-row input.grow { min-width: 0 }

/* THE PIPELINE DOORS (Chris, 2026-11-08; web/pe.js DOORS). Find on top; three
   doors with their counts; a door page of rows — date stamp, name, one pill, one
   button (only the top row's green) — in groups, the two problem groups toned.
   On a door page the month and everything under the pipeline step aside. */
#scr-pe.pe-on-door > :not(.page-name):not(#pe-find):not(#pe-panels) { display: none !important }
#scr-pe .pe-find { display: flex; flex-direction: column; gap: 6px; margin: 8px 0 12px }
#scr-pe .pe-find input[type="search"] { width: 100%; max-width: 100%; font-size: 14px; padding: 9px 11px }
#scr-pe .pe-find-hits { display: flex; flex-direction: column; gap: 4px }
#scr-pe .pe-find-hits:empty { display: none }
#scr-pe .pe-find-hit { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 8px; text-align: left; background: var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: var(--ctl-r); padding: 7px 10px; color: var(--text); font: inherit; font-size: 13px; cursor: pointer; text-decoration: none; min-width: 0 }
#scr-pe .pe-find-hit:hover, #scr-pe .pe-find-hit:focus-visible { border-color: var(--accent-2); outline: none }
#scr-pe .pe-find-hit.done { cursor: default }
#scr-pe .pe-find-hit .muted-note { overflow-wrap: anywhere }
#scr-pe .pe-doors { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin: 8px 0 10px }
#scr-pe .pe-door { display: flex; flex-direction: column; gap: 4px; text-align: left; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  padding: 14px; color: var(--text); font: inherit; cursor: pointer; min-width: 0 }
#scr-pe .pe-door:hover, #scr-pe .pe-door:focus-visible { border-color: var(--accent-2); outline: none }
#scr-pe .pe-door-n { font-size: 30px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums }
#scr-pe .pe-door-t { font-size: 15px; font-weight: 700 }
#scr-pe .pe-door-why { color: var(--muted); font-size: 12px }
#scr-pe .pe-door-flag { font-size: 12px; font-weight: 600 }
#scr-pe .pe-door-flag.bad { color: var(--bad) } #scr-pe .pe-door-flag.warn { color: var(--warn) } #scr-pe .pe-door-flag.go { color: var(--accent) }
#scr-pe .pe-door-bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px }
#scr-pe .pe-door-bar .settabs { margin: 0 }
#scr-pe .pe-door-h { margin: 6px 0 4px }
#scr-pe .pe-group { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 0 }
#scr-pe .pe-group-h { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px }
#scr-pe .pe-group-h b { font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase }
#scr-pe .pe-group-h span { color: var(--muted); font-size: 12px }
#scr-pe .pe-group.warn .pe-group-h b { color: var(--warn) } #scr-pe .pe-group.bad .pe-group-h b { color: var(--bad) }
#scr-pe .pe-row-wrap { background: var(--panel); border: 1px solid var(--line-soft); border-radius: var(--ctl-r); padding: 8px 10px }
#scr-pe .pe-row-wrap.open { border-color: var(--accent-2) }
#scr-pe .pe-row { display: grid; grid-template-columns: 56px minmax(0, 1fr) auto auto; gap: 10px; align-items: center }
#scr-pe .pe-stamp { display: flex; flex-direction: column; align-items: center; border-right: 1px solid var(--border); padding-right: 8px; font-variant-numeric: tabular-nums }
#scr-pe .pe-stamp-dw { font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted) }
#scr-pe .pe-stamp-dd { font-size: 20px; font-weight: 700; line-height: 1.1 }
#scr-pe .pe-stamp-mo { font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--pe); font-weight: 700 }
#scr-pe .pe-stamp.unclear .pe-stamp-dd { color: var(--warn) }
#scr-pe .pe-row-who { min-width: 0 }
#scr-pe .pe-row-name { font-size: 14px; font-weight: 700; color: var(--text); text-align: left; overflow-wrap: anywhere }
#scr-pe .pe-row-sub { color: var(--muted); font-size: 12px; overflow-wrap: anywhere }
#scr-pe .pe-row-raw { color: var(--warn); font-size: 12px; overflow-wrap: anywhere }
#scr-pe .pe-row-pill { white-space: nowrap }
#scr-pe .pe-row-pill.ok { color: var(--accent); border-color: currentColor }
#scr-pe .pe-row-pill.warn { color: var(--warn); border-color: currentColor }
#scr-pe .pe-row-pill.bad { color: var(--bad); border-color: currentColor }
#scr-pe .pe-row-msg:empty { display: none }
#scr-pe .pe-row-wrap > .pe-card { margin-top: 8px }
@media (max-width: 760px) {
  #scr-pe .pe-doors { grid-template-columns: minmax(0, 1fr) }
  #scr-pe .pe-row { grid-template-columns: 48px minmax(0, 1fr) }
  #scr-pe .pe-row .pe-row-pills { grid-column: 2; justify-self: start }
  #scr-pe .pe-row > .btn { grid-column: 1 / -1; min-height: 44px }
}
#scr-pe .pe-row-in { color: var(--accent); font-size: 12px; font-weight: 600 }
#scr-pe .pe-row-pills { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end }
@media (max-width: 760px) { #scr-pe .pe-row .pe-row-pills { grid-column: 2; justify-content: flex-start } }
