/* Imvelo Management Control Tower — Brand Book Edition 02 ("quiet luxury").
   Warm minimalism: paper, ink, one gold breath.

   COLOUR (ch. 3) is four values and a ratio — Midnight Navy #141F38 (ground,
   60), Bone Cream #EFE8D6 (ground, 30), Harvest Gold #E0A43B (accent, 8) and
   Cherry Coral #E24E33 (accent, 2). The book's own rules: a ground is chosen
   and committed to, never split or graduated; gold is a LINE, never a field
   larger than a paragraph; coral is a single dot per surface. The two
   colourways are the two themes — navy-on-cream by day, cream-on-navy at
   night — which is exactly the book's "Reversals" page.

   TYPE (ch. 4) is three voices and six sizes. Hanken Grotesk carries
   everything at 200/300/400/500; Bodoni Moda appears in ITALIC only, once per
   surface, and never as a headline; Space Mono sets specification in tracked
   caps. Nothing is bold — the book is explicit that emphasis comes from scale
   and space, so 500 is the heaviest weight in this file.

   LAYOUT (ch. 5) allows one rule weight — a 1px hairline at 16–30% opacity —
   and states plainly: no boxes, no cards, no shadows anywhere in this system.
   The .card class survives as a NAME for a region, but it draws a hairline and
   space rather than a container.

   INTERFACE (ch. 9, the Roast Lab) adds three: instruments are cream on navy
   while charts stay navy-on-cream "so they read like paper"; actions are
   typeset words above a hairline, with ONE filled button per screen and only
   for the irreversible act; and the platform proposes while the person
   decides — which is this app's whole design already.

   Faces are self-hosted (SIL OFL), never a CDN. */
@font-face { font-family: "Hanken Grotesk"; font-weight: 100 900; font-style: normal;
  font-display: swap; src: url("/static/fonts/hanken-var.woff2") format("woff2"); }
@font-face { font-family: "Bodoni Moda"; font-weight: 400 500; font-style: italic;
  font-display: swap; src: url("/static/fonts/bodoni-500i.woff2") format("woff2"); }
@font-face { font-family: "Space Mono"; font-weight: 400; font-style: normal;
  font-display: swap; src: url("/static/fonts/spacemono-400.woff2") format("woff2"); }
@font-face { font-family: "Space Mono"; font-weight: 500; font-style: normal;
  font-display: swap; src: url("/static/fonts/spacemono-700.woff2") format("woff2"); }
:root {
  color-scheme: light;
  --plane: #EFE8D6;              /* Bone Cream — the ground, committed to */
  --surface: #EFE8D6;            /* no cards: a region is the same paper */
  --ink: #141F38;                /* Midnight Navy */
  --ink-2: #3d485f;
  --muted: #6b7385;
  --grid: rgba(20, 31, 56, .10);
  --baseline: rgba(20, 31, 56, .22);
  --border: rgba(20, 31, 56, .18);   /* the one hairline, 16–30% */
  --border-soft: rgba(20, 31, 56, .10);
  --brand: #141F38;
  --brand-tile: #141F38;
  --accent-gold: #B98214;        /* gold, darkened for type on cream */
  --accent-gold-line: #E0A43B;   /* the true hex, for rules and fills */
  --accent-coral: #C3341A;       /* coral, darkened for type on cream */
  --accent-coral-line: #E24E33;
  /* Charts read like paper: ink first, then the two accents, then a tint. */
  --series-1: #141F38; --series-2: #B98214;
  --series-3: #C3341A; --series-4: #6b7385;
  --seq-250: rgba(20,31,56,.14); --seq-350: rgba(20,31,56,.30);
  --seq-450: rgba(20,31,56,.48); --seq-550: rgba(20,31,56,.68);
  --seq-650: #141F38;
  /* Only trouble is coloured. "Fine" is the absence of alarm, not a green. */
  --good: #6b7385; --good-text: #3d485f;
  --warning: #B98214; --serious: #C3341A; --critical: #C3341A;
  --wash-1: rgba(20, 31, 56, .045);
  --btn-bg: #141F38; --btn-fg: #EFE8D6;
  --focus: #B98214;
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --plane: #141F38;            /* Midnight Navy — the book's primary ground */
    --surface: #141F38;
    --ink: #EFE8D6;              /* Bone Cream */
    --ink-2: #cdd3dd;
    --muted: #949cad;
    --grid: rgba(239, 232, 214, .10);
    --baseline: rgba(239, 232, 214, .22);
    --border: rgba(239, 232, 214, .20);
    --border-soft: rgba(239, 232, 214, .11);
    --brand: #EFE8D6;
    --brand-tile: #EFE8D6;
    --accent-gold: #E0A43B;
    --accent-gold-line: #E0A43B;
    --accent-coral: #E24E33;
    --accent-coral-line: #E24E33;
    --series-1: #EFE8D6; --series-2: #E0A43B;
    --series-3: #E24E33; --series-4: #949cad;
    --seq-250: rgba(239,232,214,.16); --seq-350: rgba(239,232,214,.32);
    --seq-450: rgba(239,232,214,.50); --seq-550: rgba(239,232,214,.70);
    --seq-650: #EFE8D6;
    --good: #949cad; --good-text: #cdd3dd;
    --warning: #E0A43B; --serious: #E24E33; --critical: #E24E33;
    --wash-1: rgba(239, 232, 214, .055);
    --btn-bg: #EFE8D6; --btn-fg: #141F38;
    --focus: #E0A43B;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --plane: #141F38;      /* Midnight Navy — the book's primary ground */
  --surface: #141F38;
  --ink: #EFE8D6;        /* Bone Cream */
  --ink-2: #cdd3dd;
  --muted: #949cad;
  --grid: rgba(239, 232, 214, .10);
  --baseline: rgba(239, 232, 214, .22);
  --border: rgba(239, 232, 214, .20);
  --border-soft: rgba(239, 232, 214, .11);
  --brand: #EFE8D6;
  --brand-tile: #EFE8D6;
  --accent-gold: #E0A43B;
  --accent-gold-line: #E0A43B;
  --accent-coral: #E24E33;
  --accent-coral-line: #E24E33;
  --series-1: #EFE8D6; --series-2: #E0A43B;
  --series-3: #E24E33; --series-4: #949cad;
  --seq-250: rgba(239,232,214,.16); --seq-350: rgba(239,232,214,.32);
  --seq-450: rgba(239,232,214,.50); --seq-550: rgba(239,232,214,.70);
  --seq-650: #EFE8D6;
  --good: #949cad; --good-text: #cdd3dd;
  --warning: #E0A43B; --serious: #E24E33; --critical: #E24E33;
  --wash-1: rgba(239, 232, 214, .055);
  --btn-bg: #EFE8D6; --btn-fg: #141F38;
  --focus: #E0A43B;
}

* { box-sizing: border-box; }
a, button, input, select, textarea, summary { touch-action: manipulation; }
/* One focus ring, in the brand's own gold. The browser's default is a blue
   rounded rectangle that belongs to no palette in this book, and it stays on a
   button after a mouse click — :focus-visible is the browser's own answer to
   that, so a keyboard gets the ring and a click does not. Controls that
   indicate focus by moving their own hairline (every input here) set
   outline:none on :focus and keep doing so: their selectors are stronger. */
:focus-visible {
  outline: 2px solid var(--accent-gold-line);
  outline-offset: 2px;
}
/* for a label a screen reader needs and the page does not */
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden;
      clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
/* a display: rule on a class beats the UA's [hidden], which was leaving
   collapsed panels (the invoice payment and credit-note forms) on screen */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
html {
  /* Safari on a phone helpfully enlarges the text of any column it decides is
     too narrow, which on turning the handset sideways silently rewrites every
     figure on the page at a size nobody chose. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  background: var(--plane);
  color: var(--ink);
  /* ch.4 — Hanken Grotesk carries body, captions and every control */
  font: 14px/1.45 "Hanken Grotesk", "Helvetica Neue", Arial, system-ui, sans-serif;
}
/* A ticked box, a chosen radio and the highlight inside the native date picker
   are drawn by the operating system, and its default is the same blue as the
   focus ring this book already replaced. Gold is what the rest of the
   interface uses to mean "this one", so it is what these should use too. */
:root { accent-color: var(--accent-gold); }
/* Navy on the true gold, the same in both colourways, exactly as the countdown
   chip sets text on a gold field. Not `--brand-tile`, which is cream on navy
   ground and would put cream on gold. */
::selection { background: var(--accent-gold-line); color: #141f38; }
/* ch.4 — headings are Hanken at 200/300; Bodoni is italic accent only */
.display, .page-head h1, .brand-name, .login-word {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif;
  /* ch.4 "Light at size" and "Tighten the big": 200 above 60px, 300 below,
     and −2% tracking on anything display-sized. Never bold — emphasis is
     scale and space. */
  font-weight: 300;
  letter-spacing: -0.02em;
}
.display, .login-word { font-weight: 200; }
/* ch.4 "Ration the serif": Bodoni is italic, and appears once per surface —
   a tagline or two emphasised words. Never a headline. */
.manual-lede em {
  font-family: "Bodoni Moda", Georgia, serif; font-style: italic; font-weight: 400;
}
/* Brand Book ch.4 — Technical: Space Mono for labels, specs, data cues */
.eyebrow, .login-tag, .login-foot, .card .as-of, .tile .label, .ctl-label {
  font-family: "Space Mono", ui-monospace, "SFMono-Regular", monospace;
}
.card .as-of, .tile .label { letter-spacing: -0.01em; }
.eyebrow {
  text-transform: uppercase; letter-spacing: 0.22em; font-size: 10.5px;
  font-weight: 400; color: var(--muted);
}
a { color: inherit; }

.shell { display: flex; min-height: 100vh; }

/* ---------------------------------------------------------------------------
   The menu is a margin, not a panel. Edition 02 has no boxes, so the nav is
   set in the page's own paper and divided from the content by the one
   hairline; it collapses to an icon rail on desktop and slides away entirely
   on a phone. One width variable drives the rail and the space the content
   leaves it, so the two can never disagree.
   --------------------------------------------------------------------------- */
.sidebar {
  --rail: 220px;
  width: var(--rail); flex-shrink: 0; z-index: 30;   /* tooltips over content */
  background: none;
  border: 0; border-right: 1px solid var(--border); border-radius: 0;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
  margin: 0;
  transition: width .26s var(--ease);
  overflow: hidden;
}
.sidebar.rail { --rail: 66px; }
.sidebar.rail .brand-copy,
.sidebar.rail .nav-label,
.sidebar.rail .who-foot,
.sidebar.rail .build-foot,
.sidebar.rail .profile-tab-copy,
.sidebar.rail .btn-label { display: none; }
.sidebar.rail .ghost-btn {
  display: grid; place-items: center; font-size: 16px; padding: 10px 0;
}
.sidebar.rail .brand { justify-content: center; align-items: center; }
.sidebar.rail .brand-spacer { display: none; }
/* In the rail the control sits UNDER the mark, in the flow, rather than being
   crossfaded on top of it. The old version swapped the wordmark for a chevron
   whenever a pointer crossed the column: the brand disappeared on hover, which
   is a lot of movement to offer somebody who was on their way to a menu item.
   Nothing moves now — one quiet chevron that brightens when you approach it. */
.sidebar.rail .brand {
  flex-direction: column; gap: 6px; padding: 18px 0 10px;
}
.sidebar.rail .nav-toggle {
  opacity: .4; padding: 2px 7px;
  transition: opacity .18s var(--ease), color .18s var(--ease);
}
.sidebar.rail:hover .nav-toggle, .sidebar.rail .nav-toggle:focus-visible {
  opacity: 1;
}
.sidebar.rail nav a { justify-content: center; padding: 10px 0; }
/* the count becomes a dot on the icon rather than a pill with nowhere to sit */
.sidebar.rail nav a .badge {
  position: absolute; top: 5px; right: 12px;
  min-width: 8px; height: 8px; padding: 0; font-size: 0; border-radius: 999px;
  line-height: 0;
}
.sidebar.rail .sidebar-foot { padding: 8px; }
.sidebar.rail .ghost-btn { text-align: center; padding: 9px 0; }
/* The label reappears on hover as the shared #tooltip. It used to be an
   ::after on the link, which needed overflow:visible to escape the rail — and
   that let the links below the fold paint over the footer instead of
   scrolling. The shared tip is position:fixed, so the nav can clip normally. */
.rail-tip { white-space: nowrap; }

.nav-toggle {
  display: inline-flex; align-items: center; gap: 7px;
  background: none; border: 1px solid transparent; border-radius: 0;
  color: var(--ink-2); padding: 6px 7px; cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), opacity .2s var(--ease);
}
.nav-toggle:hover { background: var(--wash-1); color: var(--ink); border-color: var(--border); }
/* expanded on desktop the Flags link already carries the count, and the
   duplicate pill squeezes the wordmark onto three lines */
.sidebar:not(.rail) .nav-toggle .badge { display: none; }
.brand > div:not(.brand-mark) { min-width: 0; }
.brand-name, .brand-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-burger { width: 16px; height: 1.8px; background: currentColor; position: relative; border-radius: 2px; }
.nav-burger::before, .nav-burger::after {
  content: ""; position: absolute; left: 0; width: 16px; height: 1.8px;
  background: currentColor; border-radius: 2px;
  transition: transform .24s var(--ease), top .24s var(--ease), width .24s var(--ease);
}
.nav-burger::before { top: -5.5px; }
.nav-burger::after { top: 5.5px; }
/* collapsed: the bars become a chevron pointing the way it will open */
.sidebar.rail .nav-burger { background: transparent; }
.sidebar.rail .nav-burger::before { top: -3px; width: 10px; transform: rotate(45deg); }
.sidebar.rail .nav-burger::after { top: 3px; width: 10px; transform: rotate(-45deg); }

.nav-ico { width: 19px; height: 19px; flex: none; opacity: .85; }
.sidebar nav a.active .nav-ico { opacity: 1; }
.nav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* phone-only controls */
.nav-scrim, .nav-fab { display: none; }

.brand { display: flex; gap: 10px; align-items: center; padding: 20px 16px 14px; }
.brand-lockup { order: 0; display: flex; align-items: center; gap: 10px; min-width: 0; }
.brand-copy { min-width: 0; }
.sidebar .brand-spacer { order: 1; flex: 1; }
.sidebar .theme-switch { order: 2; }
.sidebar .nav-toggle { order: 3; }
.brand-mark {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  color: var(--ink);              /* primary/inverse colourway: navy on cream, cream on navy */
}
.brand-mark .imv-mark { width: 32px; height: auto; display: block; }
.brand-name { font-size: 13.5px; color: var(--brand); }
.brand-sub { font-size: 10.5px; color: var(--muted); letter-spacing: 0.04em; }
.theme-switch {
  flex: none; width: 44px; height: 44px; padding: 0;
  display: inline-grid; place-items: center;
  border: 0; background: transparent; color: var(--ink-2); cursor: pointer;
}
.theme-switch-track {
  position: relative; display: block; width: 34px; height: 20px;
  border: 1px solid var(--border); background: var(--wash-1);
}
.theme-switch-thumb {
  position: absolute; top: 3px; left: 3px; width: 12px; height: 12px;
  background: var(--ink-2);
  transition: transform .18s var(--ease), background .18s var(--ease);
}
:root[data-theme="dark"] .theme-switch-thumb {
  transform: translateX(14px); background: var(--brand);
}
.theme-switch:hover .theme-switch-track { border-color: var(--accent-gold-line); }
.sidebar.rail .brand-lockup { justify-content: center; }
/* flex:1 + min-height:0 so the nav takes whatever is left after the footer and
   scrolls inside it. Without min-height a flex child refuses to shrink below its
   content, and the last link (Audit trail) simply falls off the bottom. */
.sidebar nav { display: flex; flex-direction: column; padding: 4px 12px 8px; gap: 2px;
               flex: 1 1 auto; min-height: 0;
               overflow-y: auto; overflow-x: hidden;
               scrollbar-width: thin; }
/* Grouped by the question each page answers, so the list reads as four short
   decisions rather than twelve equal choices. */
.nav-group { display: flex; flex-direction: column; gap: 2px; }
.nav-group + .nav-group { margin-top: 12px; }
.nav-search { margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--border-soft); }
.nav-search kbd { margin-left: auto; min-width: 20px; padding: 1px 5px;
  border: 1px solid var(--border); color: var(--muted); text-align: center;
  font: 10px/1.35 "Space Mono", ui-monospace, monospace; }
.sidebar.rail .nav-search kbd { display: none; }
.nav-group-title {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .15em;
  color: var(--muted); padding: 4px 10px 3px;
}
/* in the rail the heading collapses to a hairline — the grouping survives,
   the words don't need to */
.sidebar.rail .nav-group-title {
  font-size: 0; letter-spacing: 0; padding: 0; height: 1px; margin: 9px 14px 8px;
  background: var(--border);
}
.sidebar nav a {
  padding: 8px 10px; border-radius: 0; text-decoration: none;
  color: var(--ink-2); display: flex; align-items: center; gap: 10px;
  position: relative; transition: background .16s var(--ease), color .16s var(--ease);
}
.sidebar nav a:hover { background: var(--wash-1); color: var(--ink); }
.sidebar nav a.active { background: var(--wash-1); color: var(--ink); font-weight: 500; }
.sidebar nav a.active::before {
  content: ""; position: absolute; left: -8px; top: 50%; translate: 0 -50%;
  width: 3px; height: 20px; border-radius: 0 3px 3px 0; background: var(--accent-gold);
}
.sidebar-foot { flex: none; padding: 12px 16px 16px; display: flex; flex-direction: column;
                gap: 8px; border-top: 1px solid var(--border-soft); }
.profile-tab { min-width: 0; padding: 7px 2px 8px; }
.profile-tab.active { color: var(--ink); border-bottom-color: var(--accent-gold-line); }
.profile-tab-copy { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.profile-tab-copy strong, .profile-tab-copy small {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.profile-tab-copy strong { color: var(--ink); font-size: 12.5px; font-weight: 550; }
.profile-tab-copy small { color: var(--muted); font-size: 10.5px; }
.shopkeeper-primary {
  min-height: 42px; background: color-mix(in srgb, var(--accent-gold) 10%, transparent);
  border-left: 3px solid var(--accent-gold); font-weight: 600;
}
.shopkeeper-guide {
  display: flex; flex-direction: column; gap: 4px; margin-bottom: 14px;
  border-left: 3px solid var(--accent-gold);
}
.shopkeeper-guide strong { font-size: 13px; }
.shopkeeper-guide span { color: var(--muted); font-size: 12px; }
.sidebar.rail .profile-tab { justify-content: center; padding: 10px 0; }
.foot-row { display: flex; gap: 6px; }
.foot-row > * { flex: 1; min-width: 0; }
.sidebar.rail .foot-row { flex-direction: column; }
.ghost-btn {
  border: 0; border-bottom: 1px solid var(--border-soft);
  background: none; color: var(--ink-2);
  padding: 6px 2px 5px; border-radius: 0; cursor: pointer; font: inherit;
  text-align: left; text-decoration: none; font-size: 13px;
}
.ghost-btn:hover { color: var(--ink); border-bottom-color: var(--accent-gold-line); }
.ghost-btn { display: flex; align-items: center; gap: 7px; }
.btn-ico { flex: none; width: 15px; text-align: center; }
/* a railed footer button names itself the same way the nav links do */
.sidebar.rail .ghost-btn { position: relative; }
.sidebar.rail .ghost-btn::after {
  content: attr(data-label); position: absolute; left: calc(100% + 12px);
  top: 50%; transform: translateY(-50%) translateX(-4px);
  background: var(--ink); color: var(--surface); font-size: 12px;
  padding: 5px 9px; border-radius: 0; white-space: nowrap;
  opacity: 0; pointer-events: none; z-index: 60;
  transition: opacity .16s var(--ease), transform .16s var(--ease);
}
.sidebar.rail .ghost-btn:hover::after, .sidebar.rail .ghost-btn:focus-visible::after {
  opacity: 1; transform: translateY(-50%) translateX(0);
}
.who-foot {
  font-size: 11px; color: var(--muted); padding: 0 2px 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* ch.4 "specification": a build number is a serial, not prose. */
.build-foot {
  font-family: "Space Mono", monospace; font-size: 9.5px; letter-spacing: .08em;
  color: var(--muted); opacity: .65; padding: 0 2px 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.main { flex: 1; padding: 26px 30px 72px; min-width: 0; }
/* The one thing allowed to shout. Coral is the single dot of alarm in this
   book and this is what it is for: everything else on the page is worthless if
   none of it is being kept. */
.storage-warning {
  border-left: 2px solid var(--accent-coral-line);
  background: color-mix(in srgb, var(--accent-coral-line) 10%, transparent);
  padding: 12px 16px; margin: 0 0 18px; font-size: 13px; line-height: 1.55;
  max-width: 90ch; overflow-wrap: anywhere;
}
.storage-warning strong { display: block; color: var(--accent-coral); }

.page-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
.page-head h1 { font-size: 23px; margin: 0; color: var(--brand); }
.page-head .sub { color: var(--muted); font-size: 13px; }
.page-head .spacer { flex: 1; }

.grid { display: grid; gap: 14px; }
.grid.tiles { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.grid.two { grid-template-columns: 1fr 1fr; }
@media (max-width: 1100px) { .grid.two { grid-template-columns: 1fr; } }
/* a grid item defaults to min-width:auto, so a wide table inside a card stops
   the card shrinking and the whole page scrolls sideways. */
.grid > * { min-width: 0; }
.card > * { max-width: 100%; }

.card {
  /* ch.5: "no boxes, no cards, no shadows anywhere in this system." The class
     stays as a NAME for a region; what it draws is a hairline and air. */
  background: none;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: 14px 16px;
}
.card h2 {
  margin: 0 0 10px; font-size: 13px; font-weight: 500; color: var(--ink-2);
  letter-spacing: 0.01em;
}
.card .as-of { color: var(--muted); font-weight: 400; float: right; font-size: 12px; }

/* stat tiles */
.tile .label { font-size: 12px; color: var(--ink-2); }
.tile .value { font-size: 30px; font-weight: 200; margin-top: 4px;
               font-family: "Hanken Grotesk", sans-serif; }
.tile .delta { font-size: 12px; margin-top: 2px; color: var(--muted); }
.tile .delta.up { color: var(--good-text); }
.tile .delta.down { color: var(--critical); }
.tile .note { font-size: 11px; color: var(--muted); margin-top: 3px; }
.account-tile { color: inherit; text-decoration: none; cursor: pointer; }
.account-tile:hover, .account-tile:focus-visible {
  border-color: var(--accent-gold-line);
  background: var(--wash-1);
}
.account-tile:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.customer-record-link {
  color: inherit; text-decoration-line: underline; text-decoration-style: dotted;
  text-decoration-color: var(--muted); text-underline-offset: 3px;
}
.customer-record-link:hover, .customer-record-link:focus-visible {
  color: var(--link); text-decoration-style: solid; text-decoration-color: currentColor;
}

/* status & badges — icon + label, never color alone */
.badge {
  font-size: 11px; padding: 1px 7px; border-radius: 0;
  border: 1px solid var(--border); color: var(--ink-2); margin-left: auto;
}
.badge-critical { background: var(--critical); color: #fff; border-color: transparent; }
.badge-warning { background: var(--warning); color: #402c00; border-color: transparent; }
.status {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 550;
}
.status::before { content: ""; width: 8px; height: 8px; border-radius: 50%; }
.status.ok::before, .status.paid::before { background: var(--good); }
.status.lagging::before, .status.warning::before, .status.awaiting::before,
.status.partial::before { background: var(--warning); }
.status.stale::before, .status.critical::before, .status.disputed::before { background: var(--critical); }
.status.never::before, .status.info::before, .status.cancelled::before { background: var(--muted); }
.status.auto::before { background: var(--seq-350); }
.status.confirmed::before { background: var(--good); }
.status.unmatched::before { background: var(--serious); }
.status.ignored::before { background: var(--muted); }

/* tables */
table.data { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data th {
  text-align: left; color: var(--muted); font-weight: 550; font-size: 12px;
  padding: 6px 8px; border-bottom: 1px solid var(--grid); white-space: nowrap;
}
table.data td { padding: 7px 8px; border-bottom: 1px solid var(--grid); vertical-align: top; }
table.data tr:last-child td { border-bottom: 0; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data .dim { color: var(--muted); }
.scroll-x { overflow-x: auto; }

/* charts */
.chart { width: 100%; }
.chart-empty { font-size: 12px; padding: 22px 0; margin: 0; }
.chart svg { display: block; width: 100%; height: auto; }
.legend { display: flex; gap: 14px; flex-wrap: wrap; margin: 2px 0 8px; font-size: 12px; color: var(--ink-2); }
.legend .key { display: inline-flex; align-items: center; gap: 6px; }
.legend .swatch { width: 10px; height: 10px; border-radius: 0; }

.tooltip {
  position: fixed; z-index: 50; pointer-events: none;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 0;
  padding: 8px 10px; font-size: 12px;
  max-width: 260px;
}
.tooltip .tt-title { font-weight: 500; margin-bottom: 4px; }
.tooltip .tt-row { display: flex; gap: 8px; justify-content: space-between; }
.tooltip .tt-row .k { display: inline-flex; align-items: center; gap: 5px; color: var(--ink-2); }
.tooltip .swatch { width: 8px; height: 8px; border-radius: 0; display: inline-block; }

/* issues */
.issue {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px;
  padding: 10px 4px; border-bottom: 1px solid var(--grid); align-items: start;
}
.issue:last-child { border-bottom: 0; }
.issue .title { font-weight: 500; }
.issue .detail { color: var(--ink-2); font-size: 12.5px; margin-top: 2px; max-width: 72ch; }
.issue .meta { color: var(--muted); font-size: 11.5px; margin-top: 3px; }
.issue .actions { display: flex; gap: 6px; align-items: flex-start; }
/* a flag someone has put their name to reads differently from one nobody owns */
.issue .stamp:empty { display: none; }
.issue .stamp {
  margin-top: 5px; font-size: 11.5px; color: var(--ink-2);
  border-left: 1px solid var(--accent-gold-line); padding-left: 9px;
}
.issue .stamp .who { font-weight: 650; color: var(--ink); }
.issue.acknowledged { background: var(--wash-1); }
.issue.acknowledged .stamp { border-left-color: var(--accent-sky); }
.issue.resolved { opacity: .62; }
.issue.resolved .stamp { border-left-color: var(--good); }
.issue.just-changed { animation: flagsettle .9s var(--ease); }
@keyframes flagsettle {
  from { background: color-mix(in srgb, var(--accent-gold) 22%, transparent); }
  to { background: transparent; }
}
.issue-note {
  grid-column: 2 / -1; display: flex; gap: 6px; margin-top: 8px; align-items: center;
}
.issue-note input {
  flex: 1; min-width: 0; padding: 5px 9px; font: inherit; font-size: 12.5px;
  border: 1px solid var(--border); border-radius: 0;
  background: var(--surface); color: var(--ink);
}
.issue-note input:focus { outline: none; border-color: var(--brand); }
.issue-note input.bad { border-color: var(--critical); }
.card.explain p {
  margin: 0 0 8px; font-size: 12.5px; color: var(--ink-2); max-width: 84ch; line-height: 1.55;
}
.card.explain p:last-child { margin-bottom: 0; }
.card.explain strong { color: var(--ink); }
.sev {
  width: 10px; height: 10px; border-radius: 50%; margin-top: 5px;
  outline: 3px solid transparent;
}
.sev.critical { background: var(--critical); }
.sev.warning { background: var(--warning); }
.sev.info { background: var(--muted); }

/* An action must look tappable before somebody reads it. Earlier releases
   reduced secondary actions to words above a hairline; on an iPad that reads
   exactly like an ordinary link. All explicit actions now keep a full border,
   useful padding and a reliable touch target. Prose navigation remains a
   normal <a> and is unaffected. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle; min-height: 38px; line-height: 1.2;
  font: inherit; font-size: 12.5px; font-weight: 500;
  padding: 7px 12px; border-radius: 0;
  border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); cursor: pointer;
  text-decoration: none;
}
.btn { appearance: none; }
/* A file picker is an action followed by a filename, not an operating-system
   widget dropped into an Imvelo form. Its button is deliberately part of the
   same rule above as every other secondary action, so every upload door —
   feeds, purchases, supplier prices, staff, contracts and lot documents —
   gets the same typography, hairline and touch target without page-by-page
   exceptions. The native input remains intact for keyboard, drag/drop and
   screen-reader behaviour. */
input[type=file] {
  max-width: 100%; color: var(--muted); font: inherit; font-size: 12.5px;
  line-height: 38px; cursor: pointer;
}
input[type=file]::file-selector-button {
  appearance: none; display: inline-flex; align-items: center;
  justify-content: center; vertical-align: middle;
  min-height: 38px; line-height: 1.2; margin-right: 10px;
  padding: 7px 12px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--ink); font: inherit; font-size: 12.5px; font-weight: 500;
  cursor: pointer;
}
.btn + .btn { margin-left: 8px; }
.btn:hover,
input[type=file]:hover::file-selector-button {
  color: var(--ink); background: var(--wash-1);
  border-color: var(--accent-gold-line);
}
.btn:disabled,
input[type=file]:disabled::file-selector-button {
  opacity: .45; cursor: default; border-color: var(--border-soft);
}
input[type=file]:disabled { cursor: default; }
input[type=file]:focus-visible { outline-offset: 3px; }
.btn.primary {
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bg); border-radius: 0;
  padding: 7px 15px; font-weight: 600;
}
.btn.primary:hover { filter: none; opacity: .88; border-color: var(--btn-bg); }
.btn.btn-quiet { border-color: var(--border-soft); color: var(--muted);
                 background: transparent; }
.btn.btn-quiet:hover { color: var(--ink); background: var(--wash-1);
                       border-color: var(--border); }
.btn.invoice-erase { color: var(--critical); border-color: var(--accent-coral-line);
                     background: transparent; min-height: 40px; }
.btn.invoice-erase:hover { color: var(--critical); border-color: var(--critical);
                           background: var(--wash-1); }
.purchase-prices-entry { margin-bottom: 14px; }
.supplier-price-upload form { margin-top: 10px; }
.price-file-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
                  margin-top: 12px; }
.price-file-row input[type=file] { flex: 1 1 270px; min-height: 42px; }
.price-version-head { display: flex; justify-content: space-between; gap: 12px;
                      align-items: center; }
.compare-picker label { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.price-change-summary { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; }
@media (max-width: 820px) {
  .price-file-row > *, .price-version-head .btn { width: 100%; }
  .price-version-head { align-items: stretch; flex-direction: column; }
  .supplier-price-upload .btn, .compare-picker select { min-height: 46px; }
}

/* feeds */
.feed-row { display: grid; grid-template-columns: 1fr auto auto auto; gap: 12px; align-items: center; }
.feed-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 12px; }
.feed-card { border: 0; border-top: 1px solid var(--border); border-radius: 0;
             padding: 12px 2px 14px 0; }
.feed-card .name { font-weight: 500; display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.feed-card .facts { color: var(--muted); font-size: 12px; margin: 6px 0 10px; }
/* The file, the button, and then whatever the upload has to say — which is a
   paragraph often enough that it cannot be a third column. It wraps to a full
   line of its own; without the wrap it was squeezed into about ninety pixels
   beside the button and set one word per line. */
.feed-card form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.feed-card .purchase-context-fields { flex: 1 0 100%; margin-bottom: 4px; }
.feed-card .purchase-context-fields label { min-width: 210px; }
.invoice-source-choice { display:flex; align-items:flex-end; gap:10px; flex:1 1 720px; }
.invoice-source-choice label { display:grid; gap:5px; color:var(--muted);
                               font-size:11.5px; font-weight:500; }
.invoice-file-source { flex:0 1 230px; }
.invoice-link-source { flex:1 1 430px; }
.invoice-link-source input { width:100%; min-height:40px; padding:8px 10px;
                             border:1px solid var(--border); background:var(--surface);
                             color:var(--ink); font:inherit; }
.invoice-link-source input:focus { outline:none; border-color:var(--accent-gold-line);
                                   box-shadow:0 0 0 2px var(--wash-1); }
.invoice-source-or { align-self:center; color:var(--muted); font-size:11px;
                     text-transform:uppercase; letter-spacing:.08em; }
.feed-card input[type=file] { font-size: 12px; color: var(--ink-2); max-width: 240px; }
/* Uploading and resolving a feed are jobs, not prose links. Make both actions
   unmistakable touch targets without turning every quiet link in the app into
   a filled control. */
.feed-card .feed-action,
.feed-card .feed-followup {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 40px; padding: 8px 15px; border: 1px solid var(--btn-bg);
  font-weight: 600; line-height: 1.2; text-align: center;
}
.feed-card .feed-action { background: var(--btn-bg); color: var(--btn-fg); }
.feed-card .feed-followup {
  margin: 8px 0 0; background: var(--surface); color: var(--ink);
  border-color: var(--border); text-decoration: none;
}
.feed-card .feed-followup:hover {
  background: var(--wash-1); border-color: var(--accent-gold-line); color: var(--ink);
}

/* The bank accounts a single statement door covers. Rows inside the card,
   not cards of their own: there is one door, and three separate cards each
   nagging in its own right read as three jobs instead of one. */
.feed-accounts { margin-top: 10px; border-top: 1px solid var(--border); padding-top: 8px; }
.feed-accounts-head {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 6px;
}
.feed-account {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12px; padding: 3px 0;
}
.feed-account strong { font-weight: 500; min-width: 88px; }
.feed-account .dim { font-size: 11.5px; }

/* Optional reports and the historic import: present, but folded away. */
.feed-fold > summary { cursor: pointer; padding: 8px 0; }
.feed-fold > summary strong { font-size: 14px; }

.flash { padding: 10px 14px; border-radius: 0; margin-bottom: 14px; font-size: 13px; }
.flash.ok { background: var(--wash-1); }
.flash.error { background: var(--critical); color: #fff; }

.filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.filters a, .filters .chip {
  font-size: 12.5px; padding: 5px 11px; border-radius: 0;
  border: 1px solid var(--border); text-decoration: none; color: var(--ink-2);
}
.filters a.on { background: var(--wash-1); color: var(--ink); font-weight: 500; }
.filters input[type=search] {
  font: inherit; font-size: 13px; padding: 5px 10px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
}

/* Audit questions nearly always combine several clues: who, what, and when.
   Keep those controls together and large enough to operate on an iPad. */
.audit-filter-form {
  display: grid;
  grid-template-columns: minmax(220px, 1.6fr) repeat(3, minmax(145px, 1fr))
                         repeat(2, minmax(130px, .8fr)) auto;
  gap: 10px; align-items: end; margin: 0 0 14px;
  padding: 12px; border: 1px solid var(--border); background: var(--surface);
}
.audit-filter-form label {
  display: grid; gap: 5px; min-width: 0;
  color: var(--muted); font-size: 11px; font-weight: 550;
}
.audit-filter-form input, .audit-filter-form select {
  width: 100%; min-width: 0; min-height: 42px;
}
.audit-filter-actions { display: flex; align-items: center; white-space: nowrap; }
.audit-filter-actions .btn { min-height: 42px; }

@media (max-width: 1180px) {
  .audit-filter-form { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .audit-search { grid-column: span 2; }
}

@media (max-width: 700px) {
  .audit-filter-form { grid-template-columns: 1fr 1fr; }
  .audit-search { grid-column: 1 / -1; }
  .audit-filter-actions { grid-column: 1 / -1; }
  .audit-filter-actions .btn { flex: 1; }
}

@media (max-width: 460px) {
  .audit-filter-form { grid-template-columns: 1fr; }
  .audit-search, .audit-filter-actions { grid-column: auto; }
}

/* One search follows the signed-in user through the whole app. Results are
   grouped by the room they live in, rather than becoming one long mixed list
   of staff, ingredients and bank lines that happen to share a word. */
.global-search-form { margin-bottom: 14px; }
.global-search-form > label { display: block; font-size: 12px; font-weight: 550; margin-bottom: 7px; }
.global-search-form p { margin: 8px 0 0; font-size: 11.5px; }
.global-search-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; }
.global-search-row input { width: 100%; min-height: 42px; font-size: 15px; }
.global-search-row .btn { min-height: 42px; min-width: 110px; }
.search-summary { margin: 4px 0 12px; color: var(--muted); font-size: 12.5px; }
.search-groups { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.search-group { margin: 0; min-width: 0; }
.search-group h2 { display: flex; align-items: baseline; gap: 8px; }
.search-results { border-top: 1px solid var(--border); }
.search-result { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 11px 2px; border-bottom: 1px solid var(--border-soft);
  text-decoration: none; min-height: 52px; }
.search-result:hover { background: var(--wash-1); }
.search-result > span:first-child { min-width: 0; }
.search-result strong, .search-result small { display: block; }
.search-result strong { overflow: hidden; text-overflow: ellipsis; }
.search-result small { color: var(--muted); margin-top: 2px; font-size: 11.5px; }
.search-kind { flex: none; color: var(--muted); font: 10px "Space Mono", ui-monospace, monospace;
  text-transform: uppercase; letter-spacing: .05em; }

@media (max-width: 760px) {
  .global-search-row { grid-template-columns: 1fr; }
  .global-search-row .btn { width: 100%; }
  .search-groups { grid-template-columns: 1fr; }
  .search-kind { display: none; }
}
/* ---------------------------------------------------------------------------
   Dropdowns, in this book's own hand.

   A <select> is two things and the browser used to draw both: the closed
   control, and the list that opens out of it. The closed one has been ours for
   a while — it renders in the UA's white otherwise, which breaks the cream and
   navy in both themes. The open one was the operating system's: a dark
   rounded panel with a tick and its own type, floating on a bone-coloured
   page. The same complaint the customer picker was built to answer, still
   true of every other list in the app.

   Two layers. The first is the closed control and works everywhere. The
   second is the open list and works where the browser can draw it from CSS;
   where it cannot, the native popup opens exactly as it does today. Nothing
   here needs JavaScript, and not one of the 27 selects changes in the markup:
   they stay <select>, so the keyboard, the type-ahead and the screen reader
   are the browser's, which is the only way that is ever right.
   --------------------------------------------------------------------------- */
select {
  font: inherit; border-radius: 0; padding: 5px 9px;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  /* the UA's own chevron and bevel go, and ours goes in its place — one
     hairline drawn as a caret, in the muted ink, with room kept for it */
  -webkit-appearance: none; 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 13px top 50%, right 9px top 50%;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
select:focus {
  outline: none; border-color: var(--brand);
}
select:disabled { opacity: .55; background-image: none; }
/* A multiple or sized select is a list box, not a dropdown: no caret. */
select[multiple], select[size]:not([size="1"]) {
  background-image: none; padding-right: 9px;
}

@supports (appearance: base-select) {
  /* Desktop only, and deliberately. On a phone the native picker is not a
     panel floating on our page — it replaces the page, full-bleed and drawn by
     the system, with momentum scroll and type-ahead. There is no cream showing
     through it, so there is nothing foreign to fix; and an in-page list would
     hand back 27px rows on the screen where this file guarantees a 38px floor
     (see the touch-target block below). The roastery's longest lists — every
     unfinished lot, every SKU — are exactly the ones used on a phone. */
  @media (hover: hover) and (pointer: fine) {
    select, ::picker(select) { appearance: base-select; }

    /* base-select draws its own ::picker-icon, so the caret painted into the
       background above is a second arrow beside it. One or the other. */
    select { background-image: none; padding-right: 12px; }
    select::picker-icon { color: var(--muted); transition: transform .18s var(--ease); }
    select:open::picker-icon { transform: rotate(180deg); }

    /* The same paper, hairline and air as the customer picker, because they
       are the same object: a list of things to choose from, on the page. */
    ::picker(select) {
      background: var(--plane); border: 1px solid var(--border);
      border-radius: 0; padding: 0; margin-top: 2px;
      max-height: 320px; overflow: auto; overscroll-behavior: contain;
      box-shadow: none;
    }
    ::picker(select):popover-open { display: block; }
    option {
      /* Set outright, not inherited: an option inside an optgroup would
         otherwise pull down the group label's monospace. */
      font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif;
      font-size: 13px; color: var(--ink);
      padding: 7px 11px; border-bottom: 1px solid var(--border);
      display: flex; align-items: center; gap: 8px;
    }
    option:last-of-type { border-bottom: 0; }
    option:hover, option:focus { background: var(--wash-1); }
    /* ch.9: the chosen one is marked by a gold rule, the way the picker marks
       it and the way an active nav row does. Not by a coloured field. */
    option:checked { box-shadow: inset 2px 0 0 var(--accent-gold); }
    option::checkmark { content: ""; display: none; }
    /* ch.4 "specification": a group heading is a label, so it is tracked caps
       in the mono face, like every other label in this system. */
    optgroup {
      font-family: "Space Mono", ui-monospace, monospace;
      font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
      color: var(--muted); padding: 9px 11px 3px;
    }
  }
}

/* inline per-card variable controls (segmented links that reload with params) */
.seg {
  display: inline-flex; gap: 0; border: 1px solid var(--border);
  border-radius: 0; overflow: hidden; vertical-align: middle;
}
.seg a {
  padding: 3px 9px; font-size: 12px; text-decoration: none; color: var(--ink-2);
  border-right: 1px solid var(--border);
}
.seg a:last-child { border-right: 0; }
.seg a:hover { background: var(--wash-1); }
.seg a.on { background: var(--brand); color: var(--surface); font-weight: 500; }
.ctl-label { font-size: 11px; color: var(--muted); margin-right: 6px; }
.card h2 .ctl { float: right; font-weight: 400; display: inline-flex; align-items: center; gap: 6px; }

/* P&L statement table */
table.pl td.amt { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.pl tr.rule td { border-top: 1px solid var(--baseline); }
table.pl tr.total td { font-weight: 650; border-top: 1.5px solid var(--ink-2); }
table.pl tr.sub td { color: var(--ink-2); }
.var-fav { color: var(--good-text); }
.var-adv { color: var(--critical); }
.metric-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px,1fr)); gap: 10px; }
.chip-key { display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--ink-2); }
.chip-key .dot { width:10px; height:10px; border-radius:0; }

/* login — the brand's primary "on navy" colourway, editorial layout */
.login-wrap {
  color-scheme: dark;   /* the stage is always navy, whatever theme the app is in */
  min-height: 100vh; display: grid; place-items: center;
  background: #141F38;   /* ch.3: one flat ground, never graduated */
  color: #efe8d6; padding: 24px;
}
.login-stage { width: min(420px, 92vw); text-align: center; }
.login-lock { color: #efe8d6; margin: 0 auto; }
.login-lock .imv-logo { width: min(300px, 74vw); height: auto; display: block; margin: 0 auto; }
.login-tag {
  text-transform: uppercase; letter-spacing: 0.34em; font-size: 11px;
  color: #e0a43b; margin-top: 12px; font-weight: 500; padding-left: 0.34em;
}
.login-rule {
  width: 44px; height: 2px; background: #e24e33; margin: 22px auto 22px; border: 0;
}
.login-sub {
  font-size: 13px; color: #b7bccb; letter-spacing: 0.02em; margin-bottom: 22px;
}
.login-form { display: flex; flex-direction: column; gap: 12px; text-align: left; }
.login-form input {
  /* the first screen anyone sees: two rules and a word, nothing boxed */
  width: 100%; font: inherit; font-size: 15px; padding: 11px 2px; border-radius: 0;
  border: 0; border-bottom: 1px solid rgba(239,232,214,0.28); background: none;
  color: #efe8d6; -webkit-appearance: none; appearance: none;
}
.login-form input::placeholder { color: #7f889c; }
.login-form input:focus {
  outline: none; border-bottom-color: var(--accent-gold-line);
}
/* browser autofill paints its own opaque slab — repaint it in the stage's navy
   so a remembered email doesn't sit in a grey box */
.login-form input:-webkit-autofill,
.login-form input:-webkit-autofill:hover,
.login-form input:-webkit-autofill:focus {
  -webkit-text-fill-color: #efe8d6;
  -webkit-box-shadow: 0 0 0 1000px #141F38 inset;
  caret-color: #efe8d6;
  border: 0; border-bottom: 1px solid rgba(239,232,214,0.28);
  transition: background-color 600000s;   /* stops the flash of UA yellow */
}
/* the login stage is always navy, so pin the button to cream regardless of theme */
.login-form .btn.primary {
  padding: 13px; border-radius: 0; font-size: 15px;
  background: #efe8d6; color: #141f38;
}
.login-form .btn.primary:hover { background: #fff; }
.login-foot {
  margin-top: 26px; font-size: 10.5px; letter-spacing: 0.22em; text-transform: uppercase;
  color: #6d7590;
}
.login-error {
  background: none; border: 0; border-left: 1px solid #E24E33;
  color: #f2c7bd; padding: 4px 0 4px 12px; font-size: 13px;
  margin-bottom: 4px; text-align: left;
}
.login-help {
  margin: 16px 0 0; font-size: 11.5px; line-height: 1.5; color: #9aa2ba;
  text-align: left;
}
.login-foot a { color: #9aa2ba; }
.login-foot a:hover { color: #efe8d6; }

/* The curator door belongs to the Art room: use the same cream ground as the
   desk and public gallery while leaving the management login navy. */
.curator-login .login-wrap { color-scheme: light; background: #efe8d6; color: #141f38; }
.curator-login .login-lock { color: #141f38; }
.curator-login .login-sub { color: rgba(20,31,56,.68); }
.curator-login .login-form input {
  border-bottom-color: rgba(20,31,56,.28); color: #141f38;
}
.curator-login .login-form input::placeholder { color: rgba(20,31,56,.5); }
.curator-login .login-form input:-webkit-autofill,
.curator-login .login-form input:-webkit-autofill:hover,
.curator-login .login-form input:-webkit-autofill:focus {
  -webkit-text-fill-color: #141f38;
  -webkit-box-shadow: 0 0 0 1000px #efe8d6 inset;
  caret-color: #141f38;
  border-bottom-color: rgba(20,31,56,.28);
}
.curator-login .login-form .btn.primary { background: #141f38; color: #efe8d6; }
.curator-login .login-form .btn.primary:hover { background: #0b1020; }
.curator-login .login-foot,
.curator-login .login-foot a { color: rgba(20,31,56,.55); }
.curator-login .login-foot a:hover { color: #b98214; }
.curator-login .login-error { color: #c3341a; }

/* A neutral threshold for people, such as the owner, who belong to both the
   financial tower and the Art room. It borrows both palettes without looking
   like either workspace has already been chosen. */
.workspace-wrap {
  min-height: 100svh; background: #efe8d6; color: #141f38;
  display: grid; place-items: center; padding: clamp(24px, 5vw, 72px);
}
.workspace-stage { width: min(920px, 100%); }
.workspace-mark svg { width: 48px; height: auto; color: #141f38; }
.workspace-kicker {
  margin: 22px 0 10px; font-family: "Space Mono", monospace; font-size: 11px;
  letter-spacing: .18em; text-transform: uppercase; color: #6b7385;
}
.workspace-stage h1 {
  margin: 0; font-size: clamp(38px, 6vw, 70px); font-weight: 200;
  letter-spacing: -.035em; line-height: 1;
}
.workspace-intro { margin: 18px 0 38px; color: #566078; font-size: 17px; }
.workspace-choices { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: rgba(20,31,56,.22); }
.workspace-choice {
  min-height: 300px; padding: clamp(26px, 4vw, 44px); text-decoration: none;
  display: flex; flex-direction: column; align-items: flex-start; transition: transform .16s var(--ease);
}
.workspace-choice:hover { transform: translateY(-3px); }
.workspace-choice.art { background: #f5f0e5; color: #141f38; border-top: 5px solid #b98214; }
.workspace-choice.tower { background: #141f38; color: #efe8d6; border-top: 5px solid #e24e33; }
.workspace-number { font: 11px "Space Mono", monospace; letter-spacing: .16em; opacity: .55; }
.workspace-choice strong { margin-top: 34px; font-size: clamp(24px, 3vw, 34px); font-weight: 300; line-height: 1.05; }
.workspace-choice > span:not(.workspace-number) { margin-top: 14px; max-width: 34ch; line-height: 1.55; opacity: .72; }
.workspace-choice em {
  margin-top: auto; padding-top: 34px; font: normal 11px "Space Mono", monospace;
  letter-spacing: .13em; text-transform: uppercase;
}
.workspace-choice.art em { color: #8e650d; }
.workspace-choice.tower em { color: #e0a43b; }
.workspace-logout {
  display: inline-block; margin-top: 24px; color: #6b7385; font: 10.5px "Space Mono", monospace;
  letter-spacing: .13em; text-transform: uppercase;
}
@media (max-width: 680px) {
  .workspace-wrap { place-items: start; }
  .workspace-choices { grid-template-columns: 1fr; }
  .workspace-choice { min-height: 240px; }
}

/* Staff arrive here on a phone, often at the door and in a hurry. Keep the
   brand but make every required action a labelled, thumb-sized target. */
.staff-login { padding: max(18px, env(safe-area-inset-top)) 18px
  max(22px, env(safe-area-inset-bottom)); align-items: start; }
.staff-login .login-stage { width: min(440px, 100%); padding-top: clamp(8px, 5vh, 44px); }
.staff-login .login-lock .imv-logo { width: min(220px, 58vw); }
.staff-login .login-tag { margin-top: 15px; }
.staff-login h1 {
  margin: 22px 0 6px; color: #efe8d6; font: 500 30px/1.1 var(--serif);
}
.staff-login .login-sub {
  max-width: 34ch; margin: 0 auto 24px; line-height: 1.5; font-size: 13px;
}
.staff-login .login-form { gap: 17px; }
.staff-login .login-form > label {
  color: #c9ceda; font-size: 12px; font-weight: 600; letter-spacing: .02em;
}
.staff-login .login-form > label > span:not(.staff-password-row) {
  float: right; color: #7f889c; font-weight: 400;
}
.staff-login .login-form input {
  min-height: 56px; padding: 13px 12px; margin-top: 6px; border: 1px solid
  rgba(239,232,214,.28); background: rgba(255,255,255,.035); font-size: 17px;
}
.staff-login .login-form input:focus { border-color: #e0a43b; }
.staff-password-row { position: relative; display: block; }
.staff-password-row input { padding-right: 68px !important; }
.staff-password-row button {
  position: absolute; right: 4px; top: 10px; bottom: 4px; min-width: 58px;
  border: 0; background: transparent; color: #e0a43b; font: 600 13px var(--sans);
}
.staff-login .login-form .btn.primary {
  min-height: 60px; margin-top: 2px; font-size: 17px; font-weight: 650;
}
.staff-login .login-help { font-size: 12px; line-height: 1.55; }
.staff-login .login-foot { margin-top: 24px; line-height: 1.6; }
@media (max-width: 480px) {
  .staff-login { place-items: start center; }
  .staff-login .login-stage { padding-top: 6px; }
  .staff-login h1 { font-size: 28px; }
}

/* ---- The employee portal: one person, their own documents, nothing else ----
   Read almost entirely on a phone, standing up, so it is a single column of
   large tap targets rather than a dashboard shrunk down. */
.portal-body { background: var(--plane); padding: 18px 14px 60px; }
.portal { max-width: 620px; margin: 0 auto; }
.portal-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 14px; flex-wrap: wrap; margin-bottom: 18px;
}
.portal-who { margin-bottom: 16px; }
.portal-who h1 {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; font-size: 25px; margin: 0 0 2px;
  color: var(--brand);
}
.portal-who .dim { font-size: 12.5px; }
.portal-card {
  background: none; border: 0; border-top: 1px solid var(--border);
  border-radius: 0; padding: 16px 0 18px; margin-bottom: 10px;
}
.portal-card h2 { font-size: 14px; margin: 0 0 10px; color: var(--brand); }
.portal-card > summary {
  cursor: pointer; list-style: none; font-size: 13px; font-weight: 500;
  color: var(--ink-2);
}
.portal-card > summary::-webkit-details-marker { display: none; }
.portal-card > summary::before { content: "\25B8 "; color: var(--muted); }
.portal-card[open] > summary::before { content: "\25BE "; }
.portal-first { border-color: var(--accent-gold); }
.portal-first p { font-size: 12.5px; margin: 0 0 12px; color: var(--ink-2); }

.payslip-list { list-style: none; margin: 0; padding: 0; }
.payslip-list > li { border-top: 1px solid var(--grid); }
.payslip-list > li:first-child { border-top: 0; }
.payslip-list a {
  display: flex; align-items: baseline; gap: 12px; padding: 14px 2px;
  text-decoration: none; color: var(--ink); min-height: 48px;
}
.payslip-list a:hover { background: var(--wash-1); }
.pl-month {
  font-family: "Space Mono", monospace; font-size: 13px; letter-spacing: .04em;
  min-width: 8ch;
}
.pl-net {
  flex: 1; text-align: right; font-size: 16px; font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.pl-net .dim { font-size: 11px; font-weight: 400; }
.pl-go { color: var(--muted); }
.pl-note {
  font-size: 11.5px; color: var(--warning); padding: 0 2px 12px; line-height: 1.5;
}
.portal-doc-list { list-style: none; margin: 0; padding: 0; }
.portal-doc-list li { border-top: 1px solid var(--grid); }
.portal-doc-list li:first-child { border-top: 0; }
.portal-doc-list a {
  min-height: 58px; padding: 12px 2px; display: flex; align-items: center;
  justify-content: space-between; gap: 14px; color: var(--ink); text-decoration: none;
}
.portal-doc-list a:hover { background: var(--wash-1); }
.portal-doc-list a > span:first-child { display: flex; flex-direction: column; gap: 3px; }
.portal-doc-list strong { font-size: 13px; font-weight: 600; }
.portal-doc-list small { color: var(--muted); font-size: 10.5px; }
.portal-foot-note { font-size: 11.5px; margin: 14px 0 0; line-height: 1.55; }
.portal-pw { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }
.portal-pw label {
  display: flex; flex-direction: column; gap: 5px; font-size: 11.5px; color: var(--muted);
}
.portal-pw input {
  font: inherit; font-size: 15px; padding: 11px 12px; border-radius: 0;
  border: 1px solid var(--border); background: var(--plane); color: var(--ink);
  min-height: 46px;
}
.portal-pw input:focus { border-color: var(--accent-gold); outline: none; }
.portal-pw-actions { display: flex; align-items: center; gap: 10px; }
.ps-queried { border-color: var(--warning);
  background: color-mix(in srgb, var(--warning) 13%, transparent); }

/* ---- Granting that access, on the HR panel ---- */
.emp-portal {
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--grid);
}
.emp-portal-head {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  font-size: 12px; margin-bottom: 8px;
}
.emp-portal-head .dim { font-size: 11px; }
.emp-portal-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.emp-portal-row input {
  font: inherit; font-size: 12.5px; padding: 6px 8px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  width: 17ch; min-width: 0;
}
.emp-portal-row input:focus { border-color: var(--accent-gold); outline: none; }
.emp-portal-note { font-size: 10.5px; margin-top: 8px; line-height: 1.5; max-width: 74ch; }
.staff-document-upload {
  display: grid; grid-template-columns: minmax(180px, 1fr) minmax(210px, 1fr)
    minmax(200px, 1fr) auto; align-items: end; gap: 10px; margin: 10px 0 14px;
}
.staff-document-upload label { display: flex; flex-direction: column; gap: 4px; }
.staff-document-upload input, .staff-document-upload select { min-height: 40px; }
.staff-document-upload .btn { min-height: 40px; }
.staff-document-groups { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.staff-document-list { list-style: none; padding: 0; margin: 5px 0 0; }
.staff-document-list li {
  min-height: 42px; border-top: 1px solid var(--grid); display: flex;
  align-items: center; gap: 8px; flex-wrap: wrap;
}
.staff-document-list li > a { flex: 1; min-width: 160px; }
.staff-document-list form { margin: 0; }

@media (max-width: 760px) {
  .emp-portal-row input { min-height: 38px; width: 100%; }
  .staff-document-upload { grid-template-columns: 1fr; }
  .staff-document-groups { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------------
   Phones & tablets. The rule that matters: a screenful of navigation must
   never sit between the user and the numbers, and nothing may scroll the page
   sideways — wide tables scroll inside their own box instead.
   --------------------------------------------------------------------------- */
@media (max-width: 820px) {
  /* The panel becomes an off-canvas drawer: it sits over the page rather than
     pushing it, so the content always starts at the top of the screen. */
  .shell { flex-direction: column; }
  .sidebar {
    --rail: min(300px, 84vw);
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 60;
    height: 100dvh; margin: 0; border-radius: 0;
    /* the drawer floats over the page here, so unlike the desktop rail it
       needs the ground painted under it rather than inherited */
    background: var(--plane);
    border: 0; border-right: 1px solid var(--border);
    transform: translateX(-102%);
    transition: transform .3s var(--ease);
    overflow: hidden;
  }
  .sidebar.rail { --rail: min(300px, 84vw); }   /* no rail state on a phone */
  .sidebar.rail .brand > div:not(.brand-mark),
  .sidebar.rail .nav-label, .sidebar.rail .who-foot,
  .sidebar.rail .build-foot { display: revert; }
  .sidebar.rail .profile-tab-copy { display: flex; }
  .sidebar.rail .brand { padding: 16px 14px 12px; justify-content: flex-start; }
  .sidebar.rail .brand-spacer { display: block; }
  .sidebar.rail .nav-toggle { position: static; transform: none; opacity: 1; pointer-events: auto; }
  .sidebar.rail nav a { justify-content: flex-start; padding: 12px 12px; }
  .sidebar.rail nav a .badge { position: static; transform: none; }
  .sidebar.rail .ghost-btn { text-align: center; display: flex; font-size: inherit; }
  .sidebar.rail .btn-label { display: revert; }
  .sidebar.rail .ghost-btn::after { content: none; }
  .sidebar.open { transform: translateX(0); }
  .sidebar .brand { padding-top: max(16px, env(safe-area-inset-top)); }
  .sidebar nav { flex: 1; padding: 4px 10px 10px; }
  .sidebar nav a { padding: 12px; }
  .sidebar-foot { flex-direction: row; flex-wrap: wrap; padding: 10px;
                  padding-bottom: max(10px, env(safe-area-inset-bottom)); }
  .sidebar-foot .ghost-btn { flex: 1; min-width: 96px; text-align: center; min-height: 40px; }
  .sidebar-foot .profile-tab {
    flex-basis: 100%; min-width: 100%; justify-content: flex-start; text-align: left;
  }
  .who-foot, .build-foot { flex-basis: 100%; }
  /* the burger becomes a close X inside the open drawer */
  .sidebar.open .nav-burger { background: transparent; }
  .sidebar.open .nav-burger::before { top: 0; width: 16px; transform: rotate(45deg); }
  .sidebar.open .nav-burger::after { top: 0; width: 16px; transform: rotate(-45deg); }

  .nav-scrim {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(10, 16, 30, .46); backdrop-filter: blur(2px);
    opacity: 0; pointer-events: none; transition: opacity .3s var(--ease);
  }
  /* setting display in CSS overrides the `hidden` attribute, so an invisible
     scrim would otherwise sit over the page and swallow every tap */
  .nav-scrim[hidden] { display: none; }
  .nav-scrim.on { opacity: 1; pointer-events: auto; }

  /* A floating summon button — the menu is a widget you call up, not a bar
     that permanently occupies the top of every page. */
  .nav-fab {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    position: fixed; z-index: 50;
    left: 14px; bottom: max(16px, env(safe-area-inset-bottom));
    min-height: 48px; padding: 0 15px; border-radius: 999px;
    background: var(--btn-bg); color: var(--btn-fg); border: 0; cursor: pointer;
    transition: transform .22s var(--ease), opacity .22s var(--ease);
  }
  .nav-fab:active { transform: scale(.94); }
  .nav-fab .badge { background: var(--accent-coral); color: #fff; }
  body.nav-open .nav-fab { opacity: 0; transform: scale(.8); pointer-events: none; }
  body.nav-open { overflow: hidden; }

  .main { padding: 14px 12px 84px; }   /* clear of the floating button */

  /* headings and their controls stack instead of fighting for one line */
  .page-head { gap: 8px 12px; margin-bottom: 12px; }
  .page-head h1 { font-size: 18px; }
  .page-head .spacer { display: none; }
  .page-head .sub { flex-basis: 100%; font-size: 12px; }

  /* two tiles across stays scannable; the notes must not balloon a card */
  .grid.tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .tile .label { font-size: 10.5px; letter-spacing: -0.02em; }
  .tile .value { font-size: 22px; }
  .tile .note, .tile .delta { font-size: 11px; }
  .card { padding: 12px 13px; }

  /* filter/segment rows: one swipeable line, not five wrapped ones */
  .filters, .seg-row {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch; padding-bottom: 2px;
  }
  .filters::-webkit-scrollbar, .seg-row::-webkit-scrollbar { display: none; }
  .filters > * { flex: 0 0 auto; }

  /* Everything you tap gets a real target. 38px is the floor; the filter
     chips and segmented controls were 24-26px, which is a miss on a phone. */
  .filters a, .filters .chip {
    padding: 9px 14px; font-size: 13px;
    display: inline-flex; align-items: center; min-height: 38px;
  }
  .seg a { padding: 9px 13px; display: inline-flex; align-items: center; min-height: 38px; }
  .card h2 .as-of[href], .card h2 a { padding: 6px 2px; display: inline-block; }
  .card h2 { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
  .card .as-of { float: none; }
  input[type=checkbox], input[type=radio] { width: 18px; height: 18px; }

  /* Every data table becomes its own scroll box. The table element itself is
     the scroller and its tbody is the wide content — setting a min-width on
     the scroller instead would just push the page open again. */
  .card > table.data {
    display: block; overflow-x: auto; max-width: 100%;
    -webkit-overflow-scrolling: touch;
  }
  .card > table.data > tbody { display: table; min-width: 100%; }
  .scroll-x { -webkit-overflow-scrolling: touch; }

  /* Bank narratives carry unbreakable reference strings
     (",0000000020001,JUL,2026,260211366100,…"). Without this one line they
     hold every container open and the whole page scrolls sideways. */
  .issue .title, .issue .detail, .issue .meta, .issue .stamp,
  .card .note, .feed-card, .add-person, .who-foot { overflow-wrap: anywhere; }
  /* NOT table cells: a table would then squeeze "Revenue" to "Revenu/e"
     instead of scrolling. Wide tables scroll; only prose breaks. */

  /* a flag row: content, then its actions underneath */
  .issue { grid-template-columns: auto 1fr; gap: 8px; }
  .issue .actions { grid-column: 2 / -1; flex-wrap: wrap; }
  .issue .actions .btn { flex: 1; min-width: 110px; min-height: 38px; }
  .issue-note { grid-column: 1 / -1; flex-wrap: wrap; }
  .issue-note input { flex: 1 1 100%; min-height: 38px; }

  .daterange { flex: 1 1 100%; }
  .daterange input[type=date] { flex: 1; min-width: 0; }

  .menu-grid { column-count: 1; column-width: auto; }
  .metric-row { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
}

@media (max-width: 420px) {
  /* Deliberately still two across. One-up reads beautifully but pushes the
     decisions a full screen further down, and the pulse is a glance while the
     decisions are the job. */
  .tile .value { font-size: 20px; }
  .tile .note { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
                overflow: hidden; }
  .page-head h1 { font-size: 17px; }
}

/* iOS zooms the whole page when a focused field is under 16px. Any control the
   user types into has to meet that, everywhere. */
@media (max-width: 820px) {
  input, select, textarea, .price-edit input, .login-form input { font-size: 16px; }
  .btn, .btn-link, .ghost-btn, button { min-height: 40px; }
  /* every form control the user actually operates, whatever page it lives on */
  input[type=text], input[type=email], input[type=password], input[type=search],
  input[type=number], input[type=date], input[type=file], select, textarea,
  .add-person input, .add-person select, .range-form select, .range-form input,
  #people select, .filters input[type=search] {
    min-height: 40px; padding-top: 8px; padding-bottom: 8px;
  }
  input[type=file] { padding-top: 0; padding-bottom: 0; line-height: 44px; }
  input[type=file]::file-selector-button { min-height: 44px; }
  .add-person label.inline input[type=checkbox], .switch input { width: 20px; height: 20px; }
  /* links inside prose stay inline but get something to aim at */
  .note a, .explain a, .card h2 a, .card h2 .as-of[href] {
    padding: 5px 1px; text-underline-offset: 3px;
  }
}

/* ---------------------------------------------------------------------------
   Elevation layer — an *elevated* take on the brand palette (not a new one):
   soft depth, glass surfaces, restrained motion. Navy/cream stay the anchors;
   gold is the single accent that catches the light.
   --------------------------------------------------------------------------- */
:root {
  --shadow-sm: none; --shadow-md: none;   /* ch.5: no shadows */
  --radius: 0;
  --ease: cubic-bezier(.4,.14,.3,1);
}
:root[data-theme="dark"], :root:where(:not([data-theme="light"])) {
  --shadow-sm: none; --shadow-md: none;
}

/* ---------------------------------------------------------------------------
   One page becoming the next.

   Every navigation here is a full document load and stays one: there is no
   router, no client-side navigation and not a line of JavaScript in this. The
   browser holds a picture of the page you are leaving, loads the next one
   exactly as it does today, and cross-fades between them. A browser that does
   not know the at-rule throws it away along with the rules under it, and gets
   precisely what it gets now.

   The menu is furniture. It is the same panel on both pages, so it must not
   fade out and back in — naming it lifts it out of the picture and holds it
   still while the page changes behind it. The main column is deliberately NOT
   named: a named part animates its own width and height between the two
   pages, and the control tower and a one-line error page are nothing like the
   same height, so it would stretch rather than fade.
   --------------------------------------------------------------------------- */
@view-transition { navigation: auto; }

/* A tap should answer before the network does. This one-pixel line appears
   immediately while the next section is requested; it never guesses that a
   protected page is safe to preload or cache. */
html.app-navigating body::before {
  content: ""; position: fixed; z-index: 200; top: 0; left: 0;
  width: 72%; height: 2px; background: var(--accent-gold-line);
  transform-origin: left; animation: imv-navigation-progress .75s var(--ease) both;
  pointer-events: none;
}
@keyframes imv-navigation-progress {
  from { transform: scaleX(.08); opacity: .65; }
  to { transform: scaleX(1); opacity: 1; }
}

.sidebar    { view-transition-name: imv-sidebar; }
.public-bar { view-transition-name: imv-topbar; }

/* Never name anything a page can render twice — the flags count is in the
   sidebar and again on the phone button — because two of one name in one
   document abandons the whole transition, silently and with nothing logged.

   Holding the panel still is only right when it is on BOTH pages. Signing in,
   signing out, opening an invoice and coming back all cross the edge of the
   building, and there the pair has a single side: told not to animate, the
   column blanked in one frame and left a 220px hole down the page you were
   leaving, or painted itself finished on top of a sign-in page still fading
   out — and the panel draws no ground of its own, so the form showed through
   it. `:only-child` is what tells the two cases apart. On that side alone the
   panel goes on the page's own clock and simply fades with it. */
::view-transition-old(imv-sidebar):not(:only-child),
::view-transition-new(imv-sidebar):not(:only-child) { animation: none; }
::view-transition-old(imv-sidebar):not(:only-child) { opacity: 0; }
::view-transition-old(imv-sidebar):only-child,
::view-transition-new(imv-sidebar):only-child {
  animation-duration: .18s; animation-timing-function: var(--ease);
}

::view-transition-old(imv-topbar):not(:only-child),
::view-transition-new(imv-topbar):not(:only-child) { animation: none; }
::view-transition-old(imv-topbar):not(:only-child) { opacity: 0; }
::view-transition-old(imv-topbar):only-child,
::view-transition-new(imv-topbar):only-child {
  animation-duration: .18s; animation-timing-function: var(--ease);
}

/* 180ms on the house curve: about the shortest cross-fade that still reads as
   one movement, and short enough that it is never the thing you are waiting
   for on a slow link. */
::view-transition-group(root),
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .18s;
  animation-timing-function: var(--ease);
}

/* ch.3: "choose a ground and commit to it." No texture, no vignette, no
   gradient — the paper is one flat value in both colourways. */

.card { transition: border-color .28s var(--ease); }
.tile .value { letter-spacing: -0.02em; }

/* The numbers are what the page is for, so their rule is the gold one — the
   accent is the hairline itself rather than a second mark above it. */
.grid.tiles .card.tile { border-top-color: var(--accent-gold-line); }


.sidebar nav a { transition: background .2s var(--ease), color .2s var(--ease); }
.sidebar nav a.active { position: relative; }
.sidebar nav a.active::before {
  content: ""; position: absolute; left: -8px; top: 6px; bottom: 6px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--accent-gold);
}
.btn, .ghost-btn,
input[type=file]::file-selector-button {
  transition: background .2s var(--ease), transform .12s var(--ease),
              filter .2s var(--ease);
}
.btn:active, .ghost-btn:active,
input[type=file]:active::file-selector-button { transform: translateY(1px); }
.seg a { transition: background .2s var(--ease), color .2s var(--ease); }
table.data tbody tr, table.data tr { transition: background .16s var(--ease); }
table.data tr:hover td { background: var(--wash-1); }

/* ---- Data feeds: grouped by document type, with owner ---- */
.feed-group { margin-bottom: 22px; }
.group-head {
  display: flex; align-items: baseline; gap: 12px; margin: 0 2px 10px;
  padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.group-head .spacer { flex: 1; }
.group-title {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif;
  font-size: 17px; font-weight: 400; letter-spacing: -0.01em; margin: 0;
  color: var(--brand); font-weight: 500;
}
.group-owner {
  font-family: "Space Mono", monospace; font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--border); border-radius: 0; padding: 3px 9px;
}
.feed-card { transition: border-color .28s var(--ease); }
.feed-card.uploading { border-color: var(--accent-gold); }
.feed-card.uploading::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-gold), transparent);
  background-size: 40% 100%; background-repeat: no-repeat;
  animation: feed-scan 1.1s var(--ease) infinite;
}
.feed-card { position: relative; }
@keyframes feed-scan { 0% { background-position: -40% 0; } 100% { background-position: 140% 0; } }
.feed-card.just-updated { border-color: var(--good); }
.feed-card.just-updated .name { animation: pop .5s var(--ease); }
@keyframes pop { 0% { transform: none; } 35% { transform: translateY(-2px); } 100% { transform: none; } }
/* Space Mono is for figures and short states. When the answer is three
   sentences explaining which file this is and where the right one comes from,
   it is prose and is set as prose — the mono face at this width broke it into
   a ragged column nobody reads. */
.up-status { flex: 1 0 100%; font-size: 12px; line-height: 1.5;
  margin: 8px 0 0; padding-top: 8px; border-top: 1px solid var(--border-soft);
  overflow-wrap: anywhere; }
.up-status strong { font-weight: 500; }
/* What to do about it, on its own line, in the reading face. */
.up-status .up-do { display: block; margin-top: 4px; color: var(--ink-2); }
.up-status .up-saw { display: block; margin-top: 4px; font-size: 11px;
  color: var(--muted); font-family: "Space Mono", monospace; }
.up-status.working { color: var(--accent-gold); font-family: "Space Mono", monospace; }
.up-status.good { color: var(--good-text); }
.up-status.bad { color: var(--critical); }

@media (max-width: 820px) {
  .feed-card .upload-form { align-items: stretch; }
  .feed-card .upload-form input[type=file] { flex: 1 1 100%; max-width: none; }
  .feed-card .feed-action, .feed-card .feed-followup { min-height: 46px; }
  .feed-card .feed-followup { width: 100%; }
  .invoice-source-choice { align-items:stretch; flex-direction:column; }
  .invoice-file-source, .invoice-link-source { flex:1 1 auto; }
  .invoice-source-or { align-self:flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
/* `*` matches elements, and the parts of a view transition are not elements —
   nothing above reaches them, so asking an operating system for less motion
   would have bought a cross-fade on every navigation instead of none. Kept in
   its own two blocks: one unsupported selector invalidates the rule it is in,
   and these must not be able to take each other down. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
@media (prefers-reduced-motion: reduce) {
  /* Belt and braces for a browser that honours the pseudo-elements but not
     `navigation: none`. The opacities are not decoration: the two pictures are
     composited additively, so stopping the animation while both sit at full
     opacity sums them into a white flash — brighter than the cross-fade it
     was meant to remove. */
  ::view-transition-group(root),
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none !important; }
  ::view-transition-old(root) { opacity: 0 !important; }
  ::view-transition-new(root) { opacity: 1 !important; }
}

/* account tiers */
.tier {
  font-family: "Space Mono", monospace; font-size: 10px; text-transform: uppercase;
  letter-spacing: .1em; padding: 2px 7px; border-radius: 0;
  border: 1px solid var(--border); color: var(--ink-2);
}
.tier-key { background: var(--accent-gold); color: #141f38; border-color: transparent; font-weight: 500; }
.tier-growth { background: var(--wash-1); color: var(--ink); }
.tier-small { color: var(--muted); }
.tier-dormant { color: var(--muted); opacity: .7; }

/* public price-list layout (no sidebar) */
.public-shell { flex-direction: column; }
.public-bar {
  display: flex; align-items: center; gap: 12px; padding: 12px 26px;
  border-bottom: 1px solid var(--border); background: var(--surface); position: sticky; top: 0; z-index: 5;
}
.public-bar .brand { padding: 0; }
.public-shell .main { max-width: 1180px; margin: 0 auto; width: 100%; }

/* menu columns */
.menu-grid { columns: 3 300px; column-gap: 26px; }

/* ---- Price list: VAT switch + inline editing ---- */
/* the whole page re-prices at once, so a brief settle makes it legible */
.vat-flip .px, .vat-flip .vat-tag, .vat-flip .vat-note { animation: vatsettle .34s var(--ease); }
@keyframes vatsettle {
  from { opacity: .25; filter: blur(1.5px); }
  to { opacity: 1; filter: none; }
}
.px { font-variant-numeric: tabular-nums; }
.price-edit {
  display: inline-flex; align-items: center; gap: 2px;
  border: 1px solid var(--border); border-radius: 0; padding: 1px 5px;
  background: var(--surface); transition: border-color .18s var(--ease);
}
.price-edit:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus) 26%, transparent); }
.price-edit .pfx { font-size: 11px; color: var(--muted); }
.price-edit input {
  width: 74px; border: 0; background: none; color: var(--ink);
  font: inherit; font-size: 12.5px; text-align: right;
  font-variant-numeric: tabular-nums; padding: 2px 0;
}
.price-edit input:focus { outline: none; }
.price-edit input::-webkit-outer-spin-button,
.price-edit input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.price-edit input { -moz-appearance: textfield; }
/* Narrow while it is holding a tick, so the button beside it does not jump
   when one appears. But it is not always a tick: "Troy Mwelwa Chipampe added —
   tell them the starting password in person" in a 1em column comes out one
   word per line. Reserve the tick's width, then let a sentence have the rest
   of the row. */
.save-state {
  font-size: 11px; min-width: 1em; flex: 1 1 auto; text-align: left;
  line-height: 1.45;
}
.save-state.ok { color: var(--good); }
.save-state.bad { color: var(--critical); }
.save-state.saving { color: var(--muted); }

/* ---- My profile ----------------------------------------------------------
   One self-service account surface for every dashboard role. It follows the
   same hairlines-and-type language as the rest of the control tower, while
   keeping identity and security as two distinct jobs. */
.account-grid { align-items: start; }
.account-note {
  margin: 0 0 14px; max-width: 58ch; color: var(--muted); font-size: 12.5px;
  line-height: 1.5;
}
.account-form { display: flex; flex-direction: column; gap: 13px; max-width: 520px; }
.account-form label {
  display: flex; flex-direction: column; gap: 5px; color: var(--muted); font-size: 11px;
}
.account-form input {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 13px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 0;
  background: var(--surface); color: var(--ink);
}
.account-form input:focus { outline: none; border-color: var(--brand); }
.field-note { color: var(--muted); font-size: 10.5px; line-height: 1.4; }
.account-actions { display: flex; align-items: center; gap: 12px; min-height: 32px; }
.account-logout {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-top: 22px; padding-top: 12px; border-top: 1px solid var(--grid);
  color: var(--ink-2); font-size: 12px;
}
.account-logout div { display: flex; flex-direction: column; gap: 2px; }
.account-logout strong { color: var(--ink); font-weight: 550; }
.profile-payroll-card { margin-top: 14px; }
.profile-pay-tabs {
  display: flex; gap: 22px; margin: -2px 0 20px; border-bottom: 1px solid var(--grid);
}
.profile-pay-tabs button {
  appearance: none; border: 0; border-bottom: 2px solid transparent;
  margin: 0 0 -1px; padding: 8px 1px; background: none; color: var(--muted);
  font: inherit; font-size: 12px; font-weight: 550; cursor: pointer;
}
.profile-pay-tabs button[aria-selected="true"] {
  color: var(--ink); border-bottom-color: var(--accent-gold);
}
.profile-pay-tabs button:focus-visible { outline: 1px solid var(--focus); outline-offset: 3px; }
.profile-pay-head { display: flex; justify-content: space-between; gap: 16px; }
.profile-pay-head h2, [data-profile-pay-panel="overtime"] h2 { margin-bottom: 5px; }
.profile-payslip-list { list-style: none; margin: 0; padding: 0; }
.profile-payslip-list li {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 11px 0; border-top: 1px solid var(--grid);
}
.profile-payslip-list li > div:first-child { display: flex; flex-direction: column; gap: 2px; }
.profile-payslip-list li span { color: var(--muted); font-size: 11px; }
.profile-payslip-actions { display: flex; gap: 7px; flex-wrap: wrap; justify-content: flex-end; }
.profile-overtime-state { max-width: 66ch; margin: 0 0 14px; line-height: 1.55; }
.profile-overtime-state strong { display: block; margin-bottom: 3px; }
.profile-overtime-note { margin-top: 16px; }
.staff-self-card { margin-top: 14px; }
.staff-profile-disclosure > summary {
  cursor: pointer; display: flex; align-items: baseline; gap: 10px; list-style-position: outside;
}
.staff-profile-disclosure > summary .dim { font-size: 11px; }
.profile-disclosure-body { margin-top: 18px; }
.staff-self-form { max-width: none; }
.staff-self-fields, .staff-bank-fields {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 13px 16px;
}
.staff-bank-fields {
  margin-top: 2px; padding-top: 14px; border-top: 1px solid var(--grid);
}
@media (max-width: 1100px) {
  .staff-self-fields, .staff-bank-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .staff-self-fields, .staff-bank-fields { grid-template-columns: 1fr; }
  .profile-payslip-list li { align-items: flex-start; flex-direction: column; }
  .profile-payslip-actions { justify-content: flex-start; }
}
.menu-section { break-inside: avoid; margin-bottom: 18px; display: inline-block; width: 100%; }
.menu-section h3 {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; font-size: 15px; font-weight: 500;
  color: var(--brand); margin: 0 0 4px; padding-bottom: 4px;
  border-bottom: 1px solid var(--accent-gold);
}
.menu-section table.data td { padding: 4px 0; border-bottom: 0; font-size: 13px; }
.menu-section table.data td.num { font-variant-numeric: tabular-nums; white-space: nowrap; padding-left: 10px; }
@media print {
  .public-bar, .sidebar { display: none; }
  .card { box-shadow: none; border: 1px solid #ddd; }
}

/* ---- The manual ------------------------------------------------------------
   ch.4 in one place: Hanken Grotesk light carries the section headings, Space
   Mono the contents rail and the small labels, and Hanken everything you
   actually read. A measure of ~78ch, because this is prose, not a table. */
.manual-wrap { display: grid; grid-template-columns: 178px minmax(0, 1fr); gap: 34px; }

.manual-toc {
  position: sticky; top: 22px; align-self: start;
  max-height: calc(100vh - 44px); overflow-y: auto;
  border-left: 1px solid var(--baseline); padding-left: 15px;
}
.manual-toc .eyebrow { font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px; }
.manual-toc a {
  display: block; padding: 4px 0; font-size: 12.5px; color: var(--ink-2);
  text-decoration: none;
}
.manual-toc a:hover { color: var(--accent-gold); }
.manual-toc-group {
  font-family: "Space Mono", ui-monospace, monospace; font-size: 9px;
  letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
  margin: 14px 0 4px;
}

.manual-body { max-width: 78ch; }
.manual-sec {
  background: none; border: 0; border-top: 1px solid var(--border);
  border-radius: 0; padding: 22px 0 26px; margin-bottom: 10px;
  /* so a heading you jumped to is not sitting under the top of the window */
  scroll-margin-top: 20px;
}
.manual-sec h2 {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; font-weight: 500;
  font-size: 21px; margin: 0 0 12px; color: var(--brand);
  display: flex; align-items: center; gap: 10px;
}
.manual-sec h2 .nav-ico { width: 21px; height: 21px; flex: none; color: var(--accent-gold); }
.manual-sec h3 {
  font-family: "Space Mono", ui-monospace, monospace; font-size: 10px;
  letter-spacing: .15em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 7px; font-weight: 500;
}
.manual-sec p { font-size: 13.5px; line-height: 1.62; color: var(--ink-2); margin: 0 0 11px; }
.manual-sec ul, .manual-sec ol { font-size: 13.5px; line-height: 1.6; color: var(--ink-2);
  margin: 0 0 11px; padding-left: 19px; }
.manual-sec li { margin-bottom: 6px; }
.manual-sec strong { color: var(--ink); }
.manual-sec code {
  font-family: "Space Mono", ui-monospace, monospace; font-size: 12px;
  background: var(--wash-1); padding: 1px 5px; border-radius: 0;
}

.manual-lead { border-color: var(--baseline); }
.manual-lede {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif;
  font-size: 18px !important; line-height: 1.45 !important; font-style: italic;
  color: var(--ink) !important; margin-bottom: 14px !important;
}
.manual-what {
  font-size: 14px !important; color: var(--ink) !important;
  border-left: 1px solid var(--accent-gold-line); padding-left: 13px;
  margin-bottom: 16px !important;
}
.manual-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px 30px; margin-bottom: 4px; }
.manual-rule {
  border-top: 1px solid var(--grid); padding-top: 11px; margin-top: 14px !important;
  font-size: 12.5px !important;
}
.manual-table td { vertical-align: top; font-size: 12.5px; line-height: 1.55; }
.manual-table td:first-child { min-width: 12ch; }

@media (max-width: 1000px) {
  .manual-wrap { grid-template-columns: 1fr; gap: 16px; }
  /* On a phone the rail becomes a scrolling strip of chips. A sticky column of
     24 links would be a screenful of navigation before any content. */
  .manual-toc {
    position: static; max-height: none; border-left: 0;
    border-bottom: 1px solid var(--grid); padding: 0 0 12px;
    display: flex; gap: 8px; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; align-items: center;
  }
  .manual-toc::-webkit-scrollbar { display: none; }
  .manual-toc .eyebrow, .manual-toc-group { display: none; }
  .manual-toc a {
    padding: 7px 12px; white-space: nowrap; border: 1px solid var(--border);
    border-radius: 0; font-size: 12px; min-height: 34px; display: flex;
    align-items: center;
  }
  .manual-sec { padding: 16px 0 20px; scroll-margin-top: 12px; }
  .manual-grid { grid-template-columns: 1fr; gap: 16px; }
  .manual-lede { font-size: 16px !important; }
}

/* The six answers: the manual's opening claim, set as a numbered list because
   they really are a set of six, not a sequence of steps. */
.manual-answers { list-style: none; counter-reset: ans; padding: 0; margin: 18px 0 6px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(268px, 1fr)); gap: 16px 26px; }
.manual-answers li { counter-increment: ans; position: relative; padding-left: 30px; }
.manual-answers li::before {
  content: counter(ans); position: absolute; left: 0; top: 1px;
  font-family: "Space Mono", ui-monospace, monospace; font-size: 11px;
  color: var(--muted); border: 1px solid var(--border); border-radius: 50%;
  width: 21px; height: 21px; display: grid; place-items: center;
}
.manual-answers h3 { margin: 0 0 3px; font-size: 15.5px; }
.manual-answers h3 a { color: inherit; text-decoration: none; }
.manual-answers h3 a:hover { text-decoration: underline; }
.manual-answers p { margin: 0; }

@media print {
  .manual-toc, .page-head .btn { display: none; }
  .manual-wrap { grid-template-columns: 1fr; }
  .manual-sec { break-inside: avoid; box-shadow: none; border: 0;
                border-bottom: 1px solid #ccc; border-radius: 0; padding: 0 0 14px;
                margin-bottom: 14px; }
  .manual-body { max-width: none; }
}

/* ---- Staff register (HR) ---- */
.hr-gaps { margin: 6px 0 0; padding-left: 18px; font-size: 12.5px; color: var(--ink-2); }
.hr-gaps li { margin-bottom: 5px; }
.hr-gaps strong { color: var(--serious); }

.hr-table tr.off { opacity: .55; }
.hr-table td { vertical-align: top; }
/* The name is a control, not a link — clicking it opens the person's details
   in place, so it must not look like navigation away from the page. */
.linklike {
  font: inherit; font-weight: 500; color: var(--ink); background: none;
  border: 0; padding: 0; cursor: pointer; text-align: left;
  border-bottom: 1px dashed var(--border);
}
.linklike:hover { color: var(--accent-gold); border-bottom-color: var(--accent-gold); }
.hr-table input.pay-in {
  font: inherit; font-size: 12px; font-variant-numeric: tabular-nums;
  /* wide enough for the highest salary on the register — 27,213.00 — so no
     figure is ever silently clipped in the box it lives in */
  width: 10.5ch; text-align: right; padding: 4px 6px; border-radius: 0;
  border: 1px solid transparent; background: transparent; color: var(--ink);
}
.hr-table .casual-cell { font-size: 11.5px; padding-top: 10px; }
.hr-table input.pay-in:hover { border-color: var(--border); }
.hr-table input.pay-in:focus {
  border-color: var(--accent-gold); background: var(--surface); outline: none;
}
.hr-table select[data-field="status"] {
  font: inherit; font-size: 11.5px; padding: 3px 6px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
}
.emp-detail > td { background: var(--wash-1); padding: 12px 14px; }
.emp-grid {
  display: grid; gap: 10px 14px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.emp-grid label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; color: var(--muted);
}
.emp-grid input, .emp-grid select {
  font: inherit; font-size: 12.5px; padding: 6px 8px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  min-width: 0;
}
.emp-grid input:focus, .emp-grid select:focus { border-color: var(--accent-gold); outline: none; }
/* A statutory number that isn't on file is the thing to fix on this page, so
   the empty box says so rather than looking like every other empty box. */
.emp-grid input.missing { border-color: var(--warning); }
.emp-grid input:disabled, .emp-grid select:disabled { opacity: .6; cursor: default; }
.emp-foot { margin-top: 10px; font-size: 11px; display: flex; flex-wrap: wrap;
             gap: 6px; align-items: center; }
.emp-remove { color: var(--critical); border-color: var(--critical); }

@media (max-width: 760px) {
  .emp-grid { grid-template-columns: 1fr 1fr; }
  .emp-grid input, .emp-grid select { min-height: 38px; }
  /* Nine columns of pay will not fit a phone, and squeezing them turns every
     name into a five-line stack and clips every figure. Hold the natural width
     and let the table scroll inside its own box — the page still does not. */
  .hr-table { min-width: 820px; }
  .hr-table td:first-child, .hr-table th:first-child { min-width: 150px; }
  .hr-table input.pay-in { min-height: 34px; }
}

/* ---- People & access (owner only) ---- */
#people select {
  font: inherit; font-size: 12px; padding: 3px 6px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
}
#people tr.off { opacity: .5; }
.switch { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; font-size: 12px; }
.switch input { accent-color: var(--accent-gold); width: 15px; height: 15px; }
.add-person { margin-top: 14px; border-top: 1px solid var(--grid); padding-top: 10px; }
.add-person summary {
  cursor: pointer; font-size: 12.5px; color: var(--ink-2); font-weight: 500;
  list-style: none; width: max-content;
}
.add-person summary:hover { color: var(--ink); }
.add-person form { margin-top: 12px; display: flex; flex-direction: column; gap: 10px; max-width: 640px; }
.add-person .row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.add-person label {
  display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 180px;
  font-size: 11px; color: var(--muted);
}
.add-person label.inline {
  flex-direction: row; align-items: center; gap: 7px; font-size: 12.5px; color: var(--ink-2);
}
.add-person label.inline input { width: 15px; height: 15px; accent-color: var(--accent-gold); }
.add-person input[type=email], .add-person input[type=text], .add-person select {
  font: inherit; font-size: 13px; padding: 6px 9px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
}
.add-person input:focus, .add-person select:focus {
  outline: none; border-color: var(--brand);
}
.user-sections details, .section-access { min-width: 220px; }
.user-sections summary, .section-access summary { cursor: pointer; font-weight: 600; }
.user-sections fieldset, .section-access fieldset {
  border: 0; border-top: 1px solid var(--border-soft); margin: 8px 0 0;
  padding: 7px 0 0; display: grid; gap: 6px;
}
.user-sections legend, .section-access legend {
  color: var(--muted); font-family: "Space Mono", monospace; font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase; padding: 0 6px 0 0;
}
.user-sections fieldset label { display: flex; gap: 7px; align-items: center; white-space: nowrap; }
.user-sections input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--accent-gold); }

/* ---- Dashboard date-range picker ---- */
.daterange { display: inline-flex; align-items: center; gap: 6px; }
.daterange input[type=date] {
  font: inherit; font-size: 12.5px; padding: 5px 9px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
}
.daterange input[type=date]:focus {
  outline: none; border-color: var(--brand);
}

/* ---- The hot seat ---------------------------------------------------------
   Three answers on opening the app: what needs me, who owes me a document,
   what is bleeding. Everything here is a link to the evidence behind it. */
.grid.two.hot { align-items: start; }
.card.seat .issue { padding: 9px 2px; }
.card.seat .issue:first-of-type { padding-top: 2px; }
.card.seat .issue .detail { font-size: 12px; }
.seat-tail { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px;
             padding-top: 10px; border-top: 1px solid var(--grid); }
.seat-tail .chip {
  font-size: 11.5px; padding: 4px 10px; border-radius: 0;
  border: 1px solid var(--border); color: var(--ink-2); text-decoration: none;
  transition: background .16s var(--ease), color .16s var(--ease);
}
.seat-tail .chip:hover { background: var(--wash-1); color: var(--ink); }
.seat-owned { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--grid); }
.owned-row { font-size: 11.5px; color: var(--ink-2); padding: 2px 0; }
.owned-row .who { font-weight: 650; color: var(--ink); }

/* who owes what — the feed owner is the point, not the feed */
.owes { padding: 9px 0; border-bottom: 1px solid var(--grid); }
.owes:last-child { border-bottom: 0; padding-bottom: 0; }
.owes-who { display: flex; align-items: baseline; gap: 8px; }
.owes-who .who { font-weight: 650; font-size: 13px; }
.owes-who .dim { font-size: 11.5px; }
.owes-list { list-style: none; margin: 5px 0 0; padding: 0;
             display: flex; flex-direction: column; gap: 3px; }
.owes-list li { font-size: 12px; color: var(--ink-2); display: flex; gap: 8px; align-items: baseline; }
.owes-list .status { flex: none; min-width: 62px; font-size: 11px; }

table.data.risk td { padding: 7px 2px; }
table.data.risk a { text-decoration: none; color: var(--ink-2); }
table.data.risk a:hover { color: var(--ink); text-decoration: underline; }
table.data.risk td.num { font-weight: 650; font-variant-numeric: tabular-nums; }

/* ---- Go-live: the line in the sand -----------------------------------------
   History is cleaned as far as it reasonably can be; from an agreed date the
   protocol has to run properly. The card says how far off that is and what is
   short, and nothing else on the page competes with it. */
.card.golive { border-color: var(--accent-gold); }
.card.golive.ready { border-color: var(--good); }
.golive-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
.golive-date {
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif;
  font-size: 26px; font-weight: 200; letter-spacing: -0.02em;
  color: var(--brand); display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.golive-days {
  font-family: "Space Mono", monospace; font-size: 11px; letter-spacing: .04em;
  color: #141f38; background: var(--accent-gold); padding: 3px 9px; border-radius: 0;
}
.card.golive.ready .golive-days { background: none; color: var(--ink-2);
  border: 1px solid var(--border); }
.golive-score { margin-left: auto; text-align: right; font-size: 12px; color: var(--ink-2); }
.golive-score strong { font-size: 16px; color: var(--ink); }
.golive-bar {
  width: 132px; height: 5px; border-radius: 0; background: var(--wash-1);
  margin-top: 5px; overflow: hidden;
}
.golive-bar span { display: block; height: 100%; background: var(--accent-gold);
                   transition: width .5s var(--ease); }
.card.golive.ready .golive-bar span { background: var(--good); }

.gates { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 8px; }
/* A gate is a line of type under a hairline, not a box. What is short of
   ready says so with one coral mark — the book's single dot. */
.gate {
  display: flex; gap: 10px; align-items: flex-start; text-decoration: none;
  padding: 9px 2px 9px 0; border: 0; border-top: 1px solid var(--border-soft);
  border-radius: 0;
  transition: border-color .16s var(--ease);
}
.gate:hover { border-top-color: var(--ink-2); }
.gate-mark {
  flex: none; width: 20px; height: 20px; border-radius: 999px;
  display: grid; place-items: center; font-size: 10.5px; font-weight: 400;
  font-family: "Space Mono", monospace;
  border: 1px solid var(--border); color: var(--muted);
}
.gate.ok .gate-mark { border-color: var(--border); color: var(--muted); }
.gate.short .gate-mark {
  background: var(--accent-coral-line); border-color: var(--accent-coral-line);
  color: #EFE8D6;
}
.gate.short { border-top-color: var(--accent-coral-line); }
.gate-label { display: block; font-size: 12.5px; font-weight: 400; color: var(--ink); }
.gate-detail { display: block; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.golive-note {
  font-size: 11.5px; color: var(--muted); margin: 11px 0 0;
  padding-top: 10px; border-top: 1px solid var(--grid); max-width: 92ch;
}
.golive-note strong { color: var(--ink-2); }

/* era marker on a flag */
.era {
  font-family: "Space Mono", monospace; font-size: 9.5px; text-transform: uppercase;
  letter-spacing: .08em; padding: 1px 6px; border-radius: 0; border: 1px solid var(--border);
}
.era.live { background: var(--accent-gold); color: #141f38; border-color: transparent; font-weight: 500; }
.era.historic { color: var(--muted); }

/* ---- Opening balances: proposal on the left, signature on the right ---- */
table.data.ob td { vertical-align: top; padding: 10px 8px; }
table.data.ob tr.agreed { background: color-mix(in srgb, var(--good) 6%, transparent); }
.ob-basis { font-size: 11px; color: var(--muted); margin-top: 3px; max-width: 46ch; line-height: 1.45; }
.ob-input input { width: 118px; text-align: right; }
.ob-drift { font-size: 11px; color: var(--warning); margin-top: 3px; font-weight: 500; }
.ob-sign { font-size: 11.5px; color: var(--ink-2); }
.ob-sign .who { font-weight: 650; color: var(--ink); }
.ob-detail-link { display: inline-block; margin-top: 5px; font-size: 11px; font-weight: 650; }
.ob-support { margin-top: 14px; scroll-margin-top: 18px; }
.ob-support-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; }
.ob-support-head h2 { margin-bottom: 3px; }
.ob-support-head p { margin: 0; font-size: 11.5px; }
.ob-support-total { white-space: nowrap; font-size: 18px; }
.ob-detail { margin-top: 10px; }
.ob-detail tfoot th { border-top: 2px solid var(--line); }
.ob-detail-note td { padding-top: 0; color: var(--muted); font-size: 10.5px; }
.ob-detail-foot { margin: 9px 0 0; font-size: 11px; }
@media (max-width: 620px) {
  .ob-support-head { flex-direction: column; gap: 8px; }
  .ob-support-total { font-size: 16px; }
}

/* ---- Payroll ---- */
tr.row-bad td { background: color-mix(in srgb, var(--critical) 7%, transparent); }
.calc-diff { font-size: 10.5px; color: var(--critical); font-weight: 500; margin-top: 2px; }

/* ---- A payslip is a document, not a screen: it must print cleanly ---- */
/* A document is paper in both colourways. The app reverses to cream-on-navy at
   night; an invoice does not, because it is the thing the customer receives and
   the thing that goes in the printer. Pinning the light palette here also stops
   the screen-only ribbon — which is drawn on paper — from putting cream type on
   a cream ground in the dark theme. */
:root body.payslip-body,
:root[data-theme="dark"] body.payslip-body {
  color-scheme: light;
  --plane: #EFE8D6; --surface: #EFE8D6;
  --ink: #141F38; --ink-2: #3d485f; --muted: #6b7385;
  --grid: rgba(20, 31, 56, .10);
  --baseline: rgba(20, 31, 56, .22);
  --border: rgba(20, 31, 56, .18);
  --border-soft: rgba(20, 31, 56, .10);
  --brand: #141F38; --brand-tile: #141F38;
  --accent-gold: #B98214; --accent-gold-line: #E0A43B;
  --accent-coral: #C3341A; --accent-coral-line: #E24E33;
  --good: #6b7385; --good-text: #3d485f;
  --warning: #B98214; --serious: #C3341A; --critical: #C3341A;
  --wash-1: rgba(20, 31, 56, .045);
  --btn-bg: #141F38; --btn-fg: #EFE8D6;
  --focus: #B98214;
}
.payslip-body { background: var(--plane); padding: 24px 16px 60px; }
.payslip {
  /* a payslip is a sheet of paper: the one place a boundary is the subject
     rather than chrome, since the edge is where the document ends. */
  max-width: 760px; margin: 0 auto; background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 0; padding: 34px 38px;
}
.ps-head { display: flex; justify-content: space-between; align-items: flex-start;
           gap: 20px; padding-bottom: 16px; border-bottom: 1px solid var(--ink); }
.ps-brand { display: flex; gap: 12px; align-items: center; }
.ps-brand .brand-name { font-size: 15px; }
.ps-title { text-align: right; }
.ps-month { font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; font-size: 17px; font-weight: 500; }
.ps-who { display: flex; flex-wrap: wrap; gap: 10px 34px; padding: 14px 0;
          border-bottom: 1px solid var(--grid); font-size: 13px; }
.ps-lbl { display: block; font-family: "Space Mono", monospace; font-size: 9.5px;
          text-transform: uppercase; letter-spacing: .14em; color: var(--muted); }
.ps-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; padding: 16px 0; }
.ps-cols h3, .ps-employer h3 { font-size: 11px; text-transform: uppercase;
  letter-spacing: .14em; color: var(--muted); margin: 0 0 6px;
  font-family: "Space Mono", monospace; font-weight: 500; }
.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.ps-table td { padding: 5px 0; }
.ps-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.ps-table tr.ps-total td { border-top: 1px solid var(--baseline); font-weight: 650; padding-top: 7px; }
.ps-note { font-size: 10.5px; color: var(--muted); }
.ps-net { display: flex; justify-content: space-between; align-items: baseline;
  background: var(--ink); color: var(--surface); padding: 13px 18px;
  border-radius: 0; margin: 4px 0 18px; }
.ps-net strong { font-size: 22px; font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; }
.ps-employer { padding-top: 12px; border-top: 1px solid var(--grid); }
.ps-warn { margin-top: 16px; padding: 11px 14px; border-radius: 0; font-size: 12px;
  background: color-mix(in srgb, var(--critical) 12%, transparent);
  border: 1px solid var(--critical); }
.ps-foot { margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--grid);
  font-size: 11px; color: var(--muted); display: flex; justify-content: space-between;
  gap: 16px; align-items: flex-end; }
/* Save and print are the same act done two ways, so they are the same button
   twice — same height, same width of padding, side by side. The book allows
   one filled button per screen and this is the screen's one: the other keeps
   the geometry and takes a hairline instead of a fill. */
.ps-print { display: flex; gap: 10px; align-items: center; flex: 0 0 auto; }
.ps-print .btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 30px; padding: 0 15px; white-space: nowrap; font-size: 12.5px;
  border: 1px solid var(--btn-bg); color: var(--ink); background: none;
}
.ps-print .btn.primary { color: var(--btn-fg); background: var(--btn-bg); }
.ps-print .btn:hover { border-color: var(--accent-gold-line); }
.ps-print .btn.primary:hover { border-color: var(--btn-bg); }

/* ---- Invoice: the same document furniture as a payslip, plus lines ---- */
.inv-seller-ids { font-size: 10.5px; color: var(--muted); margin-top: 3px; }
.inv-who { gap: 10px 28px; }
.inv-lines { width: 100%; border-collapse: collapse; font-size: 13px; margin: 16px 0 6px; }
.inv-lines th {
  text-align: left; font-family: "Space Mono", monospace; font-size: 9.5px;
  text-transform: uppercase; letter-spacing: .14em; color: var(--muted);
  font-weight: 500; padding: 0 0 6px; border-bottom: 1px solid var(--baseline);
}
.inv-lines th.num, .inv-lines td.num { text-align: right; font-variant-numeric: tabular-nums; }
.inv-lines td { padding: 7px 0; border-bottom: 1px solid var(--grid); vertical-align: top; }
.inv-lines td:first-child {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-wrap: break-word;
}
.inv-lines td:not(:first-child), .inv-lines th:not(:first-child) { padding-left: 14px; }
.inv-totals { width: 100%; max-width: 300px; margin-left: auto; border-collapse: collapse;
  font-size: 13px; margin-bottom: 8px; }
.inv-totals td { padding: 4px 0; }
.inv-totals td.num { text-align: right; font-variant-numeric: tabular-nums; }
.inv-totals tr.inv-totals-sum td { border-top: 1px solid var(--baseline);
  padding-top: 7px; font-weight: 500; }
.inv-doc-date { font-size: 11px; color: var(--muted); margin-top: 3px; }
.inv-tpin-note { margin: 10px 0 0; }
.inv-pay { padding-top: 12px; border-top: 1px solid var(--grid); font-size: 12.5px; }
.inv-pay h3 { font-size: 11px; text-transform: uppercase; letter-spacing: .14em;
  color: var(--muted); margin: 0 0 5px; font-family: "Space Mono", monospace; font-weight: 500; }

/* Banking details print one block per account. A customer paying the wrong
   account is a fortnight of chasing, so each block names its own currency. */
.inv-banks { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px 26px; margin: 8px 0 10px; }
.inv-bank-name { font-size: 11.5px; font-weight: 500; letter-spacing: .02em;
  margin-bottom: 4px; }
.inv-bank { break-inside: avoid; }   /* never split an account across pages */
.inv-bank dl { margin: 0; font-size: 11.5px; }
.inv-bank dl > div { display: flex; gap: 8px; padding: 1px 0; }
.inv-bank dt { color: var(--muted); flex: 0 0 84px; }
.inv-bank dd { margin: 0; font-variant-numeric: tabular-nums; }
.inv-thanks { margin-top: 16px; font-size: 12.5px; }
.inv-thanks p { margin: 0 0 8px; }
.inv-sign { color: var(--muted); }
.inv-sign strong { color: var(--ink); }

/* ---- Buying & costs ---- */
.alias-chip {
  display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px;
  border: 1px solid var(--grid); border-radius: 0; padding: 1px 4px 1px 9px;
  margin: 0 5px 5px 0; white-space: nowrap;
}
.alias-forget {
  border: 0; background: none; color: var(--muted); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 2px 5px; border-radius: 50%;
}
.alias-forget:hover { color: var(--critical); background: var(--wash-1); }
.merge-box { margin-top: 12px; border-top: 1px solid var(--grid); padding-top: 10px; }
.merge-box > summary { cursor: pointer; font-size: 12.5px; color: var(--muted); }
.merge-box > summary:hover { color: var(--accent-gold); }
.sku-manager { display: grid; gap: 18px; }
.sku-manager > h2 { margin-bottom: -12px; }
.sku-create-form, .sku-merge-form { border-top: 1px solid var(--border-soft); padding-top: 14px; }
.sku-create-form h3, .sku-merge-form h3 { margin: 0 0 10px; font-size: 13px; }
.sku-manager select { min-height: 38px; }

/* What is on the shelf. */
.stock-figures { display: grid; gap: 10px 26px; margin: 6px 0 0;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
.stock-figures strong { display: block; font-size: 17px;
  font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; }
.stock-figures .neg, td.neg { color: var(--critical); }
.stock-actions { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--grid); }
.stock-actions form { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.stock-actions input[type="text"] { min-width: 130px; }

/* The stock board has seven columns. Without an explicit grid each category
   chose different widths from its longest ingredient or explanation, so the
   page looked like several unrelated tables and narrow rows crushed the
   sparklines into slivers. */
.stock-category-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 8px;
}
.stock-category-head h2, .stock-category-head p { margin: 0; }
.stock-category-head p { font-size: 12px; }
.stock-board { table-layout: fixed; min-width: 880px; }
.stock-board th:first-child, .stock-board td:first-child { width: 24%; }
.stock-board th:nth-child(2), .stock-board td:nth-child(2) { width: 11%; }
.stock-board th:nth-child(3), .stock-board td:nth-child(3) { width: 18%; }
.stock-board th:nth-child(4), .stock-board td:nth-child(4) { width: 11%; }
.stock-board th:nth-child(5), .stock-board td:nth-child(5) { width: 8%; }
.stock-board th:nth-child(6), .stock-board td:nth-child(6) { width: 18%; }
.stock-board th:nth-child(7), .stock-board td:nth-child(7) { width: 10%; }

/* Twice-daily stock count: made for a phone in one hand while the other moves
   bottles. The last count is context, the input is the job, and one explicit
   tap says the item was actually seen. */
.stock-count-head { justify-content: space-between; align-items: flex-end; flex-wrap:wrap; }
.stock-count-head > div { min-width:0; }
.stock-count-actions, .count-finish-actions {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
}
.stock-count-actions .btn, .count-finish-actions .btn { margin: 0; min-height: 44px; }
#stock-count-top, #stock-count-finish { scroll-margin-block: 20px; }
.stock-count-areas { display:flex; flex-wrap:wrap; gap:6px; }
.stock-count-toolbar {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 18px; flex-wrap: wrap; margin-bottom: 12px;
}
.stock-count-toolbar .subnav { margin: 0; min-width: 190px; }
.stock-count-date { display: flex; align-items: flex-end; gap: 8px; }
.stock-count-date label { font-size: 11.5px; color: var(--muted); }
.stock-count-date input { display: block; margin-top: 3px; }
.stock-count-search {
  display: grid; grid-template-columns: minmax(220px, 520px) auto;
  align-items: end; gap: 4px 14px; margin: 0 0 12px;
  padding: 10px 0; border-top: 1px solid var(--border-soft);
  border-bottom: 1px solid var(--border-soft);
}
.stock-count-search label { grid-column: 1; font-size: 11.5px; color: var(--muted); }
.stock-count-search input { grid-column: 1; width: 100%; min-height: 42px; font-size: 16px; }
.stock-count-search [data-count-search-status] { grid-column: 2; grid-row: 2; font-size: 11.5px; }
.stock-count-intro {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; margin-bottom: 8px;
}
.stock-count-intro p { margin: 0; max-width: 72ch; }
.stock-count-intro .count-evidence {
  margin-top: 9px; display: flex; gap: 5px; flex-wrap: wrap;
  color: var(--muted); font-size: 11.5px;
}
.stock-count-intro .count-evidence strong { color: var(--good-text); }
.recipe-shop-price { margin-top: 3px; font-size: 18px; color: var(--ink); }

/* Restricted account landing pages are task maps, not miniature control
   towers. A person should be able to choose the next job without first
   understanding the accounting model behind the rest of the application. */
.role-home-head { margin-bottom: 14px; }
.role-home-intro { display: block; max-width: 760px; margin-top: 6px; line-height: 1.5; }
.role-home-guide {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 12px 14px; margin-bottom: 18px;
  border-left: 3px solid var(--accent-gold-line); background: var(--wash-1);
}
.role-home-guide span { color: var(--muted); }
.role-home { display: grid; gap: 24px; max-width: 1120px; }
.role-home-group h2 { margin: 0 0 10px; font-size: 14px; }
.role-home-actions {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px;
}
.role-home-action {
  display: grid; grid-template-rows: auto 1fr auto; min-height: 118px;
  padding: 14px; border: 1px solid var(--border); color: inherit;
  text-decoration: none; background: var(--surface);
}
.role-home-action:hover, .role-home-action:focus-visible {
  border-color: var(--accent-gold-line); background: var(--wash-1);
}
.role-home-action strong { font-size: 15px; }
.role-home-action > span:not(.role-home-open) {
  margin-top: 6px; color: var(--muted); line-height: 1.4;
}
.role-home-open { margin-top: 12px; font-size: 12px; text-decoration: underline; }

@media (max-width: 900px) {
  .role-home-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .role-home-actions { grid-template-columns: 1fr; }
  .role-home-action { min-height: 0; }
  .role-home-guide { display: grid; gap: 4px; }
}
.count-progress { min-width: 150px; text-align: right; }
.count-progress strong { display: block; font-size: 20px; font-weight: 400; }
.count-progress > span { display: block; color: var(--muted); font-size: 11px; }
.count-progress progress { width: 150px; height: 5px; accent-color: var(--good); }
.count-category { padding-bottom: 8px; }
.count-board { table-layout: fixed; min-width: 840px; }
.count-board th:first-child, .count-board td:first-child { width: 27%; }
.count-board th:nth-child(2), .count-board td:nth-child(2) { width: 17%; }
.count-board th:nth-child(3), .count-board td:nth-child(3) { width: 32%; }
.count-board th:nth-child(4), .count-board td:nth-child(4) { width: 24%; }
.count-board tr.counted td { background: color-mix(in srgb, var(--good) 5%, transparent); }
.count-board tr[data-count-row] { cursor: pointer; }
.count-board tr[data-count-row]:hover td,
.count-board tr[data-count-row]:focus-visible td {
  background: color-mix(in srgb, var(--accent-gold) 7%, transparent);
}
.count-board tr[data-count-row]:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.count-item-open {
  appearance: none; background: none; border: 0; color: inherit; cursor: pointer;
  font: inherit; margin: 0; padding: 0; text-align: left;
}
.count-item-open:hover, .count-item-open:focus-visible {
  color: var(--link); text-decoration: underline; text-underline-offset: 3px;
}
.count-item-category { display: block; margin-top: 3px; font-size: 11px; }
.count-input { width: 11ch; text-align: right; font-variant-numeric: tabular-nums; }
.bottle-count-input {
  display: flex; justify-content: flex-end; align-items: flex-end; gap: 8px;
}
.bottle-count-input label { display: grid; gap: 3px; text-align: left; }
.bottle-count-input label > span {
  color: var(--muted); font-size: 10px; white-space: nowrap;
}
.bottle-count-input input { width: 7ch; text-align: right; }
.bottle-count-input select { min-width: 9.5em; }
.bottle-count-input input, .bottle-count-input select {
  min-height: 42px; font-size: 16px;
}
.count-confirm { white-space: nowrap; }
.count-confirm .btn { margin-right: 5px; }
.count-unit-mobile { display: none; }
.count-finish {
  /* Finishing belongs after the rows, never over the current counting work. */
  position: static; display: flex; align-items: center;
  justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin-top: 20px; padding: 16px 0; border-top: 1px solid var(--border);
}
.count-finish-copy { flex: 1 1 300px; min-width: 0; }
.count-finish h2 { margin: 0 0 4px; font-size: 16px; }
.count-finish p { margin: 0; max-width: 64ch; }
.count-finish [data-complete-state]:empty { display: none; }
.count-finish [data-complete-state] { flex-basis: 100%; }
.count-history { margin-top: 22px; }
.stock-count-no-results { margin: 16px 0; }

.item-drawer-scrim {
  position: fixed; inset: 0; z-index: 74; background: rgba(10,16,30,.34);
  opacity: 0; transition: opacity .2s var(--ease);
}
.item-drawer-scrim.on { opacity: 1; }
.item-drawer {
  position: fixed; z-index: 75; top: 0; right: 0; bottom: 0;
  width: min(520px, 92vw); padding: 0; background: var(--plane);
  border-left: 1px solid var(--border); box-shadow: -18px 0 44px rgba(10,16,30,.18);
  transform: translateX(102%); transition: transform .24s var(--ease);
  overflow-y: auto; overscroll-behavior: contain;
}
.item-drawer.open { transform: translateX(0); }
.item-drawer-head {
  position: sticky; top: 0; z-index: 1; display: flex; align-items: flex-start;
  justify-content: space-between; gap: 16px; padding: 22px 24px 16px;
  border-bottom: 1px solid var(--border); background: var(--plane);
}
.item-drawer-head h2 { margin: 3px 0 0; font-size: 24px; }
.item-drawer-head p { margin: 4px 0 0; }
.item-drawer-close {
  flex: none; width: 48px; height: 48px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); font-size: 27px; cursor: pointer;
}
.item-drawer-body { padding: 20px 24px 40px; }
.item-drawer-body h3 { margin: 0; font-size: 14px; }
.item-drawer-help { margin: 5px 0 16px; }
.item-category-editor { margin: 0 0 20px; padding: 0 0 20px;
  border-bottom: 1px solid var(--border-soft); }
.item-category-editor select { min-height: 42px; min-width: min(260px, 100%); }
.item-category-editor .btn { min-height: 42px; }
.item-purchase { padding: 15px 0; border-top: 1px solid var(--border-soft); }
.item-purchase-top { display: flex; justify-content: space-between; gap: 14px; }
.item-purchase-top time { color: var(--muted); white-space: nowrap; }
.item-purchase p { margin: 6px 0 0; font-variant-numeric: tabular-nums; }
.item-purchase-empty { padding: 18px 0; border-top: 1px solid var(--border-soft); color: var(--muted); }
body.item-drawer-open { overflow: hidden; }

@media (max-width: 1180px) {
  .stock-count-intro { align-items: flex-start; }
  .count-progress { min-width: 104px; }
  .count-progress progress { width: 104px; }
  .count-category .scroll-x { overflow: visible; }
  .count-board { min-width: 0; table-layout: auto; }
  .count-board thead { display: none; }
  .count-board, .count-board tbody, .count-board tr, .count-board td { display: block; }
  .count-board tr {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    gap: 7px 12px; padding: 12px 0; border-bottom: 1px solid var(--grid);
  }
  .count-board td { width: auto !important; padding: 0; border: 0; }
  .count-board td:first-child { grid-column: 1; grid-row: 1; }
  .count-board td:nth-child(2) { grid-column: 1; grid-row: 2; text-align: left; }
  .count-board td:nth-child(3) { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
  .count-board td:nth-child(4) { grid-column: 1 / -1; grid-row: 3; }
  .count-unit-mobile { display: inline; }
  .count-input { min-height: 42px; width: 10ch; font-size: 16px; }
  .bottle-count-input { justify-content: flex-end; flex-wrap: wrap; }
  .bottle-count-input input { width: 6ch; }
  .bottle-count-input select { min-width: 8.5em; }
  .count-confirm { display: flex !important; align-items: center; gap: 6px; }
  .count-confirm .btn { min-height: 40px; flex: 1; margin: 0; }
}

@media (max-width: 720px) {
  .stock-count-actions, .count-finish-actions { width: 100%; }
  .stock-count-actions .btn { flex: 1 1 auto; }
  .count-finish-actions { flex-direction: column-reverse; align-items: stretch; }
  .stock-count-search { grid-template-columns: 1fr; }
  .stock-count-search [data-count-search-status] { grid-column: 1; grid-row: auto; }
  .item-drawer { width: 100vw; }
  .item-drawer-head, .item-drawer-body { padding-left: 18px; padding-right: 18px; }
}

/* iPads are used by touch even when the stock sheet still has table columns. */
@media (min-width: 721px) and (max-width: 1180px) {
  .count-board td { padding-top: 13px; padding-bottom: 13px; }
  .count-board .btn, .count-board input { min-height: 46px; }
  .count-board tr[data-count-row] { min-height: 58px; }
  .stock-count-toolbar .btn, .stock-count-toolbar input { min-height: 46px; }
}

/* The invoice itself, shown beside what we made of it. */
.doc-evidence { margin-bottom: 14px; }
.doc-pages { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 4px; }
.doc-page { position: relative; margin: 0; flex: 0 0 auto; max-width: 100%; }
.doc-page img {
  display: block; max-width: min(680px, 100%); height: auto;
  border: 1px solid var(--grid); border-radius: 0; background: #fff;
}
.doc-page figcaption { font-size: 11px; color: var(--muted); margin-top: 4px; }
.doc-mark {
  position: absolute; pointer-events: none; border: 2px solid var(--accent-gold);
  background: color-mix(in srgb, var(--accent-gold) 18%, transparent);
  border-radius: 2px;
}
.reread-actions { margin-top: 12px; align-items: center; }

/* Correcting what the reader got wrong. */
.purch-what { min-width: 260px; }
.fig {
  width: 100%; font-size: 12px; padding: 3px 5px;
  border: 1px solid var(--grid); border-radius: 0; background: transparent;
  color: inherit; font-variant-numeric: tabular-nums;
}
.fig:focus { border-color: var(--accent-gold); outline: none; }
td.num .fig { text-align: right; }
.fig-unit { margin-top: 3px; font-size: 11px; }
.was { font-size: 10.5px; color: var(--muted); margin-top: 2px; font-style: italic; }
.purch-src { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap;
  font-size: 10.5px; color: var(--muted); margin-top: 4px; }
.purch-raw { font-size: 10.5px; margin-top: 4px; }
.purch-raw > summary { cursor: pointer; color: var(--muted); }
.purch-raw code { font-size: 10.5px; word-break: break-word; }
.purch-suggest { font-size: 11px; color: var(--warning); margin-top: 5px;
  max-width: 44ch; }
.btn-link {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 30px; padding: 4px 8px; font: inherit; font-size: inherit;
  border: 1px solid var(--border); border-radius: 0;
  background: var(--surface); color: var(--ink); cursor: pointer;
  text-decoration: none;
}
.btn-link:hover { background: var(--wash-1); border-color: var(--accent-gold-line); }
@media (max-width: 820px) {
  .btn-link { min-height: 40px; }
}

.supplier-fix { margin-bottom: 14px; }
.purchase-list-heading { display: flex; align-items: baseline; gap: 12px;
  flex-wrap: wrap; }
.purchase-list-heading h2 { margin-right: auto; }
.supplier-fix form { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.supplier-fix label { display: grid; gap: 4px; min-width: 260px; }
.supplier-fix input[type="text"], .supplier-fix select { min-width: 260px; min-height: 42px; }
.supplier-new[hidden] { display: none; }
.purch-empty td { padding: 20px 14px; }
.purch-empty strong, .purch-empty span { display: block; }
.purch-empty span { margin-top: 6px; color: var(--muted); max-width: 76ch; }
.purch-line-pending td {
  padding: 18px 14px; color: var(--ink-2); font-weight: 600;
  background: var(--wash-1);
}
.purch-lines tr:target td {
  background: var(--wash-1);
  box-shadow: inset 0 2px 0 var(--accent), inset 0 -2px 0 var(--accent);
}
.purch-lines tr:target td:first-child { box-shadow: inset 2px 0 0 var(--accent), inset 0 2px 0 var(--accent), inset 0 -2px 0 var(--accent); }
.purch-lines tr:target td:last-child { box-shadow: inset -2px 0 0 var(--accent), inset 0 2px 0 var(--accent), inset 0 -2px 0 var(--accent); }

/* The purchase header is read in two passes: what the supplier printed, then
   how Imvelo paid and approved it.  Keeping those as aligned rows stops one
   long name or label turning the whole header into a row of mismatched boxes. */
.purchase-details-form { display: grid; gap: 14px; margin-top: 12px; }
.purchase-details-row { display: grid; gap: 12px 16px; align-items: end; }
.purchase-details-document {
  grid-template-columns: repeat(3, minmax(180px, 1fr));
}
.purchase-details-document label small { line-height: 1.35; font-weight: 400; }
.purchase-details-payment {
  grid-template-columns: minmax(190px, 1fr) minmax(180px, .9fr)
    minmax(220px, 1.15fr) minmax(390px, 1.8fr);
  padding-top: 14px; border-top: 1px solid var(--border-soft);
}
.purchase-details-row > label { display: grid; gap: 5px; min-width: 0;
  align-content: end; }
.purchase-details-row input[type="text"],
.purchase-details-row input[type="number"],
.purchase-details-row select, .purchase-details-row .purchase-date-button {
  width: 100%; min-width: 0; min-height: 42px;
}
.purchase-details-row select { white-space: nowrap; }
.purchase-details-actions { display: flex; align-items: center;
  justify-content: flex-end; gap: 10px; min-width: 0;
  padding-left: 16px; border-left: 1px solid var(--border-soft); }
.purchase-details-actions .btn, .purchase-details-actions .btn-link {
  min-height: 42px; white-space: nowrap; }
.purchase-details-actions .save-state { min-width: 58px; }
@media (max-width: 1180px) {
  .purchase-details-document { grid-template-columns: repeat(3, minmax(150px, 1fr)); }
  .purchase-details-payment { grid-template-columns: repeat(3, minmax(170px, 1fr)); }
  .purchase-details-actions { grid-column: 1 / -1; justify-content: flex-start;
    padding: 12px 0 0; border-left: 0; border-top: 1px solid var(--border-soft); }
}
@media (max-width: 760px) {
  .purchase-details-document, .purchase-details-payment {
    grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 520px) {
  .purchase-details-document, .purchase-details-payment { grid-template-columns: 1fr; }
  .purchase-details-actions { display: grid; grid-template-columns: 1fr 1fr; }
  .purchase-details-actions .btn, .purchase-details-actions .btn-link { width: 100%; }
  .purchase-details-actions .save-state { grid-column: 1 / -1; min-width: 0; }
}

/* Invoice dates are accounting evidence, so the picker is explicit and
   consistent across phone and desktop rather than inheriting a browser's
   tiny locale-dependent date control (and its extension overlays). */
.purchase-date-field { display: grid; gap: 4px; }
.purchase-date-button {
  display: inline-flex; align-items: center; gap: 9px; min-height: 42px;
  padding: 7px 11px; border: 1px solid var(--grid); border-radius: 0;
  background: var(--surface); color: var(--ink); font: inherit; cursor: pointer;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.purchase-date-button:hover, .purchase-date-button:focus-visible {
  border-color: var(--accent-gold-line); outline: none;
}
.purchase-date-button.is-empty { color: var(--muted); }
.purchase-date-button svg { width: 18px; height: 18px; fill: none;
  stroke: currentColor; stroke-width: 1.6; stroke-linecap: round;
  stroke-linejoin: round; }
.purchase-calendar {
  width: min(390px, calc(100vw - 24px)); padding: 18px;
  border: 1px solid var(--ink); border-radius: 0; box-shadow: none;
  background: var(--plane); color: var(--ink);
}
.purchase-calendar::backdrop { background: rgba(20, 31, 56, .55); }
.purchase-calendar-head { display: grid; grid-template-columns: 40px 1fr 40px;
  align-items: center; gap: 8px; margin-bottom: 14px; }
.purchase-calendar-head h3 { margin: 0; text-align: center; font-size: 15px;
  font-weight: 500; }
.purchase-calendar-nav { min-height: 38px; border: 0; background: transparent;
  color: var(--ink); cursor: pointer; font-size: 18px; }
.purchase-calendar-nav:hover:not(:disabled) { background: var(--wash-1); }
.purchase-calendar-nav:disabled { color: var(--muted); opacity: .35; cursor: default; }
.purchase-calendar-weekdays, .purchase-calendar-days {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px;
}
.purchase-calendar-weekdays { margin-bottom: 5px; color: var(--muted);
  font-size: 10.5px; text-align: center; }
.purchase-calendar-days button, .purchase-calendar-blank { aspect-ratio: 1; }
.purchase-calendar-days button { min-width: 0; min-height: 38px; padding: 0;
  border: 1px solid transparent; border-radius: 0; background: transparent;
  color: var(--ink); font: inherit; font-size: 12.5px; cursor: pointer; }
.purchase-calendar-days button:hover:not(:disabled) { border-color: var(--accent-gold-line);
  background: var(--wash-1); }
.purchase-calendar-days button.is-selected { background: var(--accent-gold-line);
  border-color: var(--accent-gold-line); color: #141f38; font-weight: 600; }
.purchase-calendar-days button.is-today:not(.is-selected) {
  border-color: var(--accent-gold-line); }
.purchase-calendar-days button:disabled { color: var(--muted); opacity: .3;
  cursor: default; }
.purchase-calendar-foot { display: flex; align-items: center;
  justify-content: flex-end; gap: 10px; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--border); }
@media (max-width: 560px) {
  .purchase-calendar { padding: 15px 12px; }
  .purchase-calendar-days button { min-height: 42px; }
  .purchase-date-button { width: 100%; justify-content: flex-start; }
}

.purch-lines td { vertical-align: top; }
.purch-lines .fig-unit { width: 100%; margin-top: 4px; }
.purch-lines .fig-total { font-weight: 600; }
.purch-calculated { margin-top: 4px; color: var(--muted); font-size: 10.5px; }
.purch-decide { min-width: 320px; }
.purch-basis { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap;
  font-size: 11.5px; margin-bottom: 6px; }
.purch-basis .dim { max-width: 44ch; }
.purch-answer { font-size: 12px; font-weight: 600; margin: 0 0 6px; }
.purch-answer.is-required { color: var(--warning); }
.purch-answer.is-done { color: var(--ink-2); }
.purch-pick { display: grid; gap: 6px; }
.purch-pick select, .purch-pick input[type="text"] { width: 100%; }
.purch-sku-row { display: grid; grid-template-columns: minmax(0, 1fr) auto;
                 gap: 8px; align-items: stretch; }
.purch-sku-combo { min-width: 0; }
.purch-pick input[type="search"].purch-sku-search { width: 100%; min-width: 0; }
.purch-sku-options { border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); margin-top: 4px; max-height: 260px; overflow-y: auto;
  padding: 4px; }
.purch-sku-option { display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 6px;
  background: transparent; color: var(--ink); text-align: left; cursor: pointer; }
.purch-sku-option:hover, .purch-sku-option.is-active {
  background: var(--wash-1); }
.purch-sku-option[hidden] { display: none; }
.purch-sku-option strong { font-size: 12.5px; font-weight: 600; }
.purch-sku-option span { color: var(--muted); font-size: 10.5px; text-align: right; }
.purch-sku-empty { font-size: 11.5px; margin: 8px; }
.purch-create-sku { white-space: nowrap; }
.purch-skip { color: var(--critical) !important;
  border-color: var(--accent-coral-line) !important; }
.purch-skip:hover { border-color: var(--critical) !important;
  background: color-mix(in srgb, var(--critical) 7%, transparent) !important; }
.purch-overhead[aria-pressed="true"] { border-color: var(--accent-gold-line);
  background: color-mix(in srgb, var(--accent-gold) 10%, transparent); }
.purch-overhead-result { margin: 0; color: var(--ink-2); max-width: 58ch; }
.purch-new { display: grid; gap: 6px; grid-template-columns: 2fr 1fr 1fr; }
.purch-remember { font-size: 11px; color: var(--muted); display: flex; gap: 6px;
  align-items: baseline; }
.purch-remember em { color: var(--ink); font-style: normal; }
/* The pack: how much is in one of whatever the supplier billed. It sits above
   the Save button on every line because every cost figure downstream is qty x
   this, and a number that decides the price of a kilo of coffee should not be
   something the screen works out privately. Marked off by a rule, not a box —
   ch.2: a region is the same paper, told apart by a hairline. */
.purch-pack { border-top: 1px solid var(--border-soft); margin-top: 8px;
              padding: 8px 0 0 10px; border-left: 1px solid var(--border-soft);
              padding-left: 10px; }
.purch-pack-ask { font-size: 11.5px; color: var(--ink-2); margin-bottom: 5px; }
.purch-pack-ask strong { font-weight: 500; color: var(--ink); }
.purch-bottle-sizes { display: flex; align-items: center; flex-wrap: wrap;
  gap: 6px; margin: 0 0 7px; font-size: 11.5px; color: var(--ink-2); }
.purch-bottle-sizes[hidden] { display: none; }
.purch-bottle-sizes .btn { margin: 0; }
.purch-bottle-size[aria-pressed="true"] { border-color: var(--accent-gold-line); }
.purch-pack-row { display: flex; gap: 6px; align-items: center;
                  flex-wrap: wrap; }
/* .purch-pick sets width:100% on every input and select inside it, which
   would stack these three one per line. The pack reads as one sentence — a
   number, its unit, and where it came from — so it stays on one. */
.purch-pick input[type="text"].purch-pack-qty { width: 8ch; flex: 0 0 auto;
  text-align: right; font-variant-numeric: tabular-nums; }
.purch-pick select.purch-pack-unit { width: auto; flex: 0 0 auto; min-width: 8ch;
  padding-right: 24px; }
.purch-pack-was { font-size: 11px; flex: 1 1 12ch; min-width: 0; }
/* The arithmetic, in the open: 10 x 60 kg = 600 kg - K 75.00 per kg. */
.purch-pack-sum { font-size: 11.5px; color: var(--ink); margin-top: 5px;
                  font-variant-numeric: tabular-nums; min-height: 1em; }
.purch-pack-sum:empty { margin-top: 0; min-height: 0; }
.purch-pack-sum.is-clash { color: var(--warning); }
/* ch.3: only trouble is coloured. An unanswered pack is the one thing on the
   row that stops it costing, so it is the one thing that gets the gold. */
.purch-pack.is-unanswered { border-left-color: var(--accent-gold-line); }
.purch-pack.is-unanswered .purch-pack-qty { border-color: var(--accent-gold-line); }
.purch-pack.is-unanswered .purch-pack-was { color: var(--warning); }
.purch-result { margin-top: 6px; font-size: 11.5px; }
.purch-blocked { color: var(--warning); margin: 0; font-size: 11.5px; max-width: 46ch; }
.deleted-lines-summary { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  margin: 0 0 10px; padding: 0 0 10px; border-bottom: 1px solid var(--border-soft); }
.deleted-lines-summary .btn { margin: 0; }
tr.is-skipped { opacity: .55; }
tr.is-overhead td:first-child { border-left: 2px solid var(--accent-gold-line); }
tr.is-open td:first-child { border-left: 2px solid var(--accent-gold-line); }
tr.is-open .purch-sku-search { border-color: var(--accent-gold-line); }
tr.is-blocked td:first-child { border-left: 1px solid var(--warning); }

@media (max-width: 760px) {
  .supplier-fix form, .supplier-fix label,
  .supplier-fix input[type="text"], .supplier-fix select,
  .supplier-fix .btn { width: 100%; min-width: 0; }
  .purch-new { grid-template-columns: 1fr; }
  .purch-sku-row { grid-template-columns: 1fr; }
  .purch-sku-options { max-height: 220px; }
  .purch-create-sku { width: 100%; }
  .purch-save, .purch-overhead, .purch-skip { width: 100%; margin-left: 0 !important; }
  .deleted-lines-summary .btn { width: 100%; }
  .purch-pick select, .purch-pick input[type="text"],
  .purch-pick input[type="search"] { min-height: 38px; }
  .purch-pack-qty, .purch-pack-unit { min-height: 38px; }
  .purch-bottle-sizes .btn { flex: 1 1 auto; }
  .purch-pack-was { flex-basis: 100%; }
}

/* Invoice review on iPad: one readable work card per supplier line, with
   controls large enough for touch. Horizontal five-column accounting tables
   are fine for a laptop and a trap when somebody is holding the document. */
@media (max-width: 1180px) {
  .purch-lines, .purch-lines tbody { display: block; width: 100%; }
  .purch-lines > tbody > tr:first-child { display: none; }
  .purch-lines tr[data-line] {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px; margin: 0 0 16px; padding: 16px;
    border: 1px solid var(--border); background: var(--surface);
  }
  .purch-lines tr[data-line].is-open { border-color: var(--accent-gold-line); }
  .purch-lines tr[data-line] td { display: block; width: auto; min-width: 0; padding: 0; border: 0; text-align: left; }
  .purch-lines tr[data-line] td:first-child,
  .purch-lines tr[data-line] td:nth-child(5) { grid-column: 1 / -1; }
  .purch-lines tr[data-line] td:nth-child(2)::before,
  .purch-lines tr[data-line] td:nth-child(3)::before,
  .purch-lines tr[data-line] td:nth-child(4)::before {
    display: block; margin-bottom: 5px; color: var(--muted); font-size: 11px;
  }
  .purch-lines tr[data-line] td:nth-child(2)::before { content: "Quantity"; }
  .purch-lines tr[data-line] td:nth-child(3)::before { content: "Unit price"; }
  .purch-lines tr[data-line] td:nth-child(4)::before { content: "Line amount"; }
  .purch-lines .fig, .purch-lines select, .purch-lines button.btn,
  .supplier-fix select, .supplier-fix input, .supplier-fix button {
    min-height: 46px; font-size: 16px;
  }
  .purch-lines .purch-new { grid-template-columns: 2fr 1fr 1fr; }
  .purch-empty { display: block; }
  .purch-empty td { display: block; }
}

@media (max-width: 620px) {
  .purch-lines tr[data-line] { grid-template-columns: 1fr; padding: 14px; }
  .purch-lines tr[data-line] td,
  .purch-lines tr[data-line] td:first-child,
  .purch-lines tr[data-line] td:nth-child(5) { grid-column: 1; }
  .purch-lines .purch-new { grid-template-columns: 1fr; }
  .reread-actions .btn { width: 100%; }
}

/* The bank-account editor on the Invoices page. */
.bank-editor { display: grid; gap: 12px; }
.bank-card { border: 0; border-top: 1px solid var(--border-soft); border-radius: 0;
             padding: 10px 0 10px 14px; }
.bank-card > h4 { margin: 0 0 8px; font-size: 12.5px; display: flex; gap: 10px;
  align-items: baseline; justify-content: space-between; flex-wrap: wrap; }
.bank-card-grid { display: grid; gap: 8px 14px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.bank-on { font-size: 11px; color: var(--muted); font-weight: 400;
  display: flex; gap: 6px; align-items: center; }

/* ---- Raising one, on the Invoices page ---- */
/* Starting an invoice is the thing this page is for, so it looks like
   something you press. It was a line of text with a small triangle in front of
   it, sitting on a full-width rule — indistinguishable from a heading, and the
   most-used control on the page. The <details> still does the work; only the
   summary is dressed as the button it has always been. */
.new-invoice { margin-bottom: 14px; }
.new-invoice > summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 34px; padding: 0 16px;
  font-size: 13px; color: var(--btn-fg); background: var(--btn-bg);
  border: 1px solid var(--btn-bg);
}
.new-invoice > summary::-webkit-details-marker { display: none; }
.new-invoice > summary::before { content: "+"; font-size: 15px; opacity: .8; }
.new-invoice[open] > summary::before { content: "\00D7"; }
.new-invoice > summary:hover { opacity: .88; }
.new-invoice > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* The explanation sits beside the button, not inside it — and not after the
   summary either, where a closed <details> would hide it. It hangs off the
   element itself, so it is there while the form is shut and gone once it is
   open and the form says the same thing better. */
.new-invoice::after {
  content: attr(data-note); margin-left: 12px;
  font-size: 11.5px; color: var(--muted); vertical-align: middle;
}
.new-invoice[open]::after { content: none; }
.new-invoice form { margin-top: 14px; }
.inv-head-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.inv-head-row label {
  display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 170px;
  font-size: 11px; color: var(--muted);
}
.inv-head-row input, .inv-editor input, .inv-editor select, .inv-editor textarea,
#company-form input {
  font: inherit; font-size: 12.5px; padding: 6px 8px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  min-width: 0;
}
.inv-head-row input:focus, .inv-editor input:focus, .inv-editor select:focus,
.inv-editor textarea:focus,
#company-form input:focus { border-color: var(--accent-gold); outline: none; }
.inv-hint { font-size: 10.5px; }
.inv-editor { min-width: 560px; }
.inv-editor .inv-item { width: 100%; }
.inv-editor .inv-desc { width: 100%; margin-top: 5px; }
.inv-editor .inv-qty { width: 7ch; text-align: right; }
.inv-editor .inv-price { width: 11ch; text-align: right; }
.inv-editor .inv-amt { font-variant-numeric: tabular-nums; padding-top: 12px; }
/* A line priced under the published list is the one thing on this form worth
   noticing before it is issued, so it is marked while it is still editable. */
.inv-editor tr.discounted .inv-price { border-color: var(--warning); }
.inv-foot { display: flex; justify-content: space-between; align-items: flex-end;
  gap: 20px; flex-wrap: wrap; margin-top: 14px; }
.inv-sum { font-size: 12.5px; min-width: 200px; }
.inv-sum > div { display: flex; justify-content: space-between; gap: 20px; padding: 3px 0; }
.inv-sum strong { font-variant-numeric: tabular-nums; }
.inv-sum-total { border-top: 1px solid var(--baseline); margin-top: 4px; padding-top: 6px !important; }
.inv-sum-total strong { font-size: 16px; font-family: "Hanken Grotesk", "Helvetica Neue", Arial, sans-serif; }
.inv-actions { display: flex; align-items: center; gap: 10px; }
.btn-quiet { font-size: 11px; padding: 2px 8px; }
#inv-discount-note { font-size: 11.5px; color: var(--warning); margin: 8px 0 0; }

/* A quote carries more conversation than a tax invoice. Give names, dates and
   descriptions enough room to be read while the offer is still editable. */
.quote-section-title { margin-top: 24px; }
.quote-customer-fields, .quote-date-fields { display: grid; align-items: start; }
.quote-customer-fields { grid-template-columns: repeat(4, minmax(180px, 1fr)); }
.quote-date-fields { grid-template-columns: repeat(5, minmax(150px, 1fr)); }
.quote-date-fields .purchase-date-button { width: 100%; justify-content: flex-start; }
.quote-lines-head { display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin-top: 24px; }
.quote-lines-head h2 { margin: 0; }
.quote-vat-toggle { display: inline-flex; align-items: center; gap: 8px;
  min-height: 38px; padding: 0 12px; border: 1px solid var(--accent-gold-line);
  background: var(--wash-1); color: var(--ink); font-size: 12px; cursor: pointer; }
.quote-vat-toggle input { accent-color: var(--accent-gold); }
.quote-price-help { margin: 7px 0 10px; }
.quote-lines-wrap { border: 1px solid var(--border-soft); }
.quote-lines-wrap .data { margin: 0; }
.quote-lines-wrap th { white-space: normal; }
.quote-lines-wrap td { vertical-align: top; }
.quote-item-col { width: 20%; }
.quote-desc-col { width: 42%; }
.quote-qty-col { width: 9%; }
.quote-price-col { width: 14%; }
.quote-amount-col { width: 12%; }
.quote-delete-col { width: 3%; }
.inv-editor .line-item, .inv-editor .line-desc, .inv-editor .line-qty,
.inv-editor .line-price { width: 100%; }
.inv-editor .line-desc { display: block; min-height: 48px; line-height: 1.4;
  resize: vertical; overflow-y: hidden; }
.inv-editor .line-qty, .inv-editor .line-price { text-align: right; }
.quote-note-field { display: grid; gap: 6px; margin-top: 22px; max-width: 850px;
  font-size: 11px; color: var(--muted); }
.quote-note-field textarea { width: 100%; min-height: 76px; padding: 9px 10px;
  border: 1px solid var(--border); border-radius: 0; background: var(--surface);
  color: var(--ink); font: inherit; line-height: 1.45; resize: vertical; }
.quote-note-field textarea:focus { border-color: var(--accent-gold); outline: none; }

@media (max-width: 1100px) {
  .quote-customer-fields, .quote-date-fields {
    grid-template-columns: repeat(2, minmax(180px, 1fr)); }
  .quote-lines-wrap .inv-editor { min-width: 860px; }
}
@media (max-width: 620px) {
  .quote-customer-fields, .quote-date-fields { grid-template-columns: 1fr; }
  .quote-lines-head { align-items: flex-start; flex-direction: column; }
  .quote-vat-toggle { width: 100%; }
}

@media (max-width: 760px) {
  .inv-head-row label { min-width: 140px; }
  .inv-head-row input, .inv-editor input, .inv-editor select, .inv-editor textarea,
  #company-form input, #bank-form input[type="text"] { min-height: 38px; }
}

/* A payslip used to be something the office printed. It is now mostly read on
   a phone, standing up, by the person it belongs to — so the header stops
   fighting for one line and the print button stops wrapping to three. */
@media (max-width: 620px) {
  .payslip { padding: 20px 18px; }
  .ps-cols { grid-template-columns: 1fr; gap: 14px; }
  .ps-head { flex-direction: column; align-items: flex-start; gap: 12px; }
  .ps-title { text-align: left; }
  .ps-who { gap: 10px 22px; }
  .ps-foot { flex-direction: column; align-items: flex-start; }
  .ps-print .btn { white-space: nowrap; min-height: 42px; padding: 0 16px; }
}
/* A one-page invoice must come out as one page — on a printer and in the PDF
   the server renders with the same stylesheet. The margins live on @page so the
   document itself carries none, and the grids are pinned to explicit columns
   because auto-fit/minmax is not resolved by every print engine. */
@page { size: A4; margin: 16mm 15mm; }
@media print {
  /* White. A browser drops background colours when it prints, which is why
     the document that comes out of the print dialog is on paper and the one
     the server rendered was on bone cream from edge to edge — WeasyPrint
     paints what the stylesheet says. So the stylesheet says paper. The navy
     balance bar keeps its fill on purpose, below. */
  .payslip-body { background: #fff; padding: 0; }
  .payslip { box-shadow: none; border: 0; max-width: none; padding: 0;
             background: #fff;
             /* Brave/macOS Quartz turns the variable Hanken webfont into
                outlines. The page still looks right, but names, amounts and
                bank details stop being selectable or searchable. A system
                PDF font stays real text in the browser's print-to-PDF path. */
             font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; }
  .payslip .brand-name, .payslip .ps-month, .payslip .ps-net strong {
    font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
  }
  /* A table at width:100% with a max-width is laid out at the full width by
     print engines that resolve max-width after the columns. Given a definite
     width there is nothing to resolve, and the totals sit in their column on
     the right where they belong. */
  .inv-totals { width: 74mm; max-width: none; margin-left: auto; }
  /* The letterhead is one line of contact details, not two: the seller block
     takes the room it needs and the invoice number takes only its own. */
  .ps-head { gap: 14px; }
  .ps-brand { flex: 1 1 auto; min-width: 0; }
  .ps-title { flex: 0 0 auto; white-space: nowrap; }
  .inv-seller-ids { font-size: 9.5px; }
  .ps-print, .no-print { display: none !important; }
  .ps-net { background: #141f38 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .inv-banks { grid-template-columns: 1fr 1fr; gap: 8px 26px; margin-top: 6px; }
  .ps-cols { grid-template-columns: 1fr 1fr; }
  .ps-head { padding-bottom: 10px; }
  .ps-who { padding: 9px 0; }
  .inv-thanks { margin-top: 10px; }
  .ps-foot { margin-top: 12px; padding-top: 8px; break-inside: avoid; }
  .inv-lines { break-inside: auto; }
  .payslip { font-size: 12px; }
}

/* Screen-only settlement ribbon on the invoice document: the person checking
   "has this been paid?" should not have to walk back to the list. */
.inv-ribbon { display: flex; gap: 10px; align-items: center; margin: 0 0 12px;
              padding: 8px 12px; border-radius: 0; font-size: 12.5px;
              background: var(--wash-1); }
.inv-ribbon .status { text-transform: uppercase; letter-spacing: .04em; }
/* Above the ribbon and outside the document proper: it is a way out of the
   page, not a line of the invoice, so it sits on the paper's margin. */
.inv-back { margin: 0 0 10px; }

/* --- The roasting log -----------------------------------------------------
   A form the operator fills standing at the drum, so the fields wrap into as
   many columns as the screen gives and the two weights sit together. */
.roast-form {
  display: grid; gap: 12px 18px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  align-items: end;
}
.roast-f { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.roast-f > span { font-size: 11px; color: var(--muted); }
.roast-f em { font-style: normal; opacity: .7; }
.roast-f input, .roast-f select {
  font: inherit; font-size: 13.5px; padding: 6px 2px; border-radius: 0;
  border: 0; border-bottom: 1px solid var(--border);
  background: none; color: var(--ink); min-width: 0;
}
.roast-f select { padding-left: 0; }
.roast-f input:focus, .roast-f select:focus {
  outline: none; border-bottom-color: var(--accent-gold-line);
}
.roast-num input { font-variant-numeric: tabular-nums; }
.roast-wide { grid-column: 1 / -1; }
/* the yield, worked out as the weights are typed — the point of the record */
.roast-yield {
  grid-column: 1 / -1; margin: 4px 0 0; font-size: 12.5px; color: var(--ink-2);
  padding-left: 12px; border-left: 1px solid var(--border);
}
.roast-yield.warn { border-left-color: var(--warning); color: var(--ink); }
.roast-yield.bad { border-left-color: var(--critical); color: var(--critical); }
.roast-go { grid-column: 1 / -1; display: flex; gap: 14px; align-items: baseline; }
/* a delivery that is finished stays on the list — the code is on bags out
   there, and a batch that cannot be looked up is a batch nobody trusts */
tr.lot-done > td { opacity: .55; }
.lot-new, .lot-upload { margin-top: 4px; }
.lot-new > summary, .lot-upload > summary {
  cursor: pointer; font-size: 12.5px; color: var(--ink-2);
  padding: 8px 0; border-top: 1px solid var(--border-soft);
}
.lot-upload > summary { border-top: 0; padding: 2px 0; color: var(--muted); }
.lot-new > summary:hover, .lot-upload > summary:hover { color: var(--ink); }
.lot-upload form {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 8px 0; font-size: 12px;
}
.lot-upload input[type=text] {
  font: inherit; font-size: 12px; border: 0;
  border-bottom: 1px solid var(--border); background: none; color: var(--ink);
  padding: 4px 2px; min-width: 14ch;
}

/* a loss outside the band a roast normally takes: kept, and marked */
.roast-odd { color: var(--warning); }

/* --- A sheet that comes up over the page ---------------------------------
   Edition 02 has no cards, but a modal is not a card: it is a second surface,
   and the reader has to be able to tell where the page stopped and it began.
   So it earns exactly two things the flat system otherwise refuses — a ground
   of its own and a hairline all the way round — and nothing else. No radius,
   no shadow; the backdrop does the lifting. */
/* The recipe book against the menu. ch.3: only trouble is coloured, and a
   costing that has drifted from the thing it costs is trouble. */
.sync-band { border-left: 1px solid var(--accent-gold-line); padding-left: 14px; }
.sync-band h2 { color: var(--accent-gold); }
.sync-list { margin: 6px 0 10px; padding-left: 18px; font-size: 12.5px;
             line-height: 1.55; max-width: 88ch; }
.sync-list li { margin-bottom: 5px; }
.sync-note { font-size: 11.5px; margin: 6px 0 0; }

/* ------------------------------------------------------- the menu, costed
   The café's cost of sales is only as good as its recipe book, and a recipe
   book is only useful if somebody can argue with it. */
.recipe-board td { vertical-align: top; }
/* ch.3: only trouble is coloured. A dish making under half its price is the
   one thing on this page somebody has to decide about. */
.thin-margin { color: var(--warning); }
.recipe-doc { display: grid; gap: 4px; }
.recipe-lines td, .recipe-margin td { vertical-align: baseline; }
.recipe-cost-source {
  color: inherit; text-decoration-line: underline;
  text-decoration-style: dotted; text-decoration-color: var(--muted);
  text-underline-offset: 3px;
}
.recipe-cost-source:hover, .recipe-cost-source:focus-visible {
  color: var(--brand); text-decoration-style: solid;
  text-decoration-color: currentColor;
}
.recipe-cost-figure { display: inline-block; min-width: 4.5ch; }
.recipe-qty { width: 7ch; text-align: right; font-variant-numeric: tabular-nums; }
.recipe-total td { border-top: 1px solid var(--border); padding-top: 8px; }
.recipe-note { font-size: 11.5px; max-width: 60ch; margin: 8px 0 0; }
.recipe-add { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.recipe-add select { flex: 1 1 18ch; min-width: 0; }
.recipe-add-qty { width: 9ch; text-align: right; }
.preparation-yield-qty, [data-preparation-yield] { width: 10ch; text-align: right; }
.preparation-yield-unit, [data-preparation-unit] { width: 10ch; }
.preparation-board .recipe-board td:first-child { min-width: 18rem; }
.preparation-create .recipe-add > input:first-child { flex: 1 1 18ch; }
.recipe-input-toggle { display: flex; gap: 10px; align-items: flex-start; margin: 14px 0; }
.recipe-input-toggle input { margin-top: 3px; width: 18px; height: 18px; flex: 0 0 auto; }
tr.off { opacity: .55; }
@media (max-width: 560px) {
  .recipe-add select, .recipe-add-qty { flex-basis: 100%; width: 100%; }
}

/* ------------------------------------------------------------------ the plan
   Next month before it happens: every line proposed from what the app can see,
   every line changeable, both kept. */
.budget-table td { vertical-align: top; }
.budget-sec td { font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); padding-top: 14px; border-bottom: 1px solid var(--border-soft); }
.budget-in { width: 11ch; text-align: right; font-variant-numeric: tabular-nums; }
.budget-basis { font-size: 11.5px; max-width: 52ch; }
/* ch.3: only trouble is coloured — and a line somebody has overruled is not
   trouble, it is a decision. It gets the rule, not the colour. */
tr.is-set > td:first-child { box-shadow: inset 2px 0 0 var(--accent-gold-line); }
.budget-said { color: var(--ink-2); }
.budget-total td { border-top: 1px solid var(--border); padding-top: 8px; }
.bad-figure { color: var(--critical); }
.budget-need { margin-top: 4px; }
.budget-note { font-size: 11.5px; max-width: 84ch; margin-top: 10px; }

/* ------------------------------------------------- the month, while it runs
   The management P&L is about a month that has already ended. This is the one
   the business is in, from its own transactions, and it opens on it. */
.mtd-table td { vertical-align: top; }
/* The basis under each figure: how the number was reached, in the place where
   somebody is already looking at it. Left-aligned even in a numeric column —
   it is a sentence, and a right-ragged sentence is harder to read than a
   left-ragged one. */
.mtd-basis { font-size: 11px; text-align: left; margin-top: 2px;
             max-width: 34ch; margin-left: auto; line-height: 1.45; }
.mtd-table td:first-child .dim { max-width: none; }
.mtd-total td { border-top: 1px solid var(--border); }
.mtd-note { font-size: 11.5px; margin-top: 10px; max-width: 92ch; }

/* ------------------------------------------------------- one person's record
   Shown in the drawer over the register and on /staff/{id}. It was an
   accordion row inside the table — a <tr> with colspan=10 that pushed every
   other row down the page, so you could look at a person or at the list,
   never at both. Regions told apart by a hairline, ch.5. */
.staff-doc { display: grid; gap: 4px; }
.staff-head { display: flex; align-items: baseline; justify-content: space-between;
              gap: 12px; flex-wrap: wrap; }
.staff-head h3 { margin: 0; font-size: 15px; font-weight: 500; }
.staff-head-right { display: flex; align-items: baseline; gap: 10px; }
.staff-h { font-size: 11px; font-weight: 400; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted); margin: 16px 0 6px;
  padding-top: 8px; border-top: 1px solid var(--border-soft); }
.staff-h .dim { letter-spacing: 0; text-transform: none; font-size: 11px; }
.staff-grid { display: grid; gap: 8px;
              grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.staff-grid label, .staff-note { display: grid; gap: 3px; font-size: 11.5px;
                                 color: var(--muted); }
.staff-grid input, .staff-grid select, .staff-note input, .staff-note textarea {
  width: 100%;
}
.staff-note textarea { resize: vertical; min-height: 58px; }
.staff-note { margin-top: 8px; }
.contract-readiness { margin-top: 12px; }
.contract-readiness > p { margin: 0; }
.contract-actions { display: flex; align-items: center; gap: 8px;
                    flex-wrap: wrap; margin-top: 10px; }
.contract-actions form { margin: 0; }
.contract-history { grid-template-columns: 1fr; margin-top: 10px; }
.contract-history li > a.btn-link { flex: 0; min-width: 0; }
.signed-contract-upload { display: flex; gap: 6px; align-items: center;
                          flex-wrap: wrap; }
.signed-contract-upload input { max-width: 230px; font-size: 11px; }
/* ch.3: only trouble is coloured. A statutory number missing from somebody
   still on the books is the one thing here that stops a payslip being right. */
.staff-grid input.missing { border-color: var(--accent-gold-line); }
.staff-calc { margin-top: 10px; }
.staff-calc td { padding: 3px 0; }
.staff-slips { width: 100%; }
.staff-slip-do { white-space: nowrap; }
.staff-slip-do .btn-link { margin-left: 10px; font-size: 11.5px; }
.staff-slip-do .btn-link:first-child { margin-left: 0; }
.staff-slip-do .btn-link[disabled] { color: var(--muted); cursor: not-allowed;
                                     text-decoration: none; }
.staff-foot { font-size: 11px; margin: 14px 0 0; padding-top: 8px;
              border-top: 1px solid var(--border-soft); }
.staff-foot + .staff-foot { border-top: 0; padding-top: 0; margin-top: 4px; }
@media (max-width: 560px) {
  .staff-grid { grid-template-columns: 1fr; }
  .staff-slip-do { white-space: normal; }
}

/* ---------------------------------------------------------------- dialogs
   The app asking a question it has to stop for. This replaces the browser's
   own confirm/alert/prompt, which arrived captioned "finance.imvelobrands.com
   says", rounded, shadowed and Chrome-blue — every one of those a thing this
   book does not do.

   ch.5: no boxes, no cards, no shadows. The sheet is told from the page by a
   single rule and by the wash behind it, not by a raised edge. ch.9: actions
   are typeset words above a hairline, and the one filled button is the
   irreversible act — which in a confirmation is the whole question. */
.imv-dialog {
  border: 1px solid var(--ink); border-radius: 0; padding: 0;
  background: var(--plane); color: var(--ink);
  width: min(440px, calc(100vw - 32px)); max-height: calc(100vh - 64px);
  box-shadow: none;
}
.imv-dialog::backdrop {
  /* The page is still there and still readable — dimmed, not hidden, because
     several of these questions quote a figure sitting behind them. */
  background: rgba(20, 31, 56, .55);
  animation: imv-dialog-wash .18s var(--ease);
}
:root[data-theme="dark"] .imv-dialog::backdrop { background: rgba(6, 10, 20, .62); }
.imv-dialog[open] { animation: imv-dialog-in .18s var(--ease); }
@keyframes imv-dialog-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
@keyframes imv-dialog-wash { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .imv-dialog[open], .imv-dialog::backdrop { animation: none; }
}
.imv-dialog-form { padding: 20px 22px 16px; display: grid; gap: 10px; }
/* 500 is the heaviest weight anywhere in this app, ch.4. */
.imv-dialog-head { font-size: 15px; font-weight: 500; line-height: 1.35;
                   margin: 0; color: var(--ink); }
.imv-dialog-say { margin: 0; font-size: 12.5px; line-height: 1.5;
                  color: var(--ink-2); max-width: 52ch; }
.imv-dialog-field { display: grid; gap: 4px; margin-top: 2px; }
.imv-dialog-label { font-size: 11.5px; color: var(--muted); }
.imv-dialog-label:empty { display: none; }
.imv-dialog-hint { font-size: 11px; }
.imv-dialog-input { width: 100%; }
/* The hairline the actions sit above. */
.imv-dialog-acts { display: flex; gap: 14px; align-items: center;
  justify-content: flex-end; border-top: 1px solid var(--border);
  margin-top: 6px; padding-top: 12px; }
.imv-dialog-no { font-size: 12.5px; }
/* ch.3: only trouble is coloured. A question about something that cannot be
   undone is the one dialog that earns the coral, and it earns it on the act
   itself — one filled button, not a decorated panel. */
.imv-dialog.is-danger .imv-dialog-yes {
  background: var(--accent-coral-line); border-color: var(--accent-coral-line);
  color: #EFE8D6; }
.imv-dialog.is-trouble .imv-dialog-head { color: var(--accent-coral); }
@media (max-width: 560px) {
  .imv-dialog { width: calc(100vw - 20px); }
  .imv-dialog-form { padding: 18px 16px 14px; }
  .imv-dialog-acts { gap: 18px; }
  .imv-dialog-acts .btn, .imv-dialog-no { min-height: 38px; }
}

/* A drawer, not a modal. What a price list is for is comparing one account
   against the next, and a sheet in the middle of the screen with a dimmed
   backdrop behind it makes that a sequence of open, read, close, open. Down
   the right instead: the list stays lit, stays clickable, and the drawer
   changes under the next name. One hairline down its left edge and the same
   paper as the page — it is a part of the page, not a thing on top of it. */
dialog.sheet {
  position: fixed; inset: 0 0 0 auto; margin: 0;
  width: min(600px, 96vw); max-width: 96vw; height: 100dvh; max-height: 100dvh;
  padding: 0; background: var(--plane); color: var(--ink);
  border: 0; border-left: 1px solid var(--border); border-radius: 0;
  overflow: auto; overscroll-behavior: contain;
  transform: translateX(100%); transition: transform .22s var(--ease);
}
dialog.sheet[open] { transform: translateX(0); }
dialog.sheet::backdrop { background: none; }
@media (prefers-reduced-motion: reduce) {
  dialog.sheet { transition: none; }
}
/* The page keeps its full width and simply ends sooner, so nothing under the
   drawer is hidden by it — the row you want next is still there to click. */
@media (min-width: 900px) {
  body.drawer-open .main { padding-right: min(600px, 96vw); }
}
.main { transition: padding-right .22s var(--ease); }
/* Which account the drawer is showing, marked on the row it came from. */
tr[aria-current="true"] > td:first-child {
  box-shadow: inset 2px 0 0 var(--accent-gold);
}
.sheet-head {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 18px 22px 12px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--plane); z-index: 1;
}
.sheet-head h2 {
  margin: 0; font-size: 18px; font-weight: 300; letter-spacing: -.01em;
  color: var(--brand); flex: 1; min-width: 0;
}
.sheet-head form { margin: 0; }
dialog.sheet > [data-popup-body] { padding: 4px 22px 22px; }
dialog.sheet .card { border: 0; padding: 0; }
/* the popup opens straight onto the numbers, so the table needs no second head */
dialog.sheet > [data-popup-body] > .scroll-x:first-child { margin-top: 10px; }
@media (max-width: 620px) {
  dialog.sheet { width: 100vw; max-width: 100vw; }
  .sheet-head { padding: 14px 14px 10px; }
  dialog.sheet > [data-popup-body] { padding: 4px 14px 18px; }
}

/* An amended line is the exception on the page, so it carries the one gold
   rule; every other line is on the official price and says so quietly. */
tr[data-price-row].is-agreed > td:first-child {
  box-shadow: inset 2px 0 0 var(--accent-gold-line);
}
.agreed-stamp { font-size: 10.5px; margin-top: 3px; }

/* --- Deciding a merge ----------------------------------------------------
   One recommendation and one field, on a line. The alternatives are a quieter
   line under it, because a pair of equally weighted buttons is a question the
   page could have answered itself: the busier account is the one to keep. */
.dup-plan {
  margin: 14px 0 12px; font-size: 13px; color: var(--ink-2);
  padding-left: 12px; border-left: 1px solid var(--border);
  max-width: 88ch;
}
.dup-plan.armed { border-left-color: var(--accent-gold-line); color: var(--ink); }
.dup-plan.bad { border-left-color: var(--critical); color: var(--critical); }
.dup-decide {
  display: flex; gap: 16px; align-items: flex-end; flex-wrap: wrap;
  margin-bottom: 10px;
}
.dup-decide .dup-field { flex: 1 1 320px; }
.dup-alts {
  display: flex; gap: 16px; align-items: baseline; flex-wrap: wrap;
  font-size: 12.5px;
}
/* Which name survives: one question with its answers under it, rather than
   two buttons pointing opposite ways and a rename hidden in a third place. */
.dup-choice { border: 0; padding: 0; margin: 0 0 12px; }
.dup-choice legend { font-size: 11px; color: var(--muted); padding: 0; margin-bottom: 6px; }
.dup-choice label {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  padding: 7px 2px; border-bottom: 1px solid var(--border-soft); cursor: pointer;
}
.dup-choice label:last-child { border-bottom: 0; }
.dup-choice label:hover { background: var(--wash-1); }
.dup-choice input[type=radio] { flex: none; margin: 0; }
.dup-choice-name { font-size: 13.5px; color: var(--ink); }
.dup-choice .dim { font-size: 11px; }
/* It is a company name; give it the room a name needs. It used to have no
   width rule at all, so it rendered at the browser's default twenty-odd
   characters and cut the name in half as you typed it. */
.dup-rename {
  font: inherit; font-size: 13px; flex: 1 1 24ch; min-width: 0; max-width: 42ch;
  padding: 3px 2px; border-radius: 0; border: 0;
  border-bottom: 1px solid var(--border); background: none; color: var(--ink);
}
.dup-rename:focus { outline: none; border-bottom-color: var(--accent-gold-line); }
.dup-why { margin-bottom: 12px; }
.dup-go { display: flex; gap: 16px; align-items: baseline; flex-wrap: wrap; }

/* a price that wraps between the K and the figure stops being one number */
table.price-book td.px, table.price-book th.num { white-space: nowrap; }
table.price-book .agreed-note-in { width: 100%; min-width: 12ch; }
.price-log { margin-top: 16px; border-top: 1px solid var(--border-soft); }
.price-log > summary {
  cursor: pointer; padding: 9px 2px; font-size: 12.5px; color: var(--ink-2);
}
.price-log > summary:hover { color: var(--ink); }
.price-log table { margin-top: 4px; }

/* --- A sortable column --------------------------------------------------- */
/* The header stays a header: no button chrome, no box. What changes is that it
   is live — the label lifts to full ink on hover, and the column in force
   carries the one gold rule under it and an arrow saying which way. */
.sort-th { padding: 0; }
.sort-th > a {
  display: flex; align-items: baseline; gap: 5px;
  padding: 7px 10px 6px; margin: 0 -10px;
  color: inherit; text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color .14s var(--ease), border-color .14s var(--ease);
}
.sort-th.num > a { justify-content: flex-end; }
.sort-th > a:hover { color: var(--ink); border-bottom-color: var(--border); }
.sort-th.on > a { color: var(--ink); border-bottom-color: var(--accent-gold-line); }
.sort-mark {
  font-size: 9px; line-height: 1; opacity: 0;
  color: var(--accent-gold); transition: opacity .14s var(--ease);
}
.sort-th.on .sort-mark { opacity: 1; }
/* the affordance only shows up when a pointer is over the row of headers, so a
   table nobody is touching still reads as type rather than as controls */
.sort-th > a::after {
  content: "\2195"; font-size: 9px; line-height: 1; margin-left: -1px;
  opacity: 0; color: var(--muted); transition: opacity .14s var(--ease);
}
tr:hover > .sort-th > a::after { opacity: .55; }
.sort-th.on > a::after { content: none; }

/* --- Customers ---------------------------------------------------------- */
/* How sure we are that two accounts are one customer. Certain reads calm, not
   alarming: it is the easy case. Possible is the one that needs a human, so it
   is the one that looks like a question. */
.conf {
  font-size: 10.5px; font-weight: 500; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 8px; border-radius: 0; border: 1px solid var(--border);
  color: var(--ink-2); white-space: nowrap;
}
.conf-certain { background: var(--good); color: #fff; border-color: transparent; }
.conf-likely { background: var(--warning); color: #402c00; border-color: transparent; }
.conf-possible { background: none; color: var(--muted); }

.dup { border-top: 1px solid var(--grid); padding: 8px 0; }
.dup:first-of-type { border-top: 0; }
.dup > summary {
  cursor: pointer; display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; font-size: 13px;
}
.dup > summary::-webkit-details-marker { display: none; }
.dup-why { color: var(--muted); font-size: 12px; flex: 1 1 100%; }
.dup-body { padding: 8px 0 4px; }

/* --- Deciding whether two accounts are one customer ----------------------- */
/* The pair, field by field, so the eye can run down two columns instead of
   holding one account in memory while it opens the other. A row where the two
   sides disagree is marked with the gold rule — on a lookalike pair, the
   fields that DIFFER are the evidence. */
.dup-compare { margin: 4px 0 12px; }
.dup-table { table-layout: fixed; }
.dup-table th:first-child, .dup-table .dup-lbl { width: 130px; }
.dup-table th { font-weight: 500; color: var(--ink); }
.dup-lbl {
  font-family: "Space Mono", ui-monospace, monospace; font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.dup-table tr.dup-differs > td { box-shadow: inset 0 -1px 0 var(--accent-gold-line); }
.dup-table tr.dup-differs .dup-lbl { color: var(--accent-gold); }

/* The decision reads down, not across: what it would do, then the one field
   and the one button, then the quieter ways out. A row of equally weighted
   buttons was a question this page could answer itself. */
.dup-act { display: block; }
.dup-field { display: flex; flex-direction: column; gap: 3px; flex: 1 1 240px; min-width: 0; }
.dup-field > span { font-size: 11px; color: var(--muted); }
.dup-field em { font-style: normal; opacity: .7; }
.dup-field input {
  font: inherit; font-size: 13px; padding: 5px 2px; border-radius: 0;
  border: 0; border-bottom: 1px solid var(--border);
  background: none; color: var(--ink);
}
.dup-field input:focus { outline: none; border-bottom-color: var(--accent-gold-line); }
/* An armed button is the one the next click will act on — the book's one
   filled button, earned by being the irreversible act about to happen. */
.dup-alts .btn.armed, .dup-decide .btn.armed {
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bg); padding: 6px 15px;
}

/* The account's own details: label above field, wrapping into as many columns
   as the screen allows. */
.cust-grid {
  display: grid; gap: 12px 20px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.cust-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cust-field input, .cust-field select, .cust-field textarea { width: 100%; }

/* A line on this customer's own price book is the one they will normally be
   sold, so it carries the weight; the other books stay legible but quiet. */
tr[data-price-row].own-book td:first-child { font-weight: 500; }
tr[data-price-row]:not(.own-book) td:first-child { color: var(--ink-2); }
.agreed-in { width: 92px; text-align: right; }

textarea { font-family: inherit; }

/* ---- The café menu, edited where it is priced --------------------------- */
/* One row is one dish: its name, the words that print under it, and a price
   per column. Sections fold, because thirteen of them open at once is a wall
   rather than a page. */
.menu-sec { border-top: 1px solid var(--border); padding: 4px 0; }
.menu-sec > summary { cursor: pointer; padding: 7px 2px; font-size: 13.5px; }
.menu-sec-num {
  font-family: "Space Mono", ui-monospace, monospace; font-size: 10.5px;
  color: var(--muted); margin-right: 8px;
}
.menu-group { padding: 4px 0 12px; }
.menu-group h4 {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin: 10px 0 4px;
}
table.menu-edit td { vertical-align: top; }
/* eighty-six boxed fields read as a spreadsheet; the same fields underlined
   read as the menu they are. The rule darkens on focus so the field you are
   in is still obvious. */
table.menu-edit .menu-name, table.menu-edit .menu-desc, .menu-meta-row input {
  background: none; border: 0; border-bottom: 1px solid var(--border-soft);
  border-radius: 0; padding: 3px 0;
}
table.menu-edit .menu-name:focus, table.menu-edit .menu-desc:focus,
.menu-meta-row input:focus { outline: none; border-bottom-color: var(--accent-gold-line); }
table.menu-edit .menu-name { width: 100%; font-weight: 500; font-size: 13px; }
table.menu-edit .menu-desc {
  width: 100%; margin-top: 2px; font-size: 12px; line-height: 1.5;
  resize: vertical; min-height: 38px;
}
.menu-desc-read { font-size: 12px; max-width: 62ch; margin-top: 2px; }
.menu-meta-row { display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap; }
.menu-meta-row input { flex: 1 1 120px; font-size: 11.5px; }
.menu-actions { white-space: nowrap; }
.menu-actions .btn { padding: 2px 6px; }
.menu-special { min-width: 150px; }
.menu-special-toggle, .menu-special-date, .menu-add-special, .menu-add-until {
  display: flex; align-items: center; gap: 6px; font-size: 11.5px;
}
.menu-special-toggle input, .menu-add-special input { margin: 0; }
.menu-special-date { margin-top: 6px; color: var(--muted); }
.menu-special-date input, .menu-add-until input {
  width: 118px; min-height: 28px; padding: 3px 5px; font-size: 11px;
}
.menu-special-since { display: block; margin-top: 4px; font-size: 10.5px; }
.menu-add-special { margin-left: 4px; }
.menu-add-until span { color: var(--muted); }
/* ---- The grip -------------------------------------------------------------
   Reordering by dragging the thing itself, the way a phone does it. Edition 02
   forbids boxes and shadows, so a row being carried is not raised by a drop
   shadow: it takes the gold rule, the ground lifts one step, and everything
   else slides. The affordance is the three bars, and they only darken when
   the row is under the pointer — a menu nobody is editing stays type. */
.menu-grip-h { width: 34px; }
.menu-grip-cell { width: 34px; padding: 0 !important; vertical-align: middle; }
.menu-grip {
  display: block; width: 34px; height: 34px; margin-left: auto;
  background: none; border: 0; padding: 0; border-radius: 0;
  cursor: grab; touch-action: none; opacity: .55;
  transition: opacity .16s var(--ease);
}
tr[data-dish]:hover .menu-grip { opacity: .9; }
.menu-grip:hover, .menu-grip:focus-visible { opacity: 1; }
.menu-grip:focus-visible { outline: 1px solid var(--accent-gold-line); outline-offset: -3px; }
.menu-grip:active { cursor: grabbing; }
.grip-bars {
  display: block; width: 15px; height: 9px; margin: 0 auto;
  border-top: 1px solid currentColor; border-bottom: 1px solid currentColor;
  position: relative; color: var(--ink);
}
.grip-bars::after {
  content: ""; position: absolute; left: 0; right: 0; top: 4px;
  border-top: 1px solid currentColor;
}

/* The carried row moves in the document rather than floating over the others,
   so what marks it is a change of ground and the gold rule down its edge — no
   transform, which WebKit would not draw on a table row anyway. */
table.menu-edit.reordering tr[data-dish].dragging > td {
  background: color-mix(in srgb, var(--ink) 6%, var(--plane));
}
table.menu-edit.reordering tr[data-dish].dragging > td:first-child {
  box-shadow: inset 2px 0 0 var(--accent-gold-line);
}
table.menu-edit.reordering .menu-grip { opacity: 1; }
table.menu-edit.reordering { cursor: grabbing; user-select: none; }
/* The source row keeps its place in the real table while a fixed clone follows
   the pointer. Moving that invisible source row makes the surrounding rows
   reflow immediately; the clone makes the carried dish visible throughout. */
table.menu-edit.reordering tr[data-dish].dragging { visibility: hidden; }
.menu-drag-ghost {
  position: fixed; z-index: 1000; pointer-events: none; margin: 0;
  border-collapse: collapse; table-layout: fixed;
  background: var(--plane); color: var(--ink);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--ink) 20%, transparent);
  opacity: .96;
}
.menu-drag-ghost td {
  background: color-mix(in srgb, var(--ink) 6%, var(--plane));
  border-bottom: 1px solid var(--border); vertical-align: top;
}
.menu-drag-ghost td:first-child { box-shadow: inset 2px 0 0 var(--accent-gold-line); }
/* the row that just landed says so for a moment, because a list that
   rearranges silently leaves you checking whether it worked */
tr[data-dish].just-moved > td { animation: dish-landed .7s var(--ease); }
@keyframes dish-landed {
  from { background: color-mix(in srgb, var(--accent-gold-line) 22%, transparent); }
  to   { background: transparent; }
}
/* A drop whose save is still in flight keeps the gold rule: the row is where
   you put it, and the claim is open until the server agrees. */
tr[data-dish].is-pending > td:first-child { box-shadow: inset 1px 0 0 var(--accent-gold-line); }
tr[data-dish].is-pending .menu-name { opacity: .75; }
/* A silent snap-back reads as "my drag never registered", so a refused move
   says so on the row it happened to. */
@keyframes dish-reverted {
  from { background: color-mix(in srgb, var(--warning) 20%, transparent); }
  to   { background: transparent; }
}
tr[data-dish].just-reverted > td { animation: dish-reverted .7s var(--ease); }
/* No second drag while the first is still being written. */
table.menu-edit.saving-order .menu-grip {
  opacity: .35; cursor: progress; pointer-events: none;
}
/* the row hover lights every row the carried one passes over, which flickers
   against its opaque cells */
table.menu-edit.reordering tr:hover > td { background: none; }
@media (prefers-reduced-motion: reduce) {
  table.menu-edit.reordering tr[data-dish] { transition: none; }
  tr[data-dish].just-moved > td,
  tr[data-dish].just-reverted > td { animation: none; }
}

/* A dish hidden for today is still here, just visibly not printing. Keep its
   availability control at full strength so "Show now" never looks disabled. */
tr.menu-off > td:not(.menu-availability) { opacity: .5; }
.menu-availability { min-width: 126px; white-space: nowrap; }
.menu-availability .status { display: block; width: fit-content; margin: 0 0 5px; }
.menu-add {
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--border-soft);
}
.menu-add input[name="name"] { flex: 0 1 200px; }
.menu-add input[name="description"] { flex: 1 1 260px; }

/* ---------------------------------------------------------------- the customer field
   The list the browser drew for a <datalist> was its own: a dark box in a
   bone-coloured page, showing nothing but the name — and indistinguishable
   from the browser's saved-form suggestions, so you could not tell the
   customer list from the last five things typed into the box. This one is the
   page's paper, one hairline, and says what each account is. */
.pick-wrap { position: relative; display: block; }
.pick-list {
  position: absolute; z-index: 40; top: calc(100% + 2px); left: 0;
  min-width: 100%; width: max-content; max-width: min(460px, 88vw);
  max-height: 300px; overflow: auto; overscroll-behavior: contain;
  background: var(--plane); border: 1px solid var(--border);
}
.pick-opt {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 7px 11px; background: none; border: 0;
  border-bottom: 1px solid var(--border);
  font: inherit; color: var(--ink);
}
.pick-opt:last-child { border-bottom: 0; }
.pick-opt.on, .pick-opt:hover { background: var(--wash-1); }
.pick-opt.on { box-shadow: inset 2px 0 0 var(--accent-gold); }
.pick-name { display: block; font-size: 13px; }
.pick-meta {
  display: block; font-family: "Space Mono", ui-monospace, monospace;
  font-size: 10.5px; color: var(--muted); margin-top: 1px;
}
.pick-none { margin: 0; padding: 9px 11px; font-size: 12px; color: var(--muted); }

/* --- What you can do to a row -----------------------------------------------
   The same object as the customer picker and the select's own list: a short
   list of things to choose from, on the page's paper, one hairline between
   them. Positioned in the viewport because it is opened at a pointer.

   An action that cannot run is greyed with its reason underneath rather than
   hidden — hiding Void on every paid invoice is what made a guard doing its
   job look like a missing feature. */
.rowmenu {
  position: fixed; z-index: 80; min-width: 230px; max-width: min(340px, 92vw);
  background: var(--plane); border: 1px solid var(--border); border-radius: 0;
  padding: 0; box-shadow: none;
}
.rowmenu-item {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--ink);
  background: none; border: 0; border-bottom: 1px solid var(--border);
  padding: 8px 12px;
}
.rowmenu-item:last-child { border-bottom: 0; }
.rowmenu-item:hover:not([disabled]),
.rowmenu-item:focus-visible:not([disabled]) { background: var(--wash-1); }
.rowmenu-item:focus-visible { outline-offset: -2px; }
.rowmenu-item[disabled] { color: var(--muted); cursor: default; }
/* ch.4 "specification": the reason is a caption on the act, so it is the mono
   face at caption size, and it wraps rather than being cut off — the whole
   point of it is that it can be read. */
.rowmenu-why {
  display: block; margin-top: 3px;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: 10px; line-height: 1.35; color: var(--muted);
  white-space: normal;
}
.rowmenu-sep { height: 0; border-top: 1px solid var(--border); }
/* The row's own control: quiet until the row is under the pointer, and always
   there on a phone, where hover does not exist and 38px is the floor. */
.rowmenu-open { opacity: 0; float: right; letter-spacing: .06em; }
tr:hover .rowmenu-open, .rowmenu-open:focus-visible { opacity: 1; }
@media (hover: none), (max-width: 820px) {
  .rowmenu-open { opacity: 1; min-height: 38px; min-width: 38px; }
}

/* The basis a VAT-free invoice rests on, under the totals where the customer
   reads them. Coral would say something is wrong; nothing is — this is a
   statement of fact about the supply, so it is quiet. */
.inv-vat-basis {
  margin: 6px 0 0; text-align: right; font-size: 11.5px; color: var(--ink-2);
  border-top: 1px solid var(--border-soft); padding-top: 6px;
}

/* ---- Sparklines (app/spark.py) -------------------------------------------
   Rendered on the server as inline SVG so a row has its shape before any
   script runs, and keeps it in a print-out and with scripting off. One rule
   weight, no axes, no frame (ch. 5); ink unless the thing it describes is
   worth a second look (ch. 3). */
.spark {
  display: inline-block;
  vertical-align: middle;
  overflow: visible;
}
.spark-none { color: var(--muted); }
.spark-row { margin: 4px 0 16px; }
.spark-row .spark { max-width: 100%; }
/* Seven bars over seven columns: the shape has to sit above the numbers it
   describes, or it is a second chart about something else. */
.spark-row .spark-bars { width: 100%; }

/* A row of small links that switch what a page is showing. Typeset words above
   a hairline (ch. 9), not a control. */
.subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: baseline;
  margin: 0 0 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--rule, var(--grid));
  font-size: 13px;
}
.subnav a { color: var(--muted); text-decoration: none; }
.subnav a:hover { color: var(--ink); }
.subnav a.on { color: var(--ink); border-bottom: 1px solid var(--ink); padding-bottom: 2px; }

/* Where somebody types the price, or how long a thing keeps. */
.cogs-set input { min-width: 8ch; }
.cogs-set .cogs-note { min-width: 22ch; flex: 1 1 22ch; }
input.stock-rule {
  width: 8ch;
  text-align: right;
}
select.stock-rule { min-width: 18ch; }
/* One order list per supplier means one table per supplier, and columns that
   land in a different place in each is a list that reads as several lists. */
.order-list { table-layout: fixed; }
.order-list th:first-child, .order-list td:first-child { width: 24%; }
.order-list th:nth-child(2), .order-list td:nth-child(2),
.order-list th:nth-child(3), .order-list td:nth-child(3),
.order-list th:nth-child(4), .order-list td:nth-child(4) { width: 11%; }
.order-list th:nth-child(5), .order-list td:nth-child(5) { width: 14%; }

/* ---- The method a cook follows -------------------------------------------
   Numbered because the order matters and a cook reads by number: "I'm on
   four". Typeset, not boxed (ch. 5). */
.recipe-method {
  margin: 6px 0 14px;
  padding-left: 1.6em;
  line-height: 1.55;
}
.recipe-method li { margin-bottom: 6px; }
.method-edit { margin: 4px 0 16px; }
.method-edit > summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.method-edit > summary:hover { color: var(--ink); }
.method-edit .field { display: block; margin: 10px 0; }
.method-edit textarea,
.method-edit input { width: 100%; }
.method-edit textarea { font: inherit; line-height: 1.5; resize: vertical; }

/* ---- Weekly rota and attendance ----------------------------------------- */
.schedule-head { align-items: flex-end; }
.schedule-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.week-nav, .card-head-row, .attendance-counts {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.card-head-row { justify-content: space-between; margin-bottom: 12px; }
.card-head-row h2 { margin-bottom: 2px; }
.card-head-row p { margin: 0; font-size: 12px; }
.rota-scroll { padding-bottom: 4px; }
.rota-table { border-collapse: collapse; min-width: 1180px; width: 100%; }
.rota-table th, .rota-table td { border: 1px solid var(--grid); vertical-align: top; }
.rota-table thead th { padding: 7px; text-align: left; background: var(--wash-1); }
.rota-table thead th span, .rota-table tbody th span { display: block; color: var(--muted); font-size: 10px; font-weight: 400; }
.rota-table tbody th { width: 150px; min-width: 150px; padding: 9px; text-align: left; }
.shift-cell { width: 145px; min-width: 145px; padding: 5px; }
.shift-cell select, .shift-cell input { width: 100%; min-width: 0; font-size: 11px; padding: 4px 5px; }
.shift-fields { margin-top: 5px; }
.shift-times { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 3px; }
.shift-times input { padding-inline: 2px; }
.shift-location, .shift-note { margin-top: 4px; }
.is-hidden { display: none !important; }
.attendance-board { margin-top: 14px; }
.schedule-settings { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(300px, .65fr); gap: 14px; margin-top: 14px; }
.location-form { border-top: 1px solid var(--grid); padding: 12px 0 4px; }
.location-form:first-of-type { border-top: 0; }
.location-form label { flex: 1 1 130px; font-size: 11px; color: var(--muted); }
.location-form label input, .location-form label select { display: block; width: 100%; margin-top: 3px; }
.location-coords .capture-location { align-self: flex-end; }
.device-list > div { display: flex; justify-content: space-between; align-items: center; gap: 8px; border-top: 1px solid var(--grid); padding: 7px 0; }
.device-list span { min-width: 0; }
.device-list strong, .device-list small { display: block; }
.device-list small { color: var(--muted); font-size: 10px; }
.device-actions { display: flex; gap: 6px; flex: none; }

/* The employee portal is deliberately one-handed: the action is the largest
   object on the page and the weekly detail stays secondary. */
.portal-tabs { display: flex; gap: 18px; border-bottom: 1px solid var(--grid); margin: 4px 0 18px; }
.portal-tabs a { padding: 8px 0; color: var(--muted); text-decoration: none; font-size: 12px; }
.portal-tabs a.on { color: var(--ink); border-bottom: 2px solid var(--ink); }
.punch-card { text-align: center; padding: 22px; }
.phone-card p { margin: 0 0 10px; font-size: 12px; }
.phone-approved { border-top-color: var(--good); }
.punch-state strong, .punch-state small { display: block; }
.punch-state small { color: var(--muted); margin-top: 3px; }
.punch-dot { display: inline-block; width: 8px; height: 8px; background: var(--good); border-radius: 50%; margin-right: 6px; }
.today-shift { margin: 20px 0; }
.today-shift span, .today-shift strong, .today-shift small { display: block; }
.today-shift span { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .1em; }
.today-shift strong { font-family: var(--serif); font-size: 30px; font-weight: 500; margin: 3px 0; }
.today-shift small { color: var(--muted); }
.punch-button { width: min(100%, 390px); min-height: 72px; border: 0; background: var(--brand); color: var(--plane); font: 600 20px var(--sans); cursor: pointer; }
.punch-button.out { background: var(--accent-coral); color: var(--brand); }
.punch-button:disabled { opacity: .55; cursor: wait; }
.punch-message { min-height: 22px; margin-top: 10px; font-size: 13px; }
.punch-message.ok { color: var(--good-text); }
.punch-message.bad { color: var(--serious); }
.location-privacy { margin: 16px auto 0; max-width: 54ch; color: var(--muted); font-size: 11px; }
.my-week { display: grid; grid-template-columns: repeat(7, 1fr); border: 1px solid var(--grid); }
.my-week > div { padding: 9px 7px; border-left: 1px solid var(--grid); min-width: 0; }
.my-week > div:first-child { border-left: 0; }
.my-week span, .my-week strong, .my-week small { display: block; }
.my-week span { font-size: 11px; color: var(--muted); }
.my-week span small { display: inline; margin-left: 3px; }
.my-week strong { font-size: 11px; margin-top: 5px; }
.my-week > div > small { font-size: 9px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.my-week .today { box-shadow: inset 0 2px 0 var(--accent-gold-line); background: var(--wash-1); }
.my-week .day-off { opacity: .55; }

@media (max-width: 900px) {
  .schedule-settings { grid-template-columns: 1fr; }
  .my-week { grid-template-columns: repeat(4, 1fr); }
  .my-week > div:nth-child(5) { border-left: 0; border-top: 1px solid var(--grid); }
  .my-week > div:nth-child(n+6) { border-top: 1px solid var(--grid); }
}
@media (max-width: 560px) {
  .week-nav { width: 100%; }
  .week-nav .btn { flex: 1; padding-inline: 5px; text-align: center; }
  .device-list > div { align-items: flex-start; flex-direction: column; }
  .device-actions { width: 100%; }
  .device-actions .btn { flex: 1; }
  .portal-tabs { gap: 12px; }
  .punch-card { padding: 18px 13px; }
  .my-week { grid-template-columns: repeat(2, 1fr); }
  .my-week > div { border-top: 1px solid var(--grid); }
  .my-week > div:nth-child(-n+2) { border-top: 0; }
  .my-week > div:nth-child(odd) { border-left: 0; }
}

/* --------------------------------------------------------------------------
   Compact app contract — the final responsive layer.

   Earlier rules grew with individual pages. This section is deliberately last
   so every dashboard route shares the same phone/landscape/touch-tablet shell,
   control sizes and record language. It is screen-only: document printing and
   the public menu keep their own contracts.
   -------------------------------------------------------------------------- */
.mobile-dock, .mobile-only { display: none; }
html:not(.js) .nav-toggle { display: none; }

.action-bar {
  display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap;
  margin: 0 0 14px;
}
.action-bar .btn { margin: 0; }
.tab-strip {
  display: flex; align-items: stretch; gap: 20px;
  border-bottom: 1px solid var(--border); overflow-x: auto;
  overscroll-behavior-inline: contain; scrollbar-width: none;
}
.tab-strip::-webkit-scrollbar { display: none; }
.tab-strip a {
  flex: 0 0 auto; min-height: 42px; display: inline-flex; align-items: center;
  color: var(--muted); text-decoration: none; white-space: nowrap;
  border-bottom: 1px solid transparent;
}
.tab-strip a.on, .tab-strip a[aria-current="page"] {
  color: var(--ink); border-bottom-color: var(--accent-gold-line);
}
.filter-toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 0 12px; border-bottom: 1px solid var(--border-soft);
}
.list-filter-form:not(.audit-filter-form) {
  width: 100%; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 18px;
}
.page-head > .list-filter-form { flex: 1 1 560px; margin: 4px 0 0 auto; }
.list-filter-search {
  display: grid; gap: 5px; flex: 2 1 250px; min-width: min(250px, 100%);
}
.list-filter-search > span { color: var(--muted); font-size: 11px; font-weight: 550; }
.list-filter-search input {
  width: 100%; min-width: 0; min-height: 44px; padding: 8px 11px;
  border: 1px solid var(--border); border-radius: 0;
  background: var(--surface); color: var(--ink); font: inherit; font-size: 14px;
}
.list-filter-search input:focus { outline: none; border-color: var(--accent-gold-line); }
.list-filter-form > select { min-height: 44px; flex: 1 1 145px; min-width: 0; }
.list-filter-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.list-filter-actions .btn { margin: 0; min-height: 44px; }
.list-filter-status { margin-left: auto; font-size: 11.5px; white-space: nowrap; }
.list-filter-status:empty { display: none; }
.list-filter-clear:disabled { display: none; }
.audit-filter-form.list-filter-form { display: grid; }
.audit-filter-form .list-filter-actions {
  display: flex; align-items: center; align-self: end; white-space: nowrap;
}
.audit-filter-form .list-filter-status { grid-column: 1 / -1; margin: 0; }
.filters > .list-filter-form,
.invoice-list-tools > .list-filter-form {
  width: auto; flex: 1 1 320px; margin: 0; padding: 0; border-bottom: 0;
}
.invoice-result-summary { margin: 0; color: var(--muted); font-size: 12px; }
.invoice-search { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.invoice-search input { min-width: min(280px, 40vw); }

@media screen and (max-width: 960px),
       screen and (hover: none) and (pointer: coarse) and (max-width: 1180px) {
  html { min-height: 100%; }
  body { min-height: 100%; font-size: 15px; }
  .shell { min-height: 100dvh; flex-direction: column; }

  /* Off-canvas only when JavaScript is available. With scripts blocked the
     complete navigation remains in normal flow and every destination works. */
  .sidebar, .sidebar.rail {
    --rail: 100vw; width: var(--rail);
    background: var(--plane); border-right: 0;
  }
  html.js .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 70;
    height: 100dvh; margin: 0; overflow: hidden;
    transform: translateX(-102%); transition: transform .18s var(--ease);
  }
  html.js .sidebar.open { transform: translateX(0); }
  html:not(.js) .sidebar, html:not(.js) .sidebar.rail {
    position: relative; inset: auto; transform: none; width: 100%; height: auto;
    max-height: none; border-right: 0; border-bottom: 1px solid var(--border);
  }
  html:not(.js) .sidebar nav { overflow: visible; max-height: none; }
  html:not(.js) .sidebar-foot { display: none; }

  .sidebar.rail .brand-lockup, .sidebar.rail .brand-copy,
  .sidebar.rail .nav-label, .sidebar.rail .profile-tab-copy,
  .sidebar.rail .btn-label { display: revert; }
  .sidebar.rail .brand { flex-direction: row; gap: 10px; justify-content: flex-start; }
  .sidebar.rail .brand-spacer { display: block; }
  .sidebar.rail .nav-toggle { position: static; transform: none; opacity: 1; }
  .sidebar.rail nav a { justify-content: flex-start; }
  .sidebar.rail nav a .badge { position: static; width: auto; height: auto;
    min-width: 22px; padding: 2px 7px; font-size: 11px; line-height: 1.4; }

  .sidebar .brand {
    min-height: 68px;
    padding: max(12px, env(safe-area-inset-top))
      max(12px, env(safe-area-inset-right)) 10px
      max(12px, env(safe-area-inset-left));
    display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px;
    gap: 8px; align-items: center;
    flex: none; border-bottom: 1px solid var(--border-soft);
  }
  .sidebar .brand-spacer { display: none; }
  .sidebar .theme-switch { grid-column: 1; justify-self: start; }
  .sidebar .brand-lockup {
    grid-column: 2; justify-self: center; justify-content: center;
    max-width: 100%; min-width: 0;
  }
  .sidebar .nav-toggle { grid-column: 3; justify-self: end; }
  .sidebar .brand-name { font-size: 16px; }
  .sidebar .brand-sub { font-size: 11.5px; }
  .sidebar .nav-toggle { min-width: 44px; min-height: 44px; justify-content: center; }
  .sidebar.open .nav-burger { background: transparent; }
  .sidebar.open .nav-burger::before {
    top: 0; width: 16px; transform: rotate(45deg);
  }
  .sidebar.open .nav-burger::after {
    top: 0; width: 16px; transform: rotate(-45deg);
  }
  /* Chromium and Safari may retain their UA :focus outline even when a
     programmatically focused button does not match :focus-visible. The class
     exists only for a pointer-open/close and is removed on the first keyboard
     event, so suppress the actual focused control here; keyboard users retain
     the normal gold ring. */
  .sidebar.pointer-open .nav-toggle:focus,
  .nav-fab.pointer-focus:focus {
    outline: 0 solid transparent !important;
    outline-offset: 0;
    box-shadow: none;
  }
  .sidebar nav {
    width: min(100%, 620px); margin-inline: auto;
    flex: 1 1 auto; min-height: 0; padding: 8px 10px 12px;
    overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .sidebar nav a { min-height: 46px; padding: 10px 12px; font-size: 15px; }
  .sidebar .nav-search kbd { display: none; }
  .nav-group + .nav-group { margin-top: 10px; }
  .nav-group-title { padding-top: 8px; }
  .sidebar-foot {
    width: min(100%, 620px); margin-inline: auto;
    flex: none; display: grid;
    grid-template-columns: repeat(auto-fit, minmax(64px, 1fr));
    gap: 5px; padding: 8px 10px max(8px, env(safe-area-inset-bottom));
    background: var(--plane); border-top: 1px solid var(--border);
  }
  .sidebar-foot .profile-tab {
    grid-column: 1 / -1; min-width: 0; min-height: 48px;
    flex-direction: row; justify-content: flex-start; padding: 4px 2px 7px;
  }
  .sidebar-foot .build-foot { display: none; }
  .sidebar-foot .foot-row { display: contents; }
  .sidebar-foot .ghost-btn {
    min-width: 0; min-height: 48px; padding: 5px 2px;
    display: flex; flex-direction: column; justify-content: center; gap: 2px;
    text-align: center; font-size: 10.5px; overflow: hidden;
  }
  .sidebar-foot .btn-ico { width: auto; font-size: 16px; }

  .nav-scrim {
    position: fixed; inset: 0; z-index: 65; display: block;
    background: rgba(10, 16, 30, .5); backdrop-filter: blur(2px);
    opacity: 0; pointer-events: none; transition: opacity .18s var(--ease);
  }
  .nav-scrim[hidden] { display: none; }
  .nav-scrim.on { opacity: 1; pointer-events: auto; }
  body.nav-open { overflow: hidden; overscroll-behavior: none; }

  /* Five role-aware destinations form the app dock. The last item opens the
     complete section drawer; none of it floats over arbitrary record rows. */
  .mobile-dock {
    display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
    position: fixed; z-index: 55; inset: auto 0 0;
    min-height: calc(62px + env(safe-area-inset-bottom));
    padding: 5px max(6px, env(safe-area-inset-right))
             max(5px, env(safe-area-inset-bottom))
             max(6px, env(safe-area-inset-left));
    background: var(--plane); border-top: 1px solid var(--border);
    transition: opacity .16s var(--ease), transform .16s var(--ease);
  }
  html:not(.js) .mobile-dock .nav-fab { display: none; }
  .dock-item, .nav-fab.dock-item {
    position: relative; inset: auto; min-width: 0; min-height: 52px; margin: 0; padding: 5px 3px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; border: 0; border-radius: 0; background: none; color: var(--muted);
    opacity: 1; transform: none; pointer-events: auto;
    font: inherit; font-size: 10.5px; line-height: 1; text-decoration: none; cursor: pointer;
  }
  .dock-item.active, .dock-item[aria-current="page"] { color: var(--ink); }
  .dock-item.active::before, .dock-item[aria-current="page"]::before {
    content: ""; position: absolute; top: -6px; left: 24%; right: 24%; height: 1px;
    background: var(--accent-gold-line);
  }
  .dock-ico { width: 21px; height: 21px; }
  .dock-icon-wrap { position: relative; display: grid; place-items: center; }
  .dock-badge {
    position: absolute; top: -3px; left: 16px; width: 8px; min-width: 8px; height: 8px;
    padding: 0; overflow: hidden; border-radius: 50%; text-indent: -999px;
    background: var(--accent-coral-line); color: transparent;
  }
  .nav-fab .nav-burger { margin: 2px 0 3px; }
  body.nav-open .mobile-dock {
    opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(100%);
  }
  body:has(#main-content input:focus, #main-content textarea:focus, #main-content select:focus)
    .mobile-dock {
    opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(100%);
  }
  body:has(#main-content input:focus, #main-content textarea:focus, #main-content select:focus)
    .main {
    padding-bottom: max(20px, env(safe-area-inset-bottom));
  }

  .main {
    width: 100%; min-width: 0;
    padding: max(20px, env(safe-area-inset-top))
      max(16px, env(safe-area-inset-right))
      calc(86px + env(safe-area-inset-bottom))
      max(16px, env(safe-area-inset-left));
  }
  .public-shell .main {
    padding-bottom: max(20px, env(safe-area-inset-bottom));
  }
  .page-head { align-items: flex-start; gap: 6px 10px; margin-bottom: 18px; }
  .page-head h1 { flex-basis: 100%; font-size: 23px; line-height: 1.18; }
  .page-head .sub { flex-basis: 100%; font-size: 13px; line-height: 1.4; }
  .page-head .spacer { display: none; }
  .page-head > .btn, .page-head > .seg, .page-head > .filters { margin-top: 5px; }
  .page-head > .btn { flex: 1 1 auto; justify-content: center; }
  .page-head > .seg, .page-head > .filters { flex-basis: 100%; width: 100%; }
  .card { padding: 14px 0; }
  .grid.tiles { gap: 10px; }
  .feed-grid { grid-template-columns: minmax(0, 1fr); }
  img, video, canvas, iframe { max-width: 100%; }
  form, fieldset { min-width: 0; max-width: 100%; }

  /* This is intentionally final and important: dozens of older page-specific
     selectors otherwise reintroduce 11–14px fields after the iOS safeguard. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  select:not([multiple]), textarea {
    min-height: 44px; max-width: 100%; font-size: 16px !important;
  }
  button:not(.punch-button), .btn, .btn-link, .ghost-btn { min-height: 44px; }
  .btn { padding: 9px 13px; font-size: 14px; }
  .btn + .btn { margin-left: 0; }
  input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; }

  /* The old .filters component remains as a safe wrapping fallback. Only an
     explicit .tab-strip is a horizontally scrolling choice. */
  .filters {
    flex-wrap: wrap; overflow: visible; gap: 8px; padding-bottom: 0;
  }
  .filters > * { flex: 0 1 auto; }
  .filters > form { flex: 1 1 100%; display: flex; gap: 8px; }
  .filters > form input[type="search"] { flex: 1; min-width: 0; }
  .filters a, .filters .chip { min-height: 44px; padding: 10px 13px; }
  .seg, .seg-row, .portal-tabs {
    max-width: 100%; overflow-x: auto; flex-wrap: nowrap;
    overscroll-behavior-inline: contain; scrollbar-width: none;
  }
  .seg::-webkit-scrollbar, .seg-row::-webkit-scrollbar,
  .portal-tabs::-webkit-scrollbar { display: none; }
  .seg a, .seg-row a, .portal-tabs a { min-height: 44px; flex: 0 0 auto; }

  .action-bar { display: grid; grid-template-columns: 1fr; }
  .action-bar > .btn { width: 100%; }
  .tab-strip { scroll-snap-type: x proximity; gap: 18px; }
  .tab-strip a { min-height: 48px; scroll-snap-align: center; font-size: 14px; }
  .filter-toolbar { display: grid; grid-template-columns: 1fr; gap: 10px; }
  .list-filter-form:not(.audit-filter-form) {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    width: 100%; margin-bottom: 16px; padding: 10px 0 12px;
    overflow: visible;
  }
  .page-head > .list-filter-form { flex-basis: 100%; margin-left: 0; }
  .list-filter-search { flex: 2 1 210px; min-width: 0; }
  .list-filter-form > select { flex: 1 1 130px; }
  .list-filter-actions { flex: 0 0 auto; }
  .list-filter-status { flex: 1 0 100%; margin: 0; white-space: normal; }
  .audit-filter-form.list-filter-form { display: grid; grid-template-columns: 1fr 1fr; }
  .audit-filter-form .list-filter-search,
  .audit-filter-form .list-filter-actions,
  .audit-filter-form .list-filter-status { grid-column: 1 / -1; }
  .filters > .list-filter-form,
  .invoice-list-tools > .list-filter-form { width: 100%; flex-basis: 100%; }
  .invoice-result-summary { font-size: 13px; }
  .invoice-search { width: 100%; margin: 0; }
  .invoice-search input { flex: 1; min-width: 0; }
  .mobile-only { display: flex; }
  .invoice-sort {
    width: 100%; display: grid; grid-template-columns: auto minmax(0, 1fr)
      minmax(0, .85fr) auto; align-items: center; gap: 7px;
    color: var(--muted); font-size: 12px;
  }
  .invoice-sort select { min-width: 0; }

  /* Operational registers: one semantic table on desktop and a labelled,
     flat record list on compact screens. No boxes, radius or shadow — the
     brand's hairline and spacing provide the hierarchy. */
  .table-shell--register, .invoice-register-wrap { overflow: visible; }
  .table-shell--wide {
    width: 100%; max-width: 100%; overflow-x: auto;
    overscroll-behavior-inline: contain; -webkit-overflow-scrolling: touch;
  }
  table.data.data--mobile-scroll:not(.data--register):not(.inv-editor) {
    display: block; width: 100%; max-width: 100%; overflow-x: auto;
    overscroll-behavior-inline: contain; -webkit-overflow-scrolling: touch;
  }
  .table-shell--wide > table.data.data--mobile-scroll:not(.data--register):not(.inv-editor) {
    display: table; width: max-content; min-width: 100%; max-width: none;
    overflow: visible;
  }
  details > summary {
    min-height: 44px; padding-block: 10px;
    white-space: normal; overflow-wrap: anywhere; cursor: pointer;
  }
  table.data--register { display: block; width: 100%; font-size: 14px; }
  table.data--register > tbody { display: block; width: 100%; }
  table.data--register .record-table-head,
  table.data--register > thead {
    position: absolute !important; width: 1px !important; height: 1px !important;
    padding: 0 !important; margin: -1px !important; overflow: hidden !important;
    clip: rect(0 0 0 0) !important; clip-path: inset(50%) !important;
    white-space: nowrap !important; border: 0 !important;
  }
  table.data--register > tbody > tr.record-row,
  table.data--register > tbody > tr:not(.record-table-head) {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 16px; width: 100%; padding: 15px 0;
    border-bottom: 1px solid var(--border); background: transparent;
  }
  table.data--register > tbody > tr:first-child { border-top: 0; }
  table.data--register > tbody > tr:last-child { border-bottom: 0; }
  table.data--register > tbody > tr > td {
    display: block; min-width: 0; padding: 0; border: 0 !important;
    text-align: left !important; overflow-wrap: anywhere;
  }
  table.data--register > tbody > tr > td::before {
    content: attr(data-label); display: block; margin: 0 0 2px;
    color: var(--muted); font: 400 9.5px/1.35 "Space Mono", monospace;
    letter-spacing: .08em; text-transform: uppercase;
  }
  table.data--register > tbody > tr > td[data-mobile-role="primary"] {
    order: -100; font-size: 17px; line-height: 1.25;
  }
  table.data--register > tbody > tr > td[data-mobile-role="primary"]::before { display: none; }
  table.data--register > tbody > tr > td[data-mobile-role="status"] { order: -90; justify-self: end; }
  table.data--register > tbody > tr > td[data-mobile-role="amount"] { order: -80; }
  table.data--register > tbody > tr > td[data-mobile-role="subject"],
  table.data--register > tbody > tr > td[data-mobile-role="actions"],
  table.data--register > tbody > tr > td[data-mobile-role="message"] { grid-column: 1 / -1; }
  table.data--register > tbody > tr > td[data-mobile-role="actions"] {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap; padding-top: 4px;
  }
  table.data--register > tbody > tr > td[data-mobile-empty="1"] { display: none; }
  table.data--register > tbody > tr:hover > td { background: transparent; }
  table.data--register > tbody > tr[aria-current="true"] {
    box-shadow: inset 1px 0 0 var(--accent-gold-line); padding-left: 10px;
  }

  /* The invoice register has explicit roles, so its most useful numbers land
     in predictable places while every accounting field stays available. */
  .invoice-register tbody > tr[data-inv] {
    grid-template-areas:
      "number total"
      "customer customer"
      "status status"
      "date due"
      "what what"
      "smart smart"
      "kg over";
    align-items: start;
  }
  .invoice-register .reg-number { grid-area: number; }
  .invoice-register .reg-total { grid-area: total; justify-self: end;
    text-align: right !important; font-size: 17px; }
  .invoice-register .reg-customer { grid-area: customer; font-size: 16px; }
  .invoice-register .reg-status { grid-area: status; }
  .invoice-register .reg-date { grid-area: date; }
  .invoice-register .reg-due { grid-area: due; }
  .invoice-register .reg-what { grid-area: what; }
  .invoice-register .reg-smart { grid-area: smart; }
  .invoice-register .reg-kg { grid-area: kg; }
  .invoice-register .reg-over { grid-area: over; }
  .invoice-register .reg-status {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: start; gap: 10px 14px; padding: 8px 0 2px;
    border-top: 1px solid var(--border-soft) !important;
  }
  .invoice-register .reg-status::before { grid-column: 1 / -1; margin-bottom: -4px; }
  .invoice-row-actions { display: flex; justify-content: flex-end; gap: 7px; flex-wrap: wrap; }
  .invoice-row-actions .btn, .invoice-row-actions .rowmenu-open {
    min-width: 44px; min-height: 44px; margin: 0; float: none; opacity: 1;
  }
  .invoice-empty { display: block !important; }
  .invoice-empty td { display: block; width: 100%; padding: 16px 0 !important; }

  /* Invoice/quotation line editing follows the already successful purchase
     line-card pattern instead of retaining a 560–860px desktop grid. */
  .inv-editor-wrap, .quote-lines-wrap { overflow: visible; border: 0; }
  .inv-editor { display: block; min-width: 0 !important; width: 100%; }
  .inv-editor thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .inv-editor tbody { display: block; }
  .inv-editor tbody > tr {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 12px; padding: 14px 0; border-bottom: 1px solid var(--border);
  }
  .inv-editor td { min-width: 0; display: block; padding: 0; border: 0; text-align: left !important; }
  .inv-editor td::before {
    content: attr(data-label); display: block; margin-bottom: 4px;
    color: var(--muted); font: 400 9.5px/1.35 "Space Mono", monospace;
    letter-spacing: .08em; text-transform: uppercase;
  }
  .inv-editor .inv-line-item, .inv-editor .inv-line-description,
  .inv-editor td:first-child { grid-column: 1 / -1; }
  .inv-editor .inv-line-amount { align-self: end; }
  .inv-editor .inv-line-remove { align-self: end; justify-self: end; }
  .inv-editor .inv-line-remove::before { display: none; }
  .inv-editor .inv-item, .inv-editor .inv-desc,
  .inv-editor .inv-qty, .inv-editor .inv-price,
  .inv-editor .inv-line-item select, .inv-editor .inv-line-description input,
  .inv-editor .inv-line-description textarea, .inv-editor .inv-line-qty input,
  .inv-editor .inv-line-price input { width: 100%; }
  .inv-editor .inv-amt { padding-top: 0; min-height: 44px; display: flex; align-items: center; }
  .inv-editor .inv-del { width: 44px; }
  .inv-head-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .inv-head-row label { min-width: 0; }
  .inv-foot { align-items: stretch; flex-direction: column; }
  .inv-sum { width: 100%; min-width: 0; }
  .inv-actions { width: 100%; flex-wrap: wrap; }
  .inv-actions .btn { flex: 1 1 160px; }
  .new-invoice::after { display: block; margin: 9px 0 0; line-height: 1.45; }
  .new-invoice > summary { min-height: 46px; }

  .scroll-x:not(.table-shell--register) {
    max-width: 100%; overscroll-behavior-inline: contain;
    -webkit-overflow-scrolling: touch; scrollbar-width: thin;
    scrollbar-color: var(--muted) transparent;
  }
  .pl th:first-child, .pl td:first-child,
  .mtd-table th:first-child, .mtd-table td:first-child,
  .budget-table th:first-child, .budget-table td:first-child,
  .rota-table th:first-child, .rota-table td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--plane);
  }

  /* A full-screen phone sheet is modal in drawer.js. Safe-area padding and a
     sticky header keep its close action reachable in Safari and standalone. */
  dialog.sheet {
    inset: 0; width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
    border: 0; overflow: auto; overscroll-behavior: contain;
  }
  dialog.sheet::backdrop { background: rgba(10, 16, 30, .5); }
  .sheet-head {
    min-height: 62px; align-items: center;
    padding: max(10px, env(safe-area-inset-top))
      max(14px, env(safe-area-inset-right)) 10px
      max(14px, env(safe-area-inset-left));
  }
  .sheet-head h2 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .sheet-head .btn { min-height: 44px; }
  dialog.sheet > [data-popup-body] {
    padding: 8px max(16px, env(safe-area-inset-right))
      max(24px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  }
  body.drawer-open { overflow: hidden; }
  .rowmenu { max-height: calc(100dvh - 20px); overflow-y: auto;
    overscroll-behavior: contain; }
  .rowmenu-item { min-height: 44px; padding: 10px 12px; }

  .chart { max-width: 100%; overflow: hidden; }
  .chart svg { max-width: 100%; }
  #imvelo-lusaka-map .map-stage svg {
    height: max(360px, 55dvh) !important; min-height: 360px;
  }
  #imvelo-lusaka-map .map-toolbar button { min-width: 44px; min-height: 44px; }
  #imvelo-lusaka-map .map-actions { width: 100%; }
  #imvelo-lusaka-map .map-actions .btn { min-height: 44px; }
}

@media screen and (max-width: 640px) {
  .main {
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
  }
  .grid.tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tile .value { font-size: 21px; }
  .invoice-sort { grid-template-columns: auto minmax(0, 1fr); }
  .invoice-sort select:last-of-type, .invoice-sort .btn { grid-column: 2; }
  .inv-head-row { grid-template-columns: 1fr; }
  .invoice-register .reg-status { grid-template-columns: 1fr; }
  .invoice-row-actions { justify-content: flex-start; }
  .sheet-head [data-popup-link] { display: none; }
  .page-head > .btn { flex-basis: 100%; width: 100%; }
  .add-person .row, .location-form .row,
  .stock-actions form, .reread-actions,
  .invoice-source-choice { align-items: stretch; }
  .add-person .row > label, .location-form .row > label,
  .stock-actions form > label { flex: 1 1 100%; width: 100%; }
  .add-person .row > .btn, .location-form .row > .btn,
  .stock-actions form > .btn, .reread-actions > .btn,
  .issue .actions > .btn { flex: 1 1 100%; width: 100%; }
  .location-coords .capture-location { align-self: stretch; }
  .purchase-details-document, .purchase-details-payment { grid-template-columns: 1fr; }
  .purchase-details-actions { display: grid; grid-template-columns: 1fr; }
  .purchase-details-actions .btn, .purchase-details-actions .btn-link {
    width: 100%; white-space: normal;
  }
  .purchase-details-actions .save-state { grid-column: 1; min-width: 0; }
}

@media screen and (max-width: 360px) {
  .dock-item, .nav-fab.dock-item { font-size: 9.5px; }
  .dock-ico { width: 19px; height: 19px; }
  .grid.tiles { grid-template-columns: 1fr; }
  table.data--register > tbody > tr.record-row,
  table.data--register > tbody > tr:not(.record-table-head) { column-gap: 10px; }
}

/* Customer billing packages and the unit displayed on document editors. */
/* Cards keep Save visible in the customer drawer as well as on phones. */
.packing-card { padding: 14px 0; border-bottom: 1px solid var(--border); }
.packing-card-heading { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; margin-bottom: 12px; }
.packing-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); gap: 12px; }
.packing-fields label { display: flex; flex-direction: column; gap: 5px; min-width: 0; font-size: 12px; }
.customer-packing input {
  width: 100%; min-width: 0; min-height: 44px; padding: 8px;
  border: 1px solid var(--border); background: transparent; color: var(--ink); font: inherit;
}
.customer-packing input:focus { outline: 2px solid var(--ink); outline-offset: 1px; }
.packing-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 16px; margin-top: 12px; }
.packing-actions .btn { min-height: 44px; }
.customer-packing .save-state { white-space: normal; }
.line-packing { margin-top: 8px; font-size: 12px; }
.line-packing label { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.line-packing .coffee-form { width: auto; max-width: 100%; }
.packing-note { margin-top: 6px; white-space: normal; line-height: 1.5; }
.billing-unit { font-size: 12px; margin-top: 5px; white-space: normal; }

.invoice-discount-controls { display: flex; flex-wrap: wrap; gap: 12px; align-items: end; margin: 18px 0; }
.invoice-discount-controls label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; }
.invoice-discount-controls input, .invoice-discount-controls select {
  min-height: 42px; width: 170px; padding: 8px; border: 1px solid var(--border);
  background: transparent; color: var(--ink); font: inherit;
}
.invoice-discount-controls > .dim { flex: 1 1 230px; font-size: 12px; line-height: 1.5; }
.invoice-discount-controls .discount-error { flex-basis: 100%; }
.inv-sum [data-discount-row][hidden] { display: none; }

/* Payroll selection is a list of people, with contained payment inputs on phones. */
.payroll-tabs { margin-bottom: 16px; flex-wrap: wrap; }
.payroll-toolbar { display: flex; align-items: end; flex-wrap: wrap; gap: 16px 24px; margin-bottom: 16px; }
.payroll-month, .payroll-downloads { display: flex; align-items: end; flex-wrap: wrap; gap: 8px; }
.payroll-month label { display: flex; flex-direction: column; gap: 6px; font-size: 12px; }
.payroll-month input { box-sizing: border-box; min-height: 44px; padding: 8px 10px; font: inherit; color: var(--ink); background: transparent; border: 1px solid var(--border); border-radius: 6px; }
.payroll-downloads { margin-left: auto; align-items: center; }
.payroll-downloads > .dim { width: 100%; font-size: 12px; }
.payroll-person-row { display: flex; align-items: center; gap: 12px; }
.payroll-person-row > .payroll-person-choice { flex: 1; min-width: 0; flex-wrap: nowrap; }
.payroll-person-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.payroll-person-actions .status { margin-right: 8px; font-size: 12px; }
.payroll-editor, .payroll-history { max-width: 1100px; margin-bottom: 16px; }
.payroll-editor > .dim, .payroll-editor fieldset .dim { font-size: 13px; line-height: 1.6; }
.payroll-editor fieldset { min-width: 0; margin: 24px 0; padding: 16px 0 0; border: 0; border-top: 1px solid var(--border); }
.payroll-editor fieldset[hidden] { display: none; }
.payroll-editor legend { font-weight: 600; padding-right: 12px; }
.payroll-fields select, .payroll-reason textarea { box-sizing: border-box; width: 100%; min-height: 44px; padding: 10px; font: inherit; color: var(--ink); background: transparent; border: 1px solid var(--border); border-radius: 6px; }
.payroll-reason { display: flex; flex-direction: column; gap: 8px; font-size: 13px; }
.payroll-history summary { cursor: pointer; padding: 14px 0; }
.payroll-history p, #payroll-edit-state { overflow-wrap: anywhere; }
.payslip-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 16px; }
.payslip-actions .ps-print { margin-left: auto; }
@media (max-width: 700px) {
  .payroll-toolbar { align-items: stretch; }
  .payroll-month, .payroll-downloads { width: 100%; margin-left: 0; }
  .payroll-month label { flex: 1; min-width: 0; }
  .payroll-month input { width: 100%; min-width: 0; }
  .payroll-downloads .btn { flex: 1 1 auto; }
  .payroll-person-row { align-items: stretch; flex-direction: column; gap: 8px; }
  .payroll-person-actions { padding-left: 34px; }
  .payroll-person-actions .status { flex-basis: 100%; }
}
.payroll-run > .dim { font-size: 13px; line-height: 1.6; }
.payroll-selection-tools { display: flex; align-items: end; flex-wrap: wrap; gap: 12px; margin: 16px 0; }
.payroll-selection-tools label, .payroll-fields label { display: flex; flex-direction: column; gap: 6px; font-size: 12px; }
.payroll-people { display: grid; gap: 10px; }
.payroll-person { border: 1px solid var(--border); padding: 14px; border-radius: 8px; min-width: 0; }
.payroll-person[hidden], .payroll-payment[hidden] { display: none; }
.payroll-person-choice { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; min-height: 44px; cursor: pointer; }
.payroll-person-choice input[type=checkbox] { width: 22px; height: 22px; min-height: 22px; flex: 0 0 22px; accent-color: var(--ink); }
.payroll-person-choice > span:first-of-type { flex: 1 1 180px; min-width: 0; overflow-wrap: anywhere; }
.payroll-person-choice .dim { display: block; font-size: 12px; margin-top: 4px; }
.payroll-person > .btn-link { display: inline-block; padding: 8px 0; }
.payroll-payment { margin: 10px 0 0; padding: 12px 0 0; border: 0; min-width: 0; }
.payroll-payment legend { font-size: 12px; }
.payroll-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: 12px; }
.payroll-fields input, .payroll-selection-tools input { min-height: 44px; width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink); font: inherit; }
.payroll-deductions { margin-top: 14px; }
.payroll-deductions summary { padding: 12px 0; font-size: 12px; cursor: pointer; }
#run-state { overflow-wrap: anywhere; }
.storage-warning { overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .payroll-selection-tools > label, .payroll-selection-tools > .btn { width: 100%; }
}
.inv-delivery-tools { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin: 16px 0; }
.inv-delivery-tools strong { flex: 1 1 190px; font-size: 12px; }
