/* ============================================================
   TopperBase UI — 2026 redesign.

   Design system, in short:
   · Type      Inter only. One family, four weights, tight tracking.
                Uppercase is reserved for micro-labels (11px), never for
                anything you can click.
   · Shape     Four radii (2 / 3 / 6 / 8). Tight, close to square: a tool
                for running a business, not a consumer app. No chamfers, no
                clip-paths, no pills. Every control is a rectangle.
   · Scale     One type scale (--fs-2xs..--fs-2xl) and one spacing scale
                (--sp-1..--sp-6). Nothing in this file sets a size that is
                not on a scale.
   · Color    A neutral slate scale plus ONE accent, taken from the logo's
                blue. Accent marks state (selected, focused, primary) and
                nothing else — it is never decoration.
   · Depth     Hairlines do the work. A card is a bordered plane, flat on
                the page. Shadows are ONLY for things that genuinely float
                above it — menus, modals, toasts, the sticky action bar.
   ============================================================ */

@font-face { font-family: "Inter"; font-weight: 400; font-style: normal; font-display: swap; src: url("../fonts/Inter-400.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-weight: 500; font-style: normal; font-display: swap; src: url("../fonts/Inter-500.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-weight: 600; font-style: normal; font-display: swap; src: url("../fonts/Inter-600.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-weight: 700; font-style: normal; font-display: swap; src: url("../fonts/Inter-700.ttf") format("truetype"); }
/* Barlow, for the wordmark. Already used by print.css for printed documents
   (Settings offers it as a document font) but never declared for the interface,
   so the wordmark could not reach it. A low-contrast grotesk with squared-off
   terminals — signage lettering, which suits an angular TB monogram and a truck
   shop rather better than Inter, a face designed to disappear into user
   interfaces. One weight here, and at 82KB it is a quarter of one Inter weight. */
@font-face { font-family: "Barlow"; font-weight: 500; font-style: normal; font-display: swap; src: url("../fonts/Barlow-500.ttf") format("truetype"); }
@font-face { font-family: "Source Serif 4"; font-weight: 600; font-style: normal; src: url("../fonts/SourceSerif4-600.ttf") format("truetype"); }
@font-face { font-family: "Source Serif 4"; font-weight: 700; font-style: normal; src: url("../fonts/SourceSerif4-700.ttf") format("truetype"); }

/* ---- Tokens ---- */
:root {
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* The wordmark, and nothing else. Kept off --font-display so a later change
     of heading font can't quietly restyle the brand. */
  --font-brand: "Barlow", "Inter", system-ui, sans-serif;
  --font-display: var(--font-body);
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Neutrals — a cool slate ramp, low chroma so the accent stays the only color. */
  --bg: #f4f6f8;
  --card: #ffffff;
  --ink: #10161f;
  --ink-soft: #39434f;
  --muted: #6b7480;
  --line: #e0e4ea;
  --line-soft: #eef0f4;
  --thead-bg: #f7f8fa;
  --hover-bg: #f4f6f9;
  --bg-soft: #f7f8fa;

  /* Accent — the blue from the TopperBase mark, deepened for UI use. */
  --accent: #1a56c8;
  --accent-hover: #14459f;
  --accent-soft: #ecf1fd;
  --accent-ink: #ffffff;

  /* Left rail. Light by default; the dark theme flips it. */
  --sidebar-bg: #ffffff;
  --sidebar-ink: #4d5765;
  --sidebar-active: #eef2fb;
  --sidebar-line: #e3e7ee;
  /* The ground behind the mark. Transparent, so it takes whatever surface it
     sits on — which is the rail, so the whole left column is one color.
     It was a light grey for a while, and against the white top bar beside it and
     the white rail below it that read as a chequerboard: three panels meeting at
     the top-left corner, two white and the odd one out grey. One color removes
     the pattern. Every theme inherits this; set a real value here if a ground is
     ever wanted again. */
  --brand-plate: transparent;

  /* Dark chrome used by preview bars and toasts, independent of the rail. */
  --chrome: #131c27;
  --auth-bg: #121a24;
  --paper-bg: #eceff3;

  --input-bg: #ffffff;
  --input-border: #ccd2db;
  --input-hover: #b3bcc9;

  --danger: #c23934; --danger-soft: #fdeceb; --danger-line: #f0cecb;
  --warn: #8a6116; --warn-ink: #6f4d10; --warn-soft: #fbf3e2; --warn-bg: #fbf3e2;
  --ok-ink: #1a6b44; --ok-soft: #e7f3ec;
  --info-ink: #1a4f96; --info-soft: #e9f0fb;

  /* Shape. Tightened from 4/6/10/14 — the old set read soft and consumer-y.
     A control is 3px, a plane is 6px, and a badge is barely rounded at all. */
  --radius-sm: 2px;
  --radius-ctl: 3px;
  --radius: 6px;
  --radius-lg: 8px;

  /* Type scale. Seven steps, nothing between them. Anything that wants a size
     picks a step; if no step fits, the design is wrong, not the scale. */
  --fs-2xs: 0.6875rem;  /* 11px — micro-labels, table headers, badges */
  --fs-xs:  0.75rem;    /* 12px — the rail, dense meta, counts */
  --fs-sm:  0.8125rem;  /* 13px — secondary text, descriptions, small controls */
  --fs-md:  0.875rem;   /* 14px — body, controls, table cells. The default. */
  --fs-lg:  1rem;       /* 16px — section headings */
  --fs-xl:  1.25rem;    /* 20px — page title, figures on a tile */
  --fs-2xl: 1.5rem;     /* 24px — reserved: one number, when it is the point */

  /* Spacing scale, 4px based. */
  --sp-1: 0.25rem; --sp-2: 0.5rem; --sp-3: 0.75rem;
  --sp-4: 1rem;    --sp-5: 1.5rem; --sp-6: 2rem;

  /* Content measures. A page is 1200px wide; things that read as a single
     column stop at one of these, so their right edges line up screen to screen. */
  --measure-narrow: 640px;   /* a short form */
  --measure-form:   780px;   /* a labelled form column */
  --measure-list:   880px;   /* a list of rows */

  /* Control heights. Every clickable/typable box lands on one of these three,
     so a button, an input and a combobox sitting in a row are the same height. */
  --control-h-sm: 28px;
  --control-h: 34px;
  /* The height of the app's top edge, used by BOTH the top bar and the brand
     band at the head of the rail. They both start at y=0, so giving them one
     height makes their bottom edges meet as a single line across the window
     instead of a step. 50px is comfortable now the band is one line: it held
     the lockup AND the shop name at first, which needed 44px of content and
     forced 56. The name moved to the bar, so the band could come down. */
  --chrome-h: 50px;
  --control-h-lg: 42px;

  /* A card and a button sit ON the page, they do not hover above it, so the
     hairline is the whole story. Kept as tokens rather than deleted at each
     call site: the surfaces stay described by the same two names, and a theme
     that wants depth back only has to redefine these. */
  --shadow-ctl: none;
  --shadow: none;
  --shadow-md: 0 4px 12px -2px rgba(16, 24, 40, 0.10), 0 2px 4px -2px rgba(16, 24, 40, 0.06);
  --shadow-pop: 0 12px 28px -8px rgba(16, 24, 40, 0.22), 0 2px 6px rgba(16, 24, 40, 0.06);
  --focus-ring: rgba(26, 86, 200, 0.18);
}

[data-theme="forest"] {
  --accent: #14684a; --accent-hover: #0f5339; --accent-soft: #e8f3ed;
  --sidebar-active: #e8f3ed; --focus-ring: rgba(20, 104, 74, 0.18);
  --info-ink: #14684a; --info-soft: #e8f3ed;
}
[data-theme="slate"] {
  --accent: #3f4f66; --accent-hover: #324054; --accent-soft: #eceef2;
  --sidebar-active: #eceef2; --focus-ring: rgba(63, 79, 102, 0.2);
  --info-ink: #3f4f66; --info-soft: #eceef2;
}
[data-theme="ember"] {
  --accent: #9c4413; --accent-hover: #80350c; --accent-soft: #fbeee5;
  --sidebar-active: #fbeee5; --focus-ring: rgba(156, 68, 19, 0.18);
  --bg: #f7f5f2; --hover-bg: #f8f5f1; --thead-bg: #faf8f5;
  --line: #e6e0d8; --line-soft: #f0ece6; --input-border: #d3cabe; --input-hover: #bcb0a1;
  --muted: #736a5f; --sidebar-line: #e9e3db;
  --info-ink: #9c4413; --info-soft: #fbeee5;
}
[data-theme="dark"] {
  --bg: #0e141b; --card: #151d26; --ink: #e4e9f0; --ink-soft: #b9c3d0; --muted: #8d99a8;
  --line: #26313d; --line-soft: #1e2731; --thead-bg: #19222c; --hover-bg: #1a2430; --bg-soft: #19222c;

  --accent: #5b95e8; --accent-hover: #7aabee; --accent-soft: #17273c; --accent-ink: #08111d;

  --sidebar-bg: #0b1119; --sidebar-ink: #97a4b4; --sidebar-active: #17273c; --sidebar-line: #1f2935;

  --chrome: #070c12; --auth-bg: #070c12; --paper-bg: #0a1017;

  --input-bg: #101922; --input-border: #2f3d4d; --input-hover: #435364;

  --danger: #e8776c; --danger-soft: #3a1e1b; --danger-line: #5a2f2a;
  --warn: #d8a94a; --warn-ink: #e2bb6c; --warn-soft: #362a11; --warn-bg: #362a11;
  --ok-ink: #6ec99a; --ok-soft: #12301f;
  --info-ink: #8bb9ec; --info-soft: #17273c;

  --shadow-ctl: none;
  --shadow: none;
  --shadow-md: 0 4px 14px -2px rgba(0, 0, 0, 0.6);
  --shadow-pop: 0 14px 32px -8px rgba(0, 0, 0, 0.7);
  --focus-ring: rgba(91, 149, 232, 0.28);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* Tell the UA which way the page leans so the controls it draws itself —
   checkboxes, the select popup, date pickers, scrollbars — follow the theme
   instead of staying stark white on the dark rail. */
:root { color-scheme: light; }
[data-theme="dark"] { color-scheme: dark; }
::selection { background: var(--accent-soft); color: var(--ink); }

body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 400 14px/1.5 var(--font-body);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}
a { color: var(--accent); text-underline-offset: 2px; }
/* Inside a list, the identifier is data that happens to be clickable — and the
   whole row is clickable anyway. Underlining every one of forty rows turned the
   left edge of every list into a blue ladder. The underline comes back on hover,
   so the affordance is still there when you reach for it. */
table.rowlink tbody a, table.list-stack tbody a { text-decoration: none; }
table.rowlink tbody a:hover, table.list-stack tbody a:hover,
table.rowlink tbody a:focus-visible, table.list-stack tbody a:focus-visible { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* Loading bar while the next page is fetched. Thicker than a hairline: at 2px
   it was easy to miss on a slow page, and a page that looks unchanged gets
   clicked a second time — which on a report with a date range runs it twice. */
body.pjax-loading::after {
  content: ""; position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 999;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: pjax-sweep 0.9s linear infinite;
}
@keyframes pjax-sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}
/* Past about half a second the page stops pretending it is idle: the content
   recedes, stops taking clicks, and says what it is doing. Deliberately delayed
   — flashing this on every fast navigation would be worse than saying nothing. */
body.pjax-slow main.container { opacity: 0.45; pointer-events: none; transition: opacity 0.15s; }
body.pjax-slow::before {
  content: "Loading…";
  position: fixed; top: 1.1rem; left: 50%; transform: translateX(-50%); z-index: 1000;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-ctl);
  padding: 0.3rem 0.9rem; font-size: var(--fs-sm); font-weight: 600;
  box-shadow: 0 6px 20px -8px rgba(16, 24, 40, 0.35);
}

/* ---- Display type: one family, tight tracking, no shouting ---- */
h1 {
  font-family: var(--font-body); font-weight: 600; font-size: var(--fs-xl);
  margin: 0.1rem 0 0.9rem; letter-spacing: -0.021em; line-height: 1.2;
  color: var(--ink); text-wrap: balance;
}
h2 {
  font-family: var(--font-body); font-weight: 600; font-size: var(--fs-lg);
  letter-spacing: -0.012em; line-height: 1.35; color: var(--ink);
  margin: 0 0 0.7rem;
}
h3 {
  font-family: var(--font-body); font-weight: 600; font-size: var(--fs-md);
  letter-spacing: -0.008em; margin: 1.1rem 0 0.45rem; color: var(--ink);
}

/* ---- Left rail: a quiet plane, separated by one hairline ---- */
body.with-sidebar { display: flex; }
/* Sized down ~15% from the original 0.875rem/234px: the rail had grown past
   what a laptop screen shows without scrolling, and the nav is a list you
   navigate by position and icon, not something you read. */
.sidebar {
  width: 246px; flex-shrink: 0; height: 100vh; position: sticky; top: 0;
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--sidebar-bg); color: var(--sidebar-ink);
  border-right: 1px solid var(--sidebar-line);
  display: flex; flex-direction: column; padding: 1.05rem 0.75rem 0.7rem;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
/* The brand band: a full-width dark header at the top of the rail, with the
   mark and wordmark reversed out of it.

   Full-bleed on purpose. It used to be an inset block with a border-bottom and
   a border-radius — but only the BOTTOM border was drawn, so the radius curled
   its two ends upward and the result read as an empty rounded box sitting under
   the logo. Negative margins pull the band out to the rail's own edges (they
   cancel .sidebar's 0.9rem/0.65rem padding), and with no radius and no border
   there is no box left to misread.

   The band is a light grey a step off the rail — enough to set the brand apart
   from the navigation without becoming a colored header. The logo keeps its
   ordinary ink on it, so nothing needs reversing and there is one less way for
   the mark and the wordmark to disagree. */
.brand {
  display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
  background: var(--brand-plate); color: var(--ink);
  /* No ground of its own (see --brand-plate) — the rail's color shows through,
     so the left column reads as one surface. The height still comes from
     --chrome-h with the content centred in it, which is what keeps the mark
     level with the icons in the top bar beside it. */
  min-height: var(--chrome-h); justify-content: center;
  /* The bottom margin is doing grouping work, not decoration: with the brand
     the same distance from "Search" as "Search" is from "Dashboard", proximity
     alone makes the logo read as the first item in the list. It needs to sit
     apart to read as a heading over them. */
  margin: -1.05rem -0.75rem 1.15rem; padding: 0.25rem 0.7rem;
  text-decoration: none;
}
/* ---- The TopperBase lockup ----
   Monogram beside a wordmark set in the interface's own type, so the brand sits
   on the same type scale as everything else instead of being a picture that
   happens to be near it. One block, four sizes; nothing here is a one-off
   value that isn't derived from the mark's own height. */
.brand-lockup {
  /* baseline, not center. Centring the mark's box against the word's line box
     depends on how the two round at each size, and they round differently: the
     same correction left the mark 0.15px off at one size and 0.62px off at
     another, which is visible. Aligning to the baseline is deterministic —
     the mark's bottom edge lands on the text baseline whatever the size — and
     measures within 0.2px at all three, consistently. */
  display: inline-flex; align-items: baseline;
  /* inherit, NOT var(--ink): the lockup has to be able to reverse. It sits on
     the card on the sign-in page (dark ink) and on the dark brand band in the
     sidebar (light ink), and pinning the color here silently wins over both —
     which is exactly what made the mark and "Topper" vanish into the band the
     first time it was tried, leaving only the accent-colored "Base" showing. */
  color: inherit; line-height: 1;
  /* The lockup carries the size and everything in it is a multiple of that. Set
     the mark in px per size instead and the proportions drift: the first cut of
     this had the drawn mark at 1.50x the wordmark's cap height at two sizes and
     1.29x at the third — exactly the kind of near-miss that reads as "slightly
     wrong" without being nameable. One ratio, three font sizes. */
  font-size: var(--brand-size);
  gap: 0.3074em;
}
.brand-mark { display: flex; color: inherit; }
.brand-mark svg {
  /* 424x305 artwork with its optical margin already inside the viewBox, so
     height drives it and the width follows.

     0.7705em was once the size at which the drawn shape exactly matched the
     wordmark's cap height. Scaled up four times since (x1.15, x1.06, x1.05,
     x1.03); the word has grown too, so the mark leads at about 1.86x the caps —
     the monogram is the brand and the word is its caption.

     If the ratio is ever reset to 1:1, take the cap figure from the FONT rather
     than eyeballing it or asking canvas measureText: this was 0.795em first,
     off a guess of "0.75em", which is 3.1% too tall. Invisible in isolation,
     but with the mark sitting on the baseline the whole excess shows at the top
     and the logo reads as riding high above the word. */
  height: 1.0159em; width: auto; display: block;
  /* Baseline alignment puts the SVG's BOTTOM EDGE on the text baseline, but the
     drawn shape stops 9/305 of the box short of that edge, so without this the
     mark floats just above the line. 9/305 x 1.0159em = 0.0300em, which lands
     the drawn shape's bottom exactly on the text baseline. Recompute it if the
     height above changes — it is a fraction OF that height, not a constant. */
  transform: translateY(0.0300em);
}
.brand-word {
  /* Deliberately smaller and lighter than the mark: the monogram is the brand
     and the word is its caption, so the word should not compete with it.
     Barlow 500 rather than Inter 400 — Barlow runs optically lighter at the same
     nominal weight, so this reads no heavier while holding together better at
     0.68em than a 400 does. Still nowhere near bold.

     Both sit on one baseline (.brand-lockup aligns on it), so resizing either
     side needs no alignment work — only the mark's own nudge, which is a
     fraction of its height. */
  font-family: var(--font-brand);
  font-weight: 500; font-size: 0.7344em;
  /* Centred against the mark, not sat on its baseline.
     Baseline alignment (see .brand-lockup) is still what positions these two,
     because it is the only deterministic way to relate them — but it puts the
     word's baseline level with the bottom of the mark, and the mark is 1.86x the
     word's cap height, so all of that extra sits above and the word reads low.
     This lifts the word by exactly half the difference:
       mark ink   = 1.0159 x 288/305 = 0.95927em of --brand-size
       word caps  = 0.7344 x 0.70313 = 0.51638em   (Barlow's real cap height,
                                        measured off the font at 1000px — canvas
                                        quantises to whole px at UI sizes)
       rise       = (0.95927 - 0.51638) / 2 = 0.22144em of --brand-size
                  = 0.3015em of the word's own font-size, which is what `em`
                    means here.
     Recompute it if either size changes. `transform` and not a margin: this
     must not move the layout, only what the eye sees. */
  transform: translateY(-0.3015em);
  /* Barlow sets more openly than Inter and needs no negative tracking to stop
     looking loose; pulling it in here just closes up the counters. */
  letter-spacing: 0; white-space: nowrap;
}

/* Display type, deliberately off the UI type scale. A wordmark is read as a
   shape rather than as text, and these are the sizes at which the wordmark's
   capitals come out the same height as the mark beside them — which is the
   whole point of the ratio above. The mark itself lands at 16.7 / 19.1 / 28.6px,
   i.e. unchanged; it is the word that grew to meet it. */
.brand-lockup-xs { --brand-size: 21px; }
.brand-lockup-sm { --brand-size: 24px; }
.brand-lockup-lg { --brand-size: 36px; }

/* Micro-label. The one place uppercase is allowed. */
.nav-label {
  display: block; font-weight: 600; font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); margin: 0.72rem 0.5rem 0.2rem;
}
.sidebar nav { display: flex; flex-direction: column; gap: 2px; }
.nav-split { display: flex; gap: 2px; align-items: stretch; }
.nav-split > a:first-child { flex: 1; min-width: 0; }
.nav-plus {
  display: flex; align-items: center; justify-content: center; width: 30px;
  color: var(--muted); border-radius: var(--radius-ctl); text-decoration: none;
  transition: background 0.12s, color 0.12s;
}
.nav-plus svg { width: 14px; height: 14px; stroke-width: 2; }
.nav-plus:hover { background: var(--accent-soft); color: var(--accent); }

/* Nav item. Active = a soft tinted plate and accent ink. No border, no bar,
   no second highlight stacked on top of the first. */
/* Scaled up on 2026-08-24: at --fs-xs with 15px icons the rail read as fine
   print beside the content. Type is up ~17% (--fs-xs -> --fs-md, the body size)
   and the icons with it; the ROWS are not, because the 2026-08-21 pass shrank
   this rail to stop it outgrowing a laptop screen and that reason still holds.
   Straight +15% on everything measured 726px of content — 45px too tall for a
   768px laptop. Bigger words in a tighter rhythm buys the legibility without
   the scrollbar; measured after, it fits. Spend any further growth here on type,
   not on padding. */
.sidebar nav a {
  display: flex; align-items: center; gap: 0.6rem;
  color: var(--sidebar-ink); text-decoration: none;
  font-weight: 500; font-size: var(--fs-md); letter-spacing: -0.006em;
  padding: 0.33rem 0.6rem; border-radius: var(--radius-ctl);
  transition: background 0.12s, color 0.12s;
}
.sidebar nav a svg {
  width: 17px; height: 17px; flex-shrink: 0;
  color: var(--muted); stroke-width: 1.75; transition: color 0.12s;
}
.sidebar nav a:hover { background: var(--hover-bg); color: var(--ink); }
.sidebar nav a:hover svg { color: var(--ink-soft); }
.sidebar nav a.active {
  background: var(--sidebar-active); color: var(--accent); font-weight: 600;
}
.sidebar nav a.active svg { color: var(--accent); }

.sidebar-theme {
  margin-top: auto; padding: 0.65rem 0.5rem 0.45rem;
  display: flex; align-items: center; gap: 0.45rem;
}
.sidebar-theme label {
  margin: 0; font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
}
.sidebar-theme select {
  flex: 1; margin: 0; padding: 0.28rem 1.6rem 0.28rem 0.45rem; font-size: var(--fs-sm);
  background-color: var(--card); color: var(--ink);
  border: 1px solid var(--input-border); border-radius: var(--radius-ctl);
}
#theme-select:focus, #theme-select:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring);
}
.sidebar-foot {
  padding: 0.6rem 0.5rem 0.1rem; border-top: 1px solid var(--sidebar-line);
  display: flex; justify-content: space-between; align-items: center; gap: 0.45rem;
  font-size: var(--fs-xs);
}
.sidebar-foot a { color: var(--ink); text-decoration: none; font-weight: 600; }
.sidebar-foot a:hover { color: var(--accent); }
.sidebar-foot .linklike { font-size: var(--fs-xs); }
.linklike {
  background: none; border: none; color: var(--muted); cursor: pointer;
  font: inherit; font-size: var(--fs-sm); font-weight: 500; padding: 0;
}
.linklike:hover { color: var(--accent); }

/* width:100% is load-bearing. `flex: 1` sized this along the main axis, which
   was the WIDTH while <body> was the row flex container. Wrapping it in
   .app-shell (a column, added with the top bar) made the main axis vertical, so
   the width fell back to auto — and an auto cross-size with `margin: 0 auto`
   means the box shrink-wraps its contents instead of stretching. Every page has
   been sized to its content since, which only showed up once a reader page was
   given a wider ceiling and refused to use it. */
.container { flex: 1; width: 100%; max-width: 1360px; margin: 0 auto; padding: 1.5rem 1.7rem 3rem; min-width: 0; }

/* ---- Top bar, profile panel & nav drawer ----
   One bar at every width, sitting above the page inside .app-shell. It is
   OUTSIDE <main>, the only region in-app navigation replaces, so it is built
   once per full load and simply stays — no re-binding, no listener leak. */
.app-shell { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  display: flex; align-items: center; gap: 0.5rem;
  position: sticky; top: 0; z-index: 60;
  background: var(--sidebar-bg); border-bottom: 1px solid var(--sidebar-line);
  padding: 0.4rem 0.7rem; min-height: var(--chrome-h);
}
.topbar-lead { display: flex; align-items: center; gap: 0.6rem; }
/* Above the drawer breakpoint the rail carries the brand and there is no menu
   button, so the whole left group goes and the actions sit alone on the right. */
.topbar-lead { display: none; }
/* margin-left:auto is what holds these against the right edge — nothing else in
   the bar takes the slack. (A centred search field lived here briefly and did;
   it was moved back to the rail.) */
.topbar-actions { display: flex; align-items: center; gap: 0.25rem; margin-left: auto; }
/* Whose books these are. Quiet — it is context, not a control — and it truncates
   rather than pushing the icons around, since shop names are free text. */
.topbar-org {
  font-size: var(--fs-sm); font-weight: 500; color: var(--muted);
  margin-right: 0.45rem; max-width: 22ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: var(--radius-ctl);
  color: var(--sidebar-ink); text-decoration: none;
}
.topbar-btn svg { width: 18px; height: 18px; }
.topbar-btn:hover { background: var(--hover-bg); color: var(--ink); }
.topbar-btn.active { background: var(--sidebar-active); color: var(--accent); }

/* The circle. Initials rather than a silhouette: in a shop of four people the
   initials say WHO is signed in, which is the question a shared terminal
   actually raises. */
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; margin-left: 0.3rem;
  border: none; cursor: pointer; padding: 0;
  background: var(--accent); color: var(--accent-ink, #fff);
  font: 600 var(--fs-xs)/1 var(--font-body); letter-spacing: 0.02em;
}
.avatar:hover { background: var(--accent-hover); }
.avatar-lg { width: 40px; height: 40px; font-size: var(--fs-sm); cursor: default; margin: 0; }
.avatar-xl { width: 56px; height: 56px; font-size: var(--fs-lg); cursor: default; margin: 0; flex: none; }

/* ---- My account ---- */
.acct-identity {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4);
}
.acct-name { font-size: var(--fs-lg); }
/* Pushed to the far end rather than sitting against the email: role is a fact
   about the account, not part of the person's name. */
.acct-identity-tags { margin-left: auto; display: flex; gap: 0.4rem; flex-wrap: wrap; }
/* Label above value, not beside it: the values here are timestamps of varying
   length, and a two-column dl would leave a ragged gutter down the card. */
.acct-facts { margin: 0 0 var(--sp-3); display: grid; gap: 0.15rem; }
.acct-facts dt { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.acct-facts dd { margin: 0 0 0.55rem; font-size: var(--fs-sm); }
.acct-activity-head { margin: var(--sp-5) 0 0.3rem; }
/* Two independent columns rather than a row grid. In a row grid the short
   Appearance card shares a row with the tall Security card and leaves a hole
   punched in the middle of the page; stacked columns push the leftover space
   to the bottom, where it reads as the end of the page instead. */
.acct-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); align-items: start; }
.acct-col { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }
@media (max-width: 1000px) { .acct-grid { grid-template-columns: 1fr; } }
@media (max-width: 560px) {
  .acct-identity { flex-wrap: wrap; }
  .acct-identity-tags { margin-left: 0; width: 100%; }
}

.profile-scrim { position: fixed; inset: 0; z-index: 90; background: rgba(16, 22, 31, 0.35); }
.profile-panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 95;
  width: min(320px, 88vw); display: flex; flex-direction: column;
  background: var(--card); border-left: 1px solid var(--line);
  box-shadow: -12px 0 40px rgba(16, 24, 40, 0.18);
  padding: 0.9rem 0.9rem 1rem;
  /* `hidden` still applies while this is a flex container, so the attribute is
     what opens and closes it — no class to fall out of step with it. */
}
.profile-panel[hidden] { display: none; }
.profile-head { display: flex; align-items: center; gap: 0.65rem; margin-bottom: 0.9rem; }
.profile-who { min-width: 0; display: flex; flex-direction: column; }
.profile-who strong { font-size: var(--fs-md); }
.profile-who span { font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; }
.profile-close {
  margin-left: auto; background: none; border: none; cursor: pointer; padding: 0.3rem;
  color: var(--muted); border-radius: var(--radius-ctl); width: auto; min-height: 0;
}
.profile-close svg { width: 18px; height: 18px; display: block; }
.profile-close:hover { background: var(--hover-bg); color: var(--ink); }

.profile-links { display: flex; flex-direction: column; gap: 2px; }
.profile-links > a, .profile-links > button {
  display: flex; align-items: center; gap: 0.6rem; width: 100%;
  padding: 0.5rem 0.55rem; border-radius: var(--radius-ctl);
  font-size: var(--fs-sm); color: var(--ink); text-decoration: none;
  background: none; border: none; cursor: pointer; text-align: left; min-height: 0;
  font-family: inherit;
}
.profile-links > a svg, .profile-links > button svg {
  width: 17px; height: 17px; flex: none; color: var(--muted);
}
.profile-links > a:hover, .profile-links > button:hover { background: var(--hover-bg); }

.profile-fallback {
  margin: 0.15rem 0 0.35rem; padding: 0.55rem 0.6rem;
  background: var(--accent-soft); border-radius: var(--radius-ctl); font-size: var(--fs-xs);
}
.profile-fallback[hidden] { display: none; }
.profile-fallback p { margin: 0 0 0.4rem; }
.profile-fallback p:last-child { margin: 0.4rem 0 0; }
.profile-address { display: flex; align-items: center; gap: 0.4rem; }
.profile-address code { font-size: var(--fs-xs); word-break: break-all; }

.profile-theme { margin-top: auto; padding-top: 0.9rem; }
.profile-theme label {
  display: block; font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--muted); margin-bottom: 0.2rem;
}
.profile-signout { margin-top: 0.6rem; }
.profile-signout .btn { display: flex; align-items: center; justify-content: center; gap: 0.5rem; }
.profile-signout svg { width: 16px; height: 16px; }

/* Plain text as typed — a blank line stays a blank line without asking whoever
   writes the help page to learn any markup. */
.help-body { white-space: pre-wrap; font-size: var(--fs-md); line-height: 1.6; }

.sidebar-backdrop { display: none; }

@media (max-width: 840px) {
  body.with-sidebar { display: block; }
  /* Below this width the rail is a drawer, so the bar takes over the menu
     button and the brand — which leaves no room for the shop's name as well,
     and the name is the least of the three. */
  .topbar-lead { display: flex; }
  .topbar-org { display: none; }

  .mobile-topbar {
    display: flex; align-items: center; gap: 0.65rem;
    position: sticky; top: 0; z-index: 60;
    background: var(--sidebar-bg); padding: 0.45rem 0.7rem;
    border-bottom: 1px solid var(--sidebar-line);
  }
  .menu-toggle {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex-shrink: 0;
    background: none; border: none; border-radius: var(--radius-ctl);
    color: var(--ink-soft); cursor: pointer;
  }
  .menu-toggle svg { width: 21px; height: 21px; stroke-width: 1.85; }
  .menu-toggle:active { background: var(--hover-bg); }
  .topbar-brand {
    /* No ground and no radius. --brand-plate is transparent now, but leaving the
       radius here would still draw a rounded chip the moment anyone gave that
       token a color again — the exact shape that was taken off the sidebar. */
    display: block; background: transparent; padding: 0.25rem 0.5rem; line-height: 0;
  }
  .topbar-brand .brand-lockup { color: var(--ink); }

  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 80;
    width: min(290px, 86vw); height: auto;
    transform: translateX(-105%); transition: transform 0.2s ease;
    box-shadow: 0 0 40px rgba(16, 24, 40, 0.28);
  }
  body.nav-open .sidebar { transform: translateX(0); }
  .sidebar-backdrop { position: fixed; inset: 0; z-index: 70; background: rgba(16, 22, 31, 0.45); }
  body.nav-open .sidebar-backdrop { display: block; }
  body.nav-open { overflow: hidden; }
  /* The drawer is a touch target, not a space problem — it scrolls, and a
     finger needs more than 12px of text to aim at. Keep the old size here. */
  .sidebar nav a { padding: 0.58rem 0.6rem; font-size: var(--fs-md); }
  .sidebar nav a svg { width: 17px; height: 17px; }
  .container { padding: 1rem 0.9rem 2.5rem; }
}

/* ---- Surfaces ---- */
/* A card nested inside a card drew a second frame around the first's padding.
   The inner one keeps its separation from a hairline instead. */
.card .card { border-color: var(--line-soft); }
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-4) 1.125rem; margin-bottom: var(--sp-4); box-shadow: var(--shadow);
}
table.card { padding: 0; border-collapse: separate; border-spacing: 0; width: 100%; overflow: hidden; }

/* ---- Tables ---- */
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
thead th {
  font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap;
  color: var(--muted); background: var(--thead-bg);
  border-bottom: 1px solid var(--line);
}
tbody tr { transition: background 0.1s; }
tbody tr:hover { background: var(--hover-bg); }
tbody tr:last-child td { border-bottom: none; }
tfoot th { border-top: 1px solid var(--line); background: var(--thead-bg); color: var(--ink); font-weight: 600; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Short, predictable values that cost a whole extra row-height when they wrap. */
td[data-label="Vehicle"], td[data-label="Type"], td[data-label="On hand"] { white-space: nowrap; }
/* Legacy imported document numbers ("INV-LARRYHMILLERCOL-07-15-26-A") are long
   enough on their own to widen the whole table and squeeze every other column.
   An explicit width holds the column at the size a normal number needs; the few
   legacy rows wrap inside it instead of making the other ten columns pay.
   min-width (not width) is what holds it: `anywhere` drops the column's
   min-content width to one character, and table auto-layout will then squeeze
   the column to nothing when the other columns want the room. */
td.stack-title, th.sortable:nth-child(2) { min-width: 7rem; }
td.stack-title a { overflow-wrap: anywhere; }
tr.overdue td { color: var(--danger); }
.row-actions { white-space: nowrap; }
.checkcol { width: 1.9rem; text-align: center; }
.checkcol input { margin: 0; }

/* ---- Page furniture ---- */
.card-head, .card > h2:first-child {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  min-height: var(--control-h-sm); margin: 0 0 0.7rem;
}
.card-head h2 { margin: 0; }
/* h1 kept its own bottom margin inside a centred flex row, which pushed the
   heading's optical centre below the buttons on its right. The row owns the
   spacing now, so every page's title and actions sit on one baseline. */
.page-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.6rem; margin-bottom: var(--sp-4); }
.page-head h1 { margin: 0; }
.actions { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }

/* ---- Buttons: one rectangle, one radius, three roles ----
   Same radius at every size: a small button is a short button, not a
   differently-shaped one. Depth is a single hairline shadow; pressing a
   button drops that shadow rather than moving the button. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  min-height: var(--control-h); padding: 0.35rem 0.85rem;
  border-radius: var(--radius-ctl);
  border: 1px solid var(--input-border); background: var(--card); color: var(--ink);
  cursor: pointer; text-decoration: none;
  font-family: var(--font-body); font-size: var(--fs-md); font-weight: 500;
  letter-spacing: -0.006em; line-height: 1.35;
  box-shadow: var(--shadow-ctl);
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
}
.btn:hover { background: var(--hover-bg); border-color: var(--input-hover); }
.btn:active { background: var(--line-soft); border-color: var(--input-hover); }
.btn.primary {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
  font-weight: 600;
}
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn.primary:active { background: var(--accent-hover); filter: brightness(0.94); }
.btn.danger { color: var(--danger); border-color: var(--danger-line); background: var(--card); }
.btn.danger:hover { background: var(--danger-soft); border-color: var(--danger); }
.btn.small {
  min-height: var(--control-h-sm); padding: 0.2rem 0.6rem;
  font-size: var(--fs-sm); border-radius: var(--radius-ctl);
}
.btn.large {
  min-height: var(--control-h-lg); padding: 0.55rem 1.25rem;
  font-size: var(--fs-md); letter-spacing: -0.004em;
}
.btn:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, button:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.inline { display: inline; }
/* An inline form holding a field and its button inside a table cell: keep the
   two on one line, or the button drops under the box and doubles the row. */
td > form.inline { display: inline-flex; gap: 0.4rem; align-items: center; width: 100%; }
td > form.inline input { margin: 0; min-width: 0; }

/* ---- Accessibility utilities ---- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 0.5rem; top: -4rem; z-index: 200;
  background: var(--accent); color: var(--accent-ink); padding: 0.55rem 0.95rem;
  border-radius: var(--radius-ctl); font-weight: 600; text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus { top: 0.5rem; outline: 2px solid var(--accent); outline-offset: 2px; }
main:focus { outline: none; }
/* The rail is light now, so it takes the same accent focus ring as the page. */
.sidebar a:focus-visible, .sidebar button:focus-visible, .sidebar select:focus-visible,
.mobile-topbar a:focus-visible, .mobile-topbar button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

.muted { color: var(--muted); }
.prewrap { white-space: pre-wrap; }

/* ---- Status tags ---- */
.tag {
  display: inline-block; font-family: var(--font-body); font-weight: 600;
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em;
  padding: 0.1rem 0.4rem; border-radius: var(--radius-sm); line-height: 1.5;
  background: var(--line-soft); color: var(--muted);
  border: 1px solid transparent;
  text-decoration: none; vertical-align: 0.08em; white-space: nowrap;
}
.tag-open, .tag-sent { background: var(--ok-soft); color: var(--ok-ink); }
.tag-paid, .tag-converted, .tag-accepted { background: var(--info-soft); color: var(--info-ink); }
.tag-void, .tag-expired { background: var(--danger-soft); color: var(--danger); }
.tag-overdue { background: var(--danger-soft); color: var(--danger); }
.tag-warn { background: var(--warn-soft); color: var(--warn); }
/* Plain good/bad, for tags that aren't naming a document status: the ordering
   panel says "arrived" and "not ordered", neither of which is a status word. */
.tag-ok { background: var(--ok-soft); color: var(--ok-ink); }
.tag-danger { background: var(--danger-soft); color: var(--danger); }
.tag.role-admin { background: var(--info-soft); color: var(--info-ink); }
.tag.role-staff { background: var(--line-soft); color: var(--muted); }

.settings-narrow { max-width: var(--measure-narrow); }
/* A report's summary card: narrower than the full column so a two-column list
   of figures is readable, but on the app's shared list measure rather than
   borrowing the settings one. */
.card.report-summary { max-width: var(--measure-list); }
/* A line of prose needs a measure. These were running the full 1360px column. */
.form-hint { font-size: var(--fs-sm); margin: 0.5rem 0 0; color: var(--muted); max-width: 78ch; }
.page-head + p.muted, .card > p.muted { max-width: 78ch; }
/* Pins the "⋯" menu to the right-hand end of the row it lands on. */
/* NOTE: this rule loses to `.tb-menu { margin: 0 }` 500 lines below — same
   specificity, later wins — so it does nothing. Left as-is deliberately: the
   "⋯" sits next to the buttons it belongs to, which is where it has always
   been. Do not "fix" it without being asked. */
.tb-menu-end { margin-left: auto; }
.danger-zone {
  display: flex; gap: 0.5rem; align-items: center; margin-top: 1rem;
  padding-top: 1rem; border-top: 1px solid var(--line);
}

/* Users & roles table */
.user-table td, .user-table th { vertical-align: middle; }
.user-table .col-actions { text-align: right; width: 1%; white-space: nowrap; }
.row-tools { display: inline-flex; gap: 0.4rem; align-items: center; justify-content: flex-end; }
.row-tools .btn.small,
.row-tools .tb-menu > summary {
  display: inline-flex; align-items: center; justify-content: center; line-height: 1;
}

/* Live password-requirement checklist */
.pw-reqs { list-style: none; margin: 0.45rem 0 0.2rem; padding: 0; font-size: var(--fs-sm); }
.pw-reqs li { color: var(--muted); padding-left: 1.35rem; position: relative; line-height: 1.75; }
.pw-reqs li::before { content: "○"; position: absolute; left: 0.15rem; }
.pw-reqs li.ok { color: var(--ok-ink); }
.pw-reqs li.ok::before { content: "✓"; left: 0; font-weight: 600; }
.sp-modal-error { margin: 0 0 0.9rem; }

.pw-reveal { display: flex; gap: 0.5rem; align-items: center; margin: 0.7rem 0 0.2rem; }
.pw-reveal code {
  flex: 1; font-family: var(--font-mono); font-size: var(--fs-lg); letter-spacing: 0.01em; user-select: all;
  padding: 0.5rem 0.7rem; background: var(--bg-soft);
  border: 1px solid var(--line); border-radius: var(--radius-ctl);
}

/* ---- Flashes: tinted panel, one hairline, no stripe stack ---- */
.flash {
  padding: 0.65rem 0.9rem; border-radius: var(--radius-ctl); margin-bottom: 0.9rem;
  font-size: var(--fs-md); border: 1px solid var(--line); background: var(--bg-soft); color: var(--ink);
}
.flash-success { background: var(--ok-soft); color: var(--ok-ink); border-color: transparent; }
.flash-error { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.flash-info { background: var(--info-soft); color: var(--info-ink); border-color: transparent; }

/* ---- Emailing a document ---- */
.email-compose { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 1rem; align-items: start; }
@media (max-width: 900px) { .email-compose { grid-template-columns: 1fr; } }
.email-compose .form-stack > label { display: block; margin-bottom: 0.8rem; }
.email-compose input[type="email"], .email-compose input[name="subject"],
.email-compose select, .email-compose textarea { width: 100%; }
.email-attach {
  display: flex; align-items: center; gap: 0.45rem; padding: 0.6rem 0.75rem;
  border: 1px dashed var(--input-border); border-radius: var(--radius-ctl);
  background: var(--bg-soft); font-size: var(--fs-md);
}
.email-clip svg { width: 16px; height: 16px; display: block; color: var(--muted); }
.email-side h2 { margin-top: 0; }

.comm-list { list-style: none; margin: 0 0 0.8rem; padding: 0; }
.comm-list li { border-bottom: 1px solid var(--line-soft); }
.comm-list li:last-child { border-bottom: 0; }
.comm-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.15rem; cursor: pointer; }
.comm-row input[type="radio"], .comm-row input[type="checkbox"] { margin: 0; flex: none; }
/* The list carries itself; + sits to its right rather than under it. Ticking a
   box saves on its own, so there is no Save button to line up with. */
.comm-panel { display: flex; align-items: flex-start; gap: 0.8rem; }
.comm-panel .comm-list { flex: 1; min-width: 0; margin-bottom: 0; }
.comm-saved { font-size: var(--fs-sm); }
.comm-add-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--control-h); min-width: var(--control-h); padding: 0;
}
.comm-add-btn svg { width: 16px; height: 16px; }
.email-to-set { border: 0; padding: 0; margin: 0 0 0.9rem; }
.email-to-set legend { padding: 0; font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em; }
.comm-email { font-weight: 500; }
.comm-label { font-size: var(--fs-sm); color: var(--muted); }
.comm-add { margin-top: 0.8rem; }

/* Notice panels: tinted background, single hairline, no left stripe. */
.email-delay {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  padding: 0.6rem 0.8rem; border: 1px solid transparent;
  border-radius: var(--radius-ctl); background: var(--info-soft); color: var(--info-ink);
  font-size: var(--fs-md);
}
.email-clock svg { width: 16px; height: 16px; display: block; color: var(--info-ink); }
.queued-list { list-style: none; margin: 0 0 1rem; padding: 0; }
.queued-list li {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; flex-wrap: wrap; padding: 0.55rem 0.8rem; margin-bottom: 0.4rem;
  border: 1px solid transparent; border-radius: var(--radius-ctl);
  background: var(--info-soft); color: var(--info-ink); font-size: var(--fs-md);
}
.po-draft-nudge {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin: 0.9rem 1rem 0; padding: 0.75rem 0.95rem;
  border: 1px solid transparent; border-radius: var(--radius-ctl); background: var(--info-soft);
}
.po-draft-nudge .po-draft-msg { display: flex; flex-direction: column; gap: 0.15rem; }
.po-draft-nudge .po-draft-msg strong { color: var(--info-ink); font-weight: 600; }
.po-draft-nudge .po-draft-msg span { color: var(--muted); font-size: var(--fs-sm); }

.credit-warning {
  margin: 0 0 1rem; padding: 0.75rem 0.95rem; border: 1px solid var(--danger-line);
  border-radius: var(--radius-ctl); background: var(--danger-soft);
}
.credit-warning strong { color: var(--danger); font-weight: 600; }
.credit-warning span { display: block; margin-top: 0.15rem; color: var(--danger); opacity: 0.85; font-size: var(--fs-sm); }

.auth-card {
  max-width: 400px; margin: 9vh auto; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 2rem;
  box-shadow: var(--shadow-md);
}

/* ============================================================
   Sign in — a white card on a deep slate field.
   ============================================================ */
body.no-sidebar .container { max-width: none; margin: 0; padding: 0; }
.signin {
  min-height: 100vh; min-height: 100svh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1.5rem; padding: 2.5rem 1.25rem;
  background:
    radial-gradient(90% 55% at 50% 0%, rgba(255, 255, 255, 0.07), transparent 70%),
    var(--auth-bg);
}
.signin-card {
  width: 100%; max-width: 404px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 2.25rem 2rem 1.75rem;
  box-shadow: 0 24px 50px -20px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.18);
}
.signin-logo { display: flex; justify-content: center; margin: 0 0 1.75rem; }
.signin-card h1 {
  font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.022em;
  margin: 0 0 0.35rem; text-align: center;
}
.signin-sub { margin: 0 0 1.75rem; text-align: center; color: var(--muted); font-size: var(--fs-md); }

.signin-field { margin-bottom: 0.95rem; }
.signin-form label {
  display: block; margin: 0 0 0.35rem; font-size: var(--fs-sm); font-weight: 500;
  letter-spacing: -0.004em; color: var(--ink-soft);
}
.signin-form input {
  margin-top: 0; min-height: var(--control-h-lg); padding: 0.6rem 0.75rem;
  font-size: var(--fs-md);
  border-radius: var(--radius-ctl); border-color: var(--input-border);
}
.signin-form input::placeholder { color: var(--muted); opacity: 0.6; }
.signin-form input:-webkit-autofill {
  -webkit-text-fill-color: var(--ink);
  box-shadow: 0 0 0 100px var(--input-bg) inset;
}
.signin-form input:-webkit-autofill:focus {
  box-shadow: 0 0 0 100px var(--input-bg) inset, 0 0 0 3px var(--focus-ring);
}

.signin-input-wrap { position: relative; display: flex; align-items: center; }
.signin-input-wrap input { padding-right: 2.8rem; }
.signin-reveal {
  position: absolute; right: 0.3rem; width: 2rem; height: 2rem; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radius-ctl); background: none; color: var(--muted); cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.signin-reveal:hover { color: var(--accent); background: var(--hover-bg); }
.signin-reveal svg { width: 17px; height: 17px; }
.signin-reveal .icon-off { display: none; }
.signin-reveal.is-on .icon-on { display: none; }
.signin-reveal.is-on .icon-off { display: block; }

.signin-submit {
  width: 100%; margin-top: 0.9rem; min-height: var(--control-h-lg); padding: 0.6rem;
  font-size: var(--fs-md); font-weight: 600; border-radius: var(--radius-ctl); letter-spacing: -0.004em;
}

.signin-alert {
  display: flex; gap: 0.5rem; align-items: flex-start;
  margin: 0 0 1.1rem; padding: 0.65rem 0.75rem; border-radius: var(--radius-ctl);
  font-size: var(--fs-md); line-height: 1.45;
  border: 1px solid transparent; background: var(--info-soft); color: var(--info-ink);
}
.signin-alert.is-error { border-color: var(--danger-line); background: var(--danger-soft); color: var(--danger); }
.signin-alert svg { width: 16px; height: 16px; flex: none; margin-top: 0.1rem; }

.signin-help {
  margin: 1.5rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--line-soft);
  text-align: center; font-size: var(--fs-sm); color: var(--muted);
}
/* Footer sits on the dark ground, so it takes its own light ink. */
.signin-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.5rem; font-size: var(--fs-sm); color: rgba(255, 255, 255, 0.55);
}
.signin-foot a { color: rgba(255, 255, 255, 0.72); text-decoration: none; }
.signin-foot a:hover { color: #fff; text-decoration: underline; }
.signin-secure { display: inline-flex; align-items: center; gap: 0.35rem; }
.signin-secure svg { width: 13px; height: 13px; }
.signin-sep { opacity: 0.35; }

@media (max-width: 420px) {
  .signin { padding: 1.75rem 1rem; gap: var(--sp-4); }
  .signin-card { padding: 1.8rem 1.35rem 1.4rem; border-radius: var(--radius); }
  .signin-logo { margin-bottom: 1.4rem; }
  .signin-card h1 { font-size: var(--fs-xl); }
}

/* ---- Forms ---- */
label { display: block; margin-bottom: 0.65rem; font-size: var(--fs-sm); font-weight: 500; color: var(--ink-soft); }
label.check { display: flex; gap: 0.45rem; align-items: center; font-weight: 400; color: var(--ink); }
/* Two columns need room; inside the document builder's narrow rail the labels
   ("Payment terms (COD, Net 10, …)") wrapped over three lines and the boxes
   stopped lining up. One column there, and the box hangs from the first line so
   a wrapped label still starts level with its checkbox. */
.check-list { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem 1.1rem; margin-top: 0.2rem; }
.check-list label.check { align-items: flex-start; }
.check-list label.check input { margin-top: 0.15rem; }
.builder2-controls .check-list { grid-template-columns: 1fr; gap: 0.4rem; }
.check-list label.check { margin: 0; }
.check-list .check-wide { grid-column: 1 / -1; }
@media (max-width: 560px) { .check-list { grid-template-columns: 1fr; } }
[hidden] { display: none !important; }

/* Fields carry the shared control height, so a field, a button and a combobox
   in the same row line up exactly. Kept at element specificity (not behind a
   :not() chain) so any component rule can still size its own control. */
input, select, textarea {
  width: 100%; min-height: var(--control-h); padding: 0.4rem 0.6rem; margin-top: 0.18rem;
  border: 1px solid var(--input-border); border-radius: var(--radius-ctl);
  font: inherit; font-size: var(--fs-md); font-weight: 400; line-height: 1.35;
  background: var(--input-bg); color: var(--ink);
  transition: border-color 0.12s, box-shadow 0.12s;
}
input:hover, select:hover, textarea:hover { border-color: var(--input-hover); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
input::placeholder, textarea::placeholder { color: var(--muted); opacity: 0.7; }
input[type="checkbox"], input[type="radio"] { width: auto; min-height: 0; accent-color: var(--accent); }
input[type="file"] { border: none; padding-left: 0; background: none; min-height: 0; }
/* The UA's picker indicator carries its own padding, which pushed these ~2px
   taller than every other field. Pin them to the control height exactly. */
input[type="date"], input[type="datetime-local"], input[type="time"], input[type="month"] {
  height: var(--control-h);
}
select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 1.9rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7480' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.6rem center;
}
select::-ms-expand { display: none; }
.narrow select, select.narrow { padding-right: 1.7rem; background-position: right 0.45rem center; }
input.narrow { max-width: 130px; }
td.narrow input { min-width: 60px; }
button { font: inherit; }

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); align-items: start; }
.form-grid fieldset {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.9rem 1rem; margin: 0; background: var(--bg-soft);
}
.form-grid .full { grid-column: 1 / -1; }
.form-stack label { margin-bottom: 0.6rem; }
.form-actions { display: flex; gap: 0.6rem; }
legend {
  font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
  padding: 0 0.35rem;
}

/* ---- Client form — the tabbed customer sheet ----
   Label left, answer right, one column of controls at a fixed measure: a form
   of mixed-width fields reads as a list of answers instead of a ragged grid.
   Everything past "who is this" lives behind the tabs. */
.cf-head { padding-bottom: 0.3rem; }
.cf-row {
  display: grid; grid-template-columns: 190px minmax(0, 1fr);
  gap: 0.35rem 1rem; align-items: center; padding: 0.28rem 0; max-width: var(--measure-form);
}
/* Beats the display above, which would otherwise override [hidden]. */
.cf-row[hidden], .cf-pane[hidden] { display: none; }
.cf-label { margin: 0; font-size: var(--fs-sm); font-weight: 500; color: var(--ink-soft); }
.cf-sub { display: block; font-size: var(--fs-2xs); font-weight: 400; }
.cf-ctl { min-width: 0; }
.cf-ctl > input, .cf-ctl > select { margin: 0; width: 100%; max-width: 420px; }
.cf-split-3, .cf-split-2 { display: grid; gap: 0.5rem; }
.cf-split-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: 560px; }
.cf-split-2 { grid-template-columns: 1fr 1fr; }
.cf-split-3 > input, .cf-split-2 input { margin: 0; width: 100%; max-width: none; }

/* Radios as plain circles — the choice is two or three options wide, so it is
   shown in full rather than folded into a dropdown. */
.choice-row { display: flex; gap: 1.3rem; align-items: center; flex-wrap: wrap; }
label.choice {
  display: inline-flex; align-items: center; gap: 0.4rem; margin: 0;
  font-weight: 400; color: var(--ink); cursor: pointer;
}
label.choice input { margin: 0; flex: none; }

.cf-tabs {
  display: flex; gap: 0.15rem; flex-wrap: wrap;
  margin: 1.2rem 0 0; border-bottom: 1px solid var(--line);
}
.cf-tab {
  appearance: none; background: none; border: 0; border-bottom: 2px solid transparent;
  margin-bottom: -1px; padding: 0.5rem 0.85rem; cursor: pointer;
  font-size: var(--fs-sm); font-weight: 500; color: var(--muted);
  border-radius: var(--radius-ctl) var(--radius-ctl) 0 0;
}
.cf-tab:hover { color: var(--ink); background: var(--hover-bg); }
.cf-tab.is-on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.cf-pane { padding: 1.1rem 0 0.2rem; }
.cf-pane > .muted { margin: 0 0 0.9rem; font-size: var(--fs-sm); }

.cf-addr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.4rem; max-width: var(--measure-list); }
.cf-addr h3 {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  margin: 0 0 0.7rem; font-size: var(--fs-sm);
}
.cf-addr h3 .muted { font-weight: 400; }
.cf-addr label { display: block; margin-bottom: 0.6rem; }
.cf-addr .cf-split-2 label { margin-bottom: 0; }
.cf-hint { margin: 0.5rem 0 0; font-size: var(--fs-xs); }

/* One row per contact / vehicle, with its own controls on the right. */
.cf-rows { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.9rem; max-width: var(--measure-list); }
.cf-child {
  display: flex; gap: 0.6rem; align-items: flex-end;
  padding: 0.6rem 0.7rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--bg-soft);
}
.cf-contact { flex-wrap: wrap; }
.cf-child-field { display: flex; flex-direction: column; gap: 0.15rem; flex: 1 1 130px; margin: 0; }
.cf-child-field input { margin: 0; width: 100%; max-width: none; }
.cf-child-label {
  font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--muted);
}
.cf-child-edit, .cf-child-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: var(--control-h); padding: 0; flex: none;
  border: 0; background: none; color: var(--muted); cursor: pointer;
  border-radius: var(--radius-ctl); line-height: 1;
}
.cf-child-edit svg { width: 0.95rem; height: 0.95rem; }
.cf-child-edit:hover { color: var(--accent); background: var(--hover-bg); }
.cf-child-x:hover { color: var(--danger); background: var(--hover-bg); }
.cf-vehicle { align-items: center; }
.cf-vehicle-text { display: flex; flex-direction: column; gap: 0.1rem; flex: 1; min-width: 0; font-size: var(--fs-md); }
.cf-vehicle-text .muted { font-size: var(--fs-xs); }
.cf-empty { margin: 0.1rem 0; font-size: var(--fs-sm); }
.cf-notes-label textarea { max-width: var(--measure-form); }
.client-form { max-width: 60rem; }
.client-form .form-actions { margin-top: 1.3rem; padding-top: 1rem; border-top: 1px solid var(--line-soft); }
@media (max-width: 640px) {
  .cf-row { grid-template-columns: 1fr; }
  .cf-split-3 { grid-template-columns: 1fr; }
}

.searchbar { display: flex; gap: 0.55rem; margin-bottom: 0.9rem; align-items: end; flex-wrap: wrap; }
.searchbar select { max-width: 190px; }
.searchbar label { margin: 0; }
.search-field { position: relative; display: flex; align-items: center; flex: 1 1 260px; max-width: 360px; }
.search-field input[type="search"] { width: 100%; max-width: none; margin: 0; padding-right: 2.3rem; }
.search-go {
  position: absolute; right: 0; top: 0; bottom: 0; width: 2.3rem; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; color: var(--muted); cursor: pointer;
}
.search-go:hover { color: var(--accent); }
.search-go svg { width: 16px; height: 16px; }

.bulk-bar[hidden] { display: none; }
.bulk-bar {
  display: flex; gap: 0.55rem; align-items: center; flex-wrap: wrap;
  background: var(--accent-soft); border: 1px solid transparent;
  border-radius: var(--radius-ctl); padding: 0.55rem 0.85rem; margin-bottom: 0.9rem;
}
.bulk-bar .muted {
  font-weight: 600; font-size: var(--fs-sm); color: var(--accent);
  text-transform: none; letter-spacing: 0;
}
/* A per-row Edit button repeated down 25 rows is a column of buttons, not a
   column of data: it out-shouts the record it belongs to and sets the row
   height. Quiet by default, outlined when the row is under the pointer. */
td.stack-actions { width: 1%; white-space: nowrap; text-align: right; }
td.stack-actions .btn.small {
  border-color: transparent; background: none; color: var(--muted);
}
tbody tr:hover td.stack-actions .btn.small {
  border-color: var(--input-border); background: var(--card); color: var(--ink);
}
/* Same reasoning for an in-table delete: a column of red-outlined buttons reads
   as a column of warnings. It goes red under the cursor, where the decision is. */
td > .btn.small.danger, td > form.inline > .btn.small.danger {
  border-color: transparent; background: none; color: var(--muted);
}
td > .btn.small.danger:hover, td > form.inline > .btn.small.danger:hover {
  border-color: var(--danger-line); background: var(--danger-soft); color: var(--danger);
}

/* Icon-only actions: square, quiet until hovered, with the label carried by
   title/aria-label so the bar stays uncluttered. */
.bulk-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--control-h); height: var(--control-h); padding: 0;
  border: 1px solid transparent; border-radius: var(--radius-ctl);
  background: none; color: var(--accent); cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.bulk-btn svg { width: 17px; height: 17px; }
.bulk-btn:hover { background: var(--card); border-color: var(--accent); }
.bulk-btn:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.bulk-btn[hidden] { display: none; }
.bulk-btn.danger { color: var(--danger); }
.bulk-btn.danger:hover { background: var(--danger-soft); border-color: var(--danger); }
/* Delete sits apart from the everyday actions so it isn't hit by muscle memory. */
.bulk-bar .bulk-btn.danger { margin-left: auto; }

/* ---- List toolbar + client merge review ---- */
.list-toolbar { margin: 0 0 0.75rem; display: flex; gap: 0.5rem; align-items: center; }
.merge-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin: 1rem 0 1.25rem; }
.merge-card {
  display: block; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1rem; cursor: pointer; background: var(--card);
  transition: border-color 0.12s, box-shadow 0.12s, background 0.12s;
}
.merge-card:hover { border-color: var(--input-hover); }
.merge-card:has(input[type="radio"]:checked) {
  border-color: var(--accent); background: var(--accent-soft);
  box-shadow: 0 0 0 1px var(--accent);
}
.merge-pick { display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.5rem; font-weight: 600; }
.merge-name { font-size: var(--fs-lg); font-weight: 600; margin: 0.25rem 0 0.5rem; }
.merge-kv { margin: 0 0 0.5rem; }
.merge-stats {
  list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem 1rem;
  padding: 0.5rem 0 0; margin: 0.5rem 0 0; border-top: 1px solid var(--line-soft);
}
.merge-stats strong { color: var(--ink); font-weight: 600; }

/* ---- Whole-row clickable lists ---- */
table.rowlink tbody tr { cursor: pointer; }
table.rowlink tbody tr:hover td { background: var(--hover-bg); }

/* ---- Quote / invoice reader (list + preview) ---- */
/* ---- The split reader: list beside document ----
   The 1360px container cap is right for pages of prose and forms, where line
   length governs readability (the usual 45-75 characters). A reader is neither:
   the left column is a scannable list and the right is a sheet of paper. Capping
   it just parks both in the middle of a wide monitor with several hundred pixels
   of nothing either side — measured at 2560px: 477px dead on each side.
   So reader pages get a wider ceiling, and the surplus is split usefully rather
   than poured into the grey around the sheet (see .reader below and the zoom
   steps in docs/preview.html). 1700px because past that the list stops earning
   width and the sheet has run out of zoom. */
body.reader-page .container { max-width: 1700px; }

/* ---- The main list pages ----
   Ten columns of facts, read across: number, date, who, which truck, what it
   came to, what is still owed, where it is. A measure meant for prose parks
   that in the middle of a wide screen with the ends of the rows nowhere near
   the edges, so these pages use the width they are given. Only the list pages
   opt in — nothing else in the app is a ten-column scan. */
body.list-page .container { max-width: none; }

/* Rows sized to be read at arm's length across a shop counter rather than to
   fit the most rows on screen. ONE class for every main list — quotes,
   invoices, clients, credits, payments — because the complaint that started
   this was that they did not match each other.

   --list-fs is not a token on purpose: the scale has nothing between --fs-md
   (14px) and --fs-lg (16px), 16 measured a shade heavy here, and this is that
   less 8%. Named once rather than sprinkled as a magic number in six rules. */
.list-lg {
  --list-fs: 0.92rem;        /* --fs-lg less 8% */
  --list-pad-y: 0.63rem;
  --list-pad-x: 0.64rem;
}
.list-lg td { padding: var(--list-pad-y) var(--list-pad-x); font-size: var(--list-fs); }
.list-lg thead th { font-size: var(--fs-xs); padding: 0.51rem var(--list-pad-x); }
/* The badges are read as part of the row, so they step up with it — left at
   11px they read as a footnote against the row's text. */
.list-lg .tag { font-size: var(--fs-xs); padding: 0.14rem 0.41rem; }

/* The list takes a share of the extra rather than a fixed 320px: at 320 the
   subtitle truncates mid-vehicle ("2026 Ford F-..."), which is exactly the
   detail you are scanning for. Clamped so it never gets so wide that a
   single-column list looks lost in it. */
.reader {
  display: grid; grid-template-columns: clamp(320px, 24%, 430px) 1fr;
  gap: 1rem; height: calc(100vh - 5.5rem); min-height: 460px;
}
.reader-list {
  display: flex; flex-direction: column; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--card); overflow: hidden; box-shadow: var(--shadow);
}
.reader-list-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--line-soft); }
/* The one tab control. Every tab strip in the app is this — see .item-tab,
   .rd-tab and .po-tabs, which are the same rule under other names. */
.reader-tabs { display: flex; gap: var(--sp-1); }
.reader-tab, .item-tab, .rd-tab, .po-tabs a {
  padding: 0.3rem 0.7rem; border-radius: var(--radius-ctl); font-weight: 500;
  font-size: var(--fs-sm); color: var(--muted); text-decoration: none;
  white-space: nowrap; transition: background 0.12s, color 0.12s;
}
.reader-tab:hover, .item-tab:hover, .rd-tab:hover, .po-tabs a:hover {
  background: var(--hover-bg); color: var(--ink);
}
.reader-tab.active, .item-tab.active, .rd-tab.active, .po-tabs a.active {
  background: var(--accent-soft); color: var(--accent); font-weight: 600;
}
.reader-search { position: relative; margin: 0; }
.reader-list > .reader-search { padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--line-soft); }
.reader-search input[type="search"] { width: 100%; margin: 0; }
.po-list > .reader-search { margin-bottom: 0.1rem; }
.reader-items { overflow-y: auto; flex: 1; }
.reader-item {
  display: block; padding: 0.6rem 0.7rem; border-bottom: 1px solid var(--line-soft);
  text-decoration: none; color: var(--ink); transition: background 0.1s;
}
.reader-item:hover { background: var(--hover-bg); }
.reader-item.selected { background: var(--accent-soft); }
.reader-item.selected .reader-item-row { color: var(--accent); }
.reader-item-row { display: flex; justify-content: space-between; gap: 0.5rem; font-weight: 600; font-size: var(--fs-md); }
.reader-item-client { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reader-item-amt { font-variant-numeric: tabular-nums; white-space: nowrap; }
.reader-item-sub {
  font-size: var(--fs-sm); color: var(--muted); margin: 0.15rem 0 0.35rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.reader-empty { padding: 1rem; color: var(--muted); }
.reader-main {
  display: flex; flex-direction: column; min-width: 0; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--card); overflow: hidden; box-shadow: var(--shadow);
}
.reader-toolbar {
  display: flex; flex-direction: row; align-items: center; justify-content: space-between;
  flex-wrap: nowrap; gap: 0.6rem; padding: 0.45rem 0.5rem;
  border-bottom: 1px solid var(--line-soft); background: var(--card);
}
.reader-title {
  display: flex; align-items: center; gap: 0.5rem; font-size: var(--fs-md);
  font-weight: 600; letter-spacing: -0.01em; padding-left: 0.35rem;
  flex: 1 1 0; min-width: 0;
}
/* A long product name used to push the toolbar's buttons onto a second row,
   stacking Edit above the "…" menu. The title gives way instead. */
.reader-title strong, .reader-title span {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-width: 0;
}
.reader-toolbar .tb { flex: none; flex-wrap: nowrap; }

/* Document toolbar — text buttons, flat until hovered.
   `flex: 1 1 auto` matters more than it looks: without it the toolbar is only
   as wide as its buttons, so when they wrap onto a second row that row is short
   and the auto margin pinning "⋯" to the right has no free space to absorb. The
   menu then sat halfway across the bar and opened over the document behind it.
   Filling the width keeps every wrapped row full, so "⋯" stays at the right and
   its panel opens tucked under the corner. */
.tb { display: flex; align-items: center; flex-wrap: wrap; }
.tb-inline { display: contents; }
.tb form { margin: 0; }
.tb-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  height: 32px; padding: 0 0.65rem; box-sizing: border-box;
  background: none; border: 0; border-radius: var(--radius-ctl); color: var(--ink-soft);
  font: inherit; font-weight: 500; font-size: var(--fs-md); line-height: 1; cursor: pointer;
  text-decoration: none; white-space: nowrap; transition: background 0.12s, color 0.12s;
}
.tb-btn:hover { background: var(--hover-bg); color: var(--ink); }
.tb-btn svg { width: 16px; height: 16px; flex: none; color: var(--muted); stroke-width: 1.85; }
.tb-btn:hover svg { color: var(--ink-soft); }
.tb-btn .tb-caret { width: 14px; height: 14px; margin-left: -0.15rem; }
/* Destructive toolbar action (delete). Red only on hover/focus, so the button
   reads as available rather than as a standing warning. */
.tb-btn.danger { color: var(--danger); }
.tb-btn.danger svg { color: var(--danger); opacity: 0.8; }
.tb-btn.danger:hover, .tb-btn.danger:focus-visible { background: var(--danger-soft); color: var(--danger); }
.tb-btn.danger:hover svg { color: var(--danger); opacity: 1; }
.tb-div { width: 1px; height: 18px; flex: none; align-self: center; background: var(--line); margin: 0 0.25rem; }
.tb-more { font-size: var(--fs-lg); letter-spacing: 1px; padding: 0 0.55rem; }
.tb-menu { position: relative; display: inline-flex; align-items: center; margin: 0; }
.tb-menu > summary { list-style: none; margin: 0; }
.tb-menu > summary::-webkit-details-marker { display: none; }
.tb-menu[open] > summary { background: var(--hover-bg); }
.dash-new > summary { display: inline-flex; align-items: center; gap: 0.35rem; }
.dash-new > summary svg { width: 15px; height: 15px; }
.dash-new > summary .tb-caret { width: 13px; height: 13px; opacity: 0.9; }
.tb-menu[open] > summary.primary { background: var(--accent-hover); border-color: var(--accent-hover); }
.tb-pop {
  position: absolute; top: calc(100% + 5px); left: 0; z-index: 50; min-width: 194px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-pop); padding: 0.25rem; display: flex; flex-direction: column;
}
.tb-pop-right { left: auto; right: 0; }
.tb-pop form { display: block; }
.tb-pop a, .tb-item {
  display: flex; width: 100%; align-items: center; gap: 0.5rem;
  min-height: var(--control-h); padding: 0.45rem 0.6rem;
  border-radius: var(--radius-ctl); background: none; border: 0;
  color: var(--ink); font: inherit; font-size: var(--fs-md); line-height: 1.35; text-align: left;
  cursor: pointer; text-decoration: none; white-space: nowrap;
}
.tb-pop a:hover, .tb-item:hover { background: var(--hover-bg); }
.tb-item.danger { color: var(--danger); }
.tb-item.danger:hover { background: var(--danger-soft); }
.reader-pdf { flex: 1; min-height: 0; background: var(--paper-bg); }
.reader-pdf iframe { width: 100%; height: 100%; border: 0; display: block; }
/* Credits render their document as markup rather than a PDF, so the pane
   scrolls itself instead of handing off to an embedded viewer. */
.reader-doc { flex: 1; min-height: 0; overflow-y: auto; background: var(--paper-bg); padding: 1.2rem; }

/* ---- Credit notes ---- */
.tag-issued { background: var(--info-soft); color: var(--info-ink); }
.credit-sheet { max-width: 46rem; margin: 0 auto; background: var(--card);
  border: 1px solid var(--line-soft); border-radius: var(--radius); padding: 1.4rem; }
.credit-sheet-head { display: flex; justify-content: space-between; align-items: flex-start;
  gap: 1rem; flex-wrap: wrap; border-bottom: 1px solid var(--line-soft);
  padding-bottom: 0.9rem; margin-bottom: 0.9rem; }
.credit-sheet-head h2 { margin: 0; }
.credit-sheet-amt { text-align: right; display: flex; flex-direction: column;
  align-items: flex-end; gap: 0.35rem; }
.credit-total { font-size: var(--fs-2xl); font-weight: 700; font-variant-numeric: tabular-nums; }
.credit-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.7rem 1rem; margin: 0 0 1rem; }
.credit-meta dt { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); margin-bottom: 0.1rem; }
.credit-meta dd { margin: 0; }
.credit-period-note { border-left: 3px solid var(--warn); padding-left: 0.7rem; margin: 0 0 1rem; }
.credit-totals { margin-top: 1rem; max-width: 22rem; margin-left: auto; }
.credit-notes { margin-top: 1.2rem; border-top: 1px solid var(--line-soft); padding-top: 0.8rem; }
.credit-notes h3 { margin: 0 0 0.3rem; font-size: var(--fs-md); }
.credit-notes p { margin: 0; white-space: pre-wrap; }
.credit-lines { width: 100%; border-collapse: collapse; }
.credit-lines th { text-align: left; font-size: var(--fs-xs); text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--muted); padding: 0.3rem 0.35rem; }
.credit-lines td { padding: 0.25rem 0.35rem; border-bottom: 1px solid var(--line-soft); }
.credit-lines input[type="text"], .credit-lines input:not([type]) { width: 100%; }
.credit-lines .line-qty, .credit-lines .line-price { width: 6rem; text-align: right; }
.credit-lines .line-total { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Payments-received panel — a collapsed strip between the toolbar and the
   document preview, so the money taken is one click away without the preview
   losing any height. */
/* Two status strips on a document: a row of tabs, then ONE shared area beneath
   them. Tabs are pinned to row 1 and bodies to row 2 spanning every column, so
   whichever tab is pressed its contents land in the same place — directly under
   the row you pressed, full width.

   These were side-by-side <details>, where an open panel had to span the row to
   have space for its table and therefore reflowed onto a SECOND row: press the
   right-hand strip, and what opened appeared under the left-hand one.

   The columns must be EXPLICIT. `grid-column: 1 / -1` counts lines in the
   explicit grid, so with only `grid-auto-columns` the -1 has nothing to resolve
   against and the body silently stops spanning — it lands in column one and the
   ordering rows get squeezed into half the reader with grey beside them. */
.reader-panels {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  border-bottom: 1px solid var(--line-soft);
}
/* The ordering include ships a bare <form> beside its tab (declared outside the
   panel so collapsing cannot take the controls with it). As a grid child it
   claims a column of its own; it holds one hidden input, so taking it out of the
   box tree costs nothing and it still submits. Closed <dialog>s are display:none
   already and need no such help. */
.reader-panels > form { display: contents; }

/* A document with only one of the two strips gets a tab across the whole row
   rather than half of one with a gap beside it. */
.doc-tab:only-of-type { grid-column: 1 / -1; }
.doc-tab {
  grid-row: 1;
  /* nowrap, not wrap: the row is as tall as its tallest tab, so letting the
     longer summary fold onto a second line makes BOTH tabs taller and costs the
     document that height. The summary ellipsises instead — the detail it is
     abbreviating is one click away, underneath. */
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: nowrap;
  width: 100%; margin: 0; min-height: 0; text-align: left; min-width: 0;
  padding: 0.55rem 0.85rem; cursor: pointer;
  background: var(--card); border: 0; border-radius: 0;
  font: inherit; font-size: var(--fs-md); color: var(--ink);
}
.doc-tab + .doc-tab { border-left: 1px solid var(--line-soft); }
.doc-tab .pay-panel-head { flex: none; }
.doc-tab .pay-panel-sum {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.doc-tab:hover { background: var(--hover-bg); }
.doc-tab.is-open { background: var(--accent-soft); }
.doc-tab-caret {
  width: 15px; height: 15px; margin-left: auto; flex: none;
  color: var(--muted); transition: transform 0.15s;
}
.doc-tab.is-open .doc-tab-caret { transform: rotate(180deg); }

.doc-body {
  grid-row: 2; grid-column: 1 / -1;
  border-top: 1px solid var(--line-soft); background: var(--card);
}
.doc-body[hidden] { display: none; }

.pay-panel { border-bottom: 1px solid var(--line-soft); background: var(--card); }
.pay-panel > summary {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  padding: 0.55rem 0.85rem; cursor: pointer; list-style: none;
  font-size: var(--fs-md); user-select: none;
}
.pay-panel > summary::-webkit-details-marker { display: none; }
.pay-panel > summary:hover { background: var(--hover-bg); }
/* Matches the app's established section label (see .bm-section h4). */
.pay-panel-head {
  font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); display: inline-flex; align-items: center; gap: 0.45rem;
}
.pay-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.25rem; height: 1.25rem; padding: 0 0.35rem; border-radius: var(--radius-sm);
  background: var(--info-soft); color: var(--info-ink);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0; text-transform: none;
}
.pay-panel-sum { font-size: var(--fs-sm); }
.pay-panel-sum strong { color: var(--warn); font-weight: 600; }

/* ---- Needs-ordering screen ----
   Covered lines stay visible but recede: seeing that a line is already handled
   is the reassurance, and hiding them would make the list look incomplete. */
.shortage-group .card-head h2 { font-size: var(--fs-lg); }
/* Every supplier group is the same table. Fixed layout so their columns line
   up down the page instead of each one measuring its own contents — but the
   widths have to be declared, or "fixed" just divides the row evenly and
   starves the item name. Item is the only column left to size itself. */
.shortage-group table { table-layout: fixed; }
.shortage-group th.checkcol { width: 2.2rem; }
.shortage-group th.num { width: 6.5rem; }
.shortage-group thead th:nth-last-child(2) { width: 7rem; }   /* Order qty */
.shortage-group thead th:last-child { width: 8rem; }          /* Status */
.shortage-group td { overflow-wrap: anywhere; }
.shortage-group tr.sh-covered { opacity: 0.55; }
.shortage-group tr.sh-covered input[type="checkbox"] { cursor: not-allowed; }
/* The action bar sticks to the bottom so the button — which says how many
   orders and what they cost — is readable without scrolling back down. */
.shortage-bar {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  border-top: 2px solid var(--line);
}
.bar-vendor { display: flex; align-items: center; gap: 0.5rem; margin: 0; }
.bar-vendor select { margin: 0; min-width: 12rem; }
.shortage-bar #shortage-submit { margin-left: auto; }
.shortage-bar #shortage-submit[disabled] { opacity: 0.5; cursor: not-allowed; }

.capacity-field { display: block; margin: 0.9rem 0; }
.capacity-field input { margin: 0.2rem 0.5rem 0.2rem 0; }
.capacity-field .muted { display: block; font-size: var(--fs-sm); }

/* A day's booked load on the month calendar. Thin, under the date, and only
   drawn when there is work with an estimate on it — an empty bar on every day
   would be noise, and a full one has to mean something. */
.cal-load {
  display: block; height: 3px; margin: 0.15rem 0.1rem 0.2rem;
  background: var(--line-soft); border-radius: var(--radius-sm); overflow: hidden;
}
.cal-load-fill { display: block; height: 100%; background: var(--accent); }
.cal-load.is-over .cal-load-fill { background: var(--danger); }

/* The catalog search at the top of the item window. Deliberately the first
   thing in the modal: browsing is the fallback, not the default. */

/* A discount reads as money coming off, not as a product sold for minus $150. */
.li-discount .li-mfr { color: var(--ok-ink); }
.li-discount .li-name { font-weight: 500; }
.li-off { color: var(--muted); font-size: var(--fs-sm); margin-right: 0.15rem; }
tr:has(> .li-discount) .line-total { color: var(--ok-ink); }

/* ---- Dashboard: search, setup checklist, and what needs attention ----
   Both cards exist to disappear: the checklist once the shop is set up, the
   attention list once nothing is wrong. Neither is decoration, so they sit
   above the figures — a number computed from bad stock is worse than none. */
.dash-search { margin-bottom: var(--sp-4); }
/* Alone on its row, so it takes the room its placeholder actually needs. */
.dash-search .search-field { max-width: 34rem; }
.setup-card, .attention-card { margin-bottom: 1.1rem; }
.setup-list, .attention-list { list-style: none; margin: 0; padding: 0; }
.setup-list li, .attention-list li {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding: 0.55rem 0; border-bottom: 1px solid var(--line-soft);
}
.setup-list li:last-child, .attention-list li:last-child { border-bottom: 0; }
.setup-list .btn, .attention-list .btn { margin-left: auto; }
.setup-mark {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  border: 1.5px solid var(--line); display: flex; align-items: center;
  justify-content: center; font-size: var(--fs-xs); font-weight: 700; color: var(--ok-ink);
}
.setup-list li.is-done .setup-mark { border-color: var(--ok-ink); background: var(--ok-soft); }
.setup-list li.is-done .setup-text strong { color: var(--muted); font-weight: 500; }
.setup-text, .attention-text { display: flex; flex-direction: column; min-width: 14rem; flex: 1; }
.setup-text .muted, .attention-text .muted { font-size: var(--fs-sm); }
.attention-list .tag { flex: none; min-width: 2rem; text-align: center; }

/* ---- Fulfillment: the job panel on an invoice, and the job board ----
   The panel is three controls in a row that wrap to one per line on a phone,
   above a timeline. Deliberately quiet: it sits under the money on a page whose
   subject is the money, and only the "past its promised date" line raises its
   voice. */
.job-controls { display: flex; gap: var(--sp-4); flex-wrap: wrap; align-items: flex-end; }
.job-field { display: flex; flex-direction: column; gap: 0.25rem; min-width: 12rem; }
.job-field label { font-size: var(--fs-xs); font-weight: 600; color: var(--muted); }
.job-due-row { display: flex; gap: 0.4rem; align-items: center; }
.job-late { margin: 0.7rem 0 0; color: var(--danger); font-weight: 600; font-size: var(--fs-md); }
.job-note { display: flex; gap: 0.5rem; margin-top: 0.9rem; }
.job-note input { flex: 1; }
/* The timeline is history, so it reads as a log: small, dense, newest first,
   and never the thing your eye lands on first. */
.job-timeline { list-style: none; margin: 1rem 0 0; padding: 0.8rem 0 0; border-top: 1px solid var(--line-soft); font-size: var(--fs-sm); }
.job-timeline li { display: flex; gap: 0.5rem; flex-wrap: wrap; padding: 0.28rem 0; }
.job-when { color: var(--muted); min-width: 12rem; }
.job-what strong { font-weight: 600; }
.job-who { margin-left: auto; }
/* A stage badge on the invoice list. Not one of the status colors: a job stage
   is a different axis from Draft/Sent/Paid, and coloring it like one would
   invite reading it as one. */
.tag-stage { background: var(--line-soft); color: var(--ink-soft); }

/* ---- The jobs screen: saved views | queue | the job ----
   Three panes, the arrangement a help-desk queue settles into. Built on the
   same .reader shell as the quote/invoice split view, so it inherits the
   scrolling, the card treatment and the mobile stacking already tuned there. */
.reader.reader-3 { grid-template-columns: 190px 300px 1fr; }
.job-views .reader-items { padding: 0.3rem; }
.job-view {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0.38rem 0.5rem; border-radius: var(--radius-ctl);
  font-size: var(--fs-sm); color: var(--ink); text-decoration: none;
}
.job-view:hover { background: var(--hover-bg); }
.job-view.selected { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
/* The count beside a view, and the same pill on the tabs. Muted, because it is
   context for the label rather than a figure anybody acts on. */
.job-view-n { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.job-view.selected .job-view-n { color: var(--accent); }
.job-views .nav-label { margin: 0.7rem 0.5rem 0.2rem; }
/* A queue row: who it's for and how long it has sat, then the invoice and the
   vehicle, then the chips. The "days" figure sits where money sits in the
   document reader — it is the number you scan this list for. */
.job-row .tag { margin-right: 0.25rem; }
.job-who-chip {
  display: inline-block; padding: 0.1rem 0.4rem; border-radius: var(--radius-sm);
  background: var(--bg-soft); border: 1px solid var(--line-soft);
  font-size: var(--fs-2xs); color: var(--muted);
}
.job-blank { display: flex; align-items: center; justify-content: center; height: 100%; }
.job-pane { padding: 1rem 1.15rem 1.4rem; }
.job-headline h2 { margin: 0; font-size: var(--fs-lg); }
.job-headline p { margin: 0.15rem 0 0; font-size: var(--fs-sm); }
.job-props {
  display: flex; flex-wrap: wrap; gap: 1rem 1.4rem; margin: 1rem 0 0;
  padding: 0.85rem 0; border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}
.job-static { font-size: var(--fs-md); font-weight: 500; }
/* Planned vs actual time, and the clock. Two forms on one row: typing a figure
   and pressing Start are different intentions and shouldn't share a button. */
.job-time {
  display: flex; align-items: flex-end; gap: var(--sp-4); flex-wrap: wrap;
  padding: 0.8rem 0; border-bottom: 1px solid var(--line-soft);
}
.job-time-form, .job-clock { display: flex; align-items: flex-end; gap: 0.6rem; flex-wrap: wrap; margin: 0; }
.job-time-grp { display: flex; align-items: center; gap: 0.2rem; }
.job-time-grp label {
  font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
  margin: 0 0.35rem 0 0; white-space: nowrap;
}
input.job-num { width: 3.2rem; margin: 0; text-align: right; }
.job-unit { font-size: var(--fs-sm); color: var(--muted); margin-right: 0.4rem; }
.job-running { font-size: var(--fs-sm); color: var(--warn); font-weight: 600; }
.job-clock { margin-left: auto; }
.job-closed-note { margin: 0.7rem 0 0; font-size: var(--fs-sm); }
/* The parts table, directly under the properties: on a job screen this is the
   content, so unlike the invoice's collapsed panel it is always open. */
.job-parts { margin-top: 1.1rem; }
.job-parts-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.4rem; }
.job-parts-head h3 { margin: 0; font-size: var(--fs-md); }
.job-parts-head .btn { margin-left: auto; }
.job-parts .ord-table { font-size: var(--fs-sm); }
.job-parts .ord-table th, .job-parts .ord-table td { padding: 0.4rem 0.5rem; }
.job-parts .form-hint { margin: 0.45rem 0 0; }

/* The composer sits ABOVE the history: writing a note is what people come to
   this pane to do, and a box buried under thirty events doesn't get used. */
.job-note-form { margin: 1.1rem 0 0; }
.job-note-label { display: block; font-size: var(--fs-xs); font-weight: 600; color: var(--muted); margin-bottom: 0.25rem; }
.job-note-form textarea { width: 100%; margin: 0; }
.job-note-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.7rem; flex-wrap: wrap; margin-top: 0.4rem; font-size: var(--fs-xs);
}
.job-tabs { margin-top: 1.2rem; }
.job-empty { margin: 0.8rem 0 0; font-size: var(--fs-sm); }
.job-ev-head { display: flex; gap: 0.6rem; align-items: baseline; font-size: var(--fs-xs); }
.job-ev-head .job-who { font-weight: 600; color: var(--ink); margin-left: 0; }
.job-ev-head .job-when { color: var(--muted); min-width: 0; }
/* A note is somebody's words and reads as such; everything else on the timeline
   is the system reporting itself and stays quiet. */
.job-timeline li.is-note {
  background: var(--bg-soft); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 0.5rem 0.65rem; margin: 0.45rem 0;
}
.job-timeline li.is-note .job-what { white-space: pre-wrap; font-size: var(--fs-md); }
/* The invoice panel's timeline is one line per event and lays out in a row; the
   job pane's has a head and a body and stacks. Same class, two shapes. */
.job-pane .job-timeline li { display: block; }
.job-pane .job-timeline { padding-top: 0.5rem; }
/* Three panes need room. Drop the saved-views rail first — it is navigation,
   and the queue itself carries the same filters in its own header — then fall
   to a single column with the rest of the reader. */
@media (max-width: 1180px) {
  .reader.reader-3 { grid-template-columns: 260px 1fr; }
  .reader.reader-3 .job-views { display: none; }
}
@media (max-width: 820px) {
  .reader.reader-3 { grid-template-columns: 1fr; }
  .reader.reader-3 .job-views { display: flex; }
  .job-views .reader-items { display: flex; flex-wrap: wrap; gap: 0.2rem; }
  .job-views .nav-label { width: 100%; }
}
/* Settings: one stage per row — name, what it means, whether it finishes the
   job — saved together, because changing a stage's meaning without renaming it
   is rare and the pair is easier to reason about side by side. */
.stage-row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; margin: 0; }
.stage-row input[name="name"] { min-width: 11rem; }
.stage-row label.check { margin: 0; white-space: nowrap; }

/* ---- Ordering panel (invoice reader) ----
   Same collapsed-details shell as the payments panel above it; only the body is
   a table rather than a row list. The count pill goes amber when something is
   still on its way, so "are we waiting?" is answerable without opening it. */
.ord-count-wait { background: var(--warn-soft); color: var(--warn); }
.ord-panel .form-hint { padding: 0 0.85rem 0.7rem; margin: 0; }

/* One row per part rather than seven columns. The name and what is on the shelf
   read first, the order's details sit under them in a quiet meta line, and the
   state tag lands on the right where the eye finishes. Everything the table
   carried is still here — it is stacked instead of squeezed. */
.ord-list { padding: 0.2rem 0.85rem 0.4rem; }
.ord-row {
  display: grid; grid-template-columns: auto 1fr auto; align-items: start;
  gap: 0 0.7rem; padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--line-soft);
}
.ord-row:last-child { border-bottom: 0; }
/* A part nobody has ordered and there isn't stock for is the one thing on this
   panel that needs chasing, so it gets a tint rather than only a tag. */
.ord-row.ord-short { background: none; box-shadow: inset 3px 0 0 var(--danger); }
.ord-pick { display: flex; align-items: center; min-height: 1.6rem; margin: 0; cursor: pointer; }
.ord-pick input { margin: 0; }
.ord-main { min-width: 0; }
.ord-name {
  font-weight: 600; font-size: var(--fs-md); color: var(--ink);
  letter-spacing: -0.008em; overflow-wrap: break-word;
}
.ord-sub, .ord-meta {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.15rem 0.7rem;
  font-size: var(--fs-xs); color: var(--muted); margin-top: 0.15rem;
  font-variant-numeric: tabular-nums;
}
.ord-sku { font-weight: 500; }
/* The shelf count, which is the number somebody is deciding on. Green when
   there is something to take, plain when there is not — never alarming on its
   own, because zero on the shelf is normal for a special order. */
.ord-stock { color: var(--ok-ink); font-weight: 600; }
.ord-stock.is-none { color: var(--muted); font-weight: 500; }
.ord-pulled {
  color: var(--ok-ink); font-weight: 600;
  border: 1px solid currentColor; border-radius: var(--radius-sm); padding: 0 0.4rem;
}
/* Separators between the meta facts, so they read as a sentence without needing
   punctuation typed into the markup. */
.ord-meta > span + span::before { content: '·'; margin-right: 0.7rem; color: var(--line); }
.ord-meta a { font-weight: 600; }
.ord-side {
  display: flex; flex-direction: column; align-items: flex-end; gap: 0.35rem; flex: none;
}
.ord-empty { padding: 0.6rem 0.5rem; }

/* The action bar. Sticks to the bottom of the panel body so the two buttons stay
   reachable however long the list of parts is. */
.ord-bar {
  display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap;
  padding: 0.6rem 0.85rem 0.75rem; border-top: 1px solid var(--line-soft);
  position: sticky; bottom: 0; background: var(--card);
}
.ord-count { font-size: var(--fs-sm); font-weight: 500; }
.ord-recv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
/* Serial boxes in the Create-PO window: one per ticked item, name on the left so
   the column of boxes stays scannable when several parts are being ordered. */
.ord-po-serials { display: grid; gap: 0.4rem; margin: 0.7rem 0 0; }
.ord-po-serial { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); align-items: center; gap: 0.5rem; margin: 0; }
.ord-po-serial span { font-size: var(--fs-sm); color: var(--muted); overflow-wrap: break-word; }
@media (max-width: 560px) {
  .ord-row { grid-template-columns: auto 1fr; }
  .ord-side { grid-column: 2; align-items: flex-start; flex-direction: row; flex-wrap: wrap; margin-top: 0.3rem; }
  .ord-recv-grid { grid-template-columns: 1fr; }
}
/* Caret sits at the far right and turns as the panel opens, like the reference. */
.pay-caret { width: 15px; height: 15px; margin-left: auto; color: var(--muted); flex: none; transition: transform 0.15s; }
.pay-panel[open] > summary .pay-caret { transform: rotate(180deg); }
.pay-panel[open] > summary { border-bottom: 1px solid var(--line-soft); }
.pay-rows { padding: 0.2rem 0.85rem 0.6rem; }
.pay-row {
  display: grid; align-items: center; gap: 0.5rem 0.9rem;
  /* Receipt number, date, method, reference, amount, links. */
  grid-template-columns: auto 8.5rem minmax(7rem, auto) 1fr auto auto;
  padding: 0.5rem 0; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-md);
}
.pay-row:last-child { border-bottom: 0; }
/* Underlined, like every other link in the app. It used to opt out with
   text-decoration:none and only underline on hover, which left it reading as
   bold blue data rather than as something to click — and the Receipt/PDF links
   sitting a few columns to its right WERE underlined, so it looked like the one
   thing in the row you couldn't press. The list-row exemption above doesn't
   apply here: this is a handful of rows, not forty. */
.pay-no {
  font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.pay-date { color: var(--ink-soft); white-space: nowrap; }
.pay-method { display: inline-flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
.pay-ref { font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.pay-amt { font-weight: 600; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.pay-links { display: inline-flex; gap: 0.6rem; white-space: nowrap; }
.pay-links a { font-size: var(--fs-sm); }
@media (max-width: 700px) {
  /* Stack: the five-column row can't hold its shape at phone width. */
  .pay-row { grid-template-columns: 1fr auto; }
  .pay-no { grid-column: 1 / -1; }
  .pay-ref, .pay-links { grid-column: 1 / -1; }
  .pay-amt { text-align: right; }
}
/* Item and customer readers: same shell as the document reader, but the
   right-hand pane is tabbed detail that scrolls rather than an embedded PDF.
   (.rd-* is the customer reader; it shares this treatment exactly.) */
.item-tabs, .rd-tabs {
  display: flex; gap: var(--sp-1); padding: 0.4rem 0.55rem;
  border-bottom: 1px solid var(--line-soft); background: var(--card);
}
/* .item-tab / .rd-tab share the one tab rule above. */
/* A rail tab with nobody on that side: shown with its 0 so the count is the
   explanation, but not clickable — there is no customer for it to open. */
.reader-tab.is-empty { opacity: 0.5; cursor: default; }
.reader-tab.is-empty:hover { background: none; }
.rail-count { font-variant-numeric: tabular-nums; opacity: 0.75; font-weight: 400; }
.item-pane, .rd-pane { flex: 1; min-height: 0; overflow-y: auto; padding: 0.9rem 1rem 1.2rem; }
.item-pane > .stat-row, .rd-pane > .stat-row { margin-top: 0; }
.item-pane > .card, .rd-pane > .card { margin-bottom: 1rem; }
.rd-pane > .grid-2 { margin-bottom: 1rem; }
/* Transactions: one line per kind, each opening in place. Same collapsed-strip
   idea as .pay-panel, but stacked as a list of cards. */
.tx-groups { display: flex; flex-direction: column; gap: 0.6rem; }
.tx-group {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); overflow: hidden;
}
.tx-group > summary {
  display: flex; align-items: center; gap: 0.65rem; flex-wrap: wrap;
  padding: 0.7rem 0.9rem; cursor: pointer; list-style: none; user-select: none;
  font-size: var(--fs-md);
}
.tx-group > summary::-webkit-details-marker { display: none; }
.tx-group > summary:hover { background: var(--hover-bg); }
.tx-group[open] > summary { border-bottom: 1px solid var(--line-soft); }
.tx-caret {
  width: 14px; height: 14px; flex: none; color: var(--muted);
  transition: transform 0.12s ease;
}
.tx-group[open] > summary .tx-caret { transform: rotate(90deg); }
.tx-name { font-weight: 600; }
.tx-meta { font-size: var(--fs-sm); }
/* Pushed right so the figures line up down the stack. */
.tx-amt { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 500; }
.tx-body { padding: 0.2rem 0.9rem 0.9rem; }
.tx-body table { margin-top: 0.5rem; }
.tx-actions { display: flex; gap: 0.5rem; padding-top: 0.6rem; }
.tx-cap { margin: 0.5rem 0 0; font-size: var(--fs-xs); }
@media (max-width: 560px) { .tx-amt { margin-left: 0; width: 100%; } }
.stat-sub { display: block; font-size: var(--fs-xs); color: var(--muted); margin-top: 0.15rem; }
.stat-warn .stat-value { color: var(--danger); }
/* "NET SALES AFTER CREDITS" — the <small> qualifier was inheriting the label's
   uppercase and tracking, so the tile shouted twice. It reads as an aside now. */
.stat-label small, .kpi-label small {
  font-size: inherit; font-weight: 400; text-transform: none; letter-spacing: 0; opacity: 0.85;
}
.over-limit { color: var(--danger); font-weight: 600; }
.item-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; align-items: start; }
.item-cards > .card { margin-bottom: 0; }
.item-menu { padding: 0.5rem 0; border-bottom: 1px solid var(--line-soft); }
.item-menu:last-child { border-bottom: none; }
.list-filter { display: flex; flex-direction: column; gap: 0.2rem; color: var(--muted);
  font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
.list-filter select { margin: 0; }

/* Editor action bar — pinned to the bottom of the window so Save/Cancel are in
   reach at any scroll position. Offset by the sidebar so it never sits under it. */
.edit-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--card); border-top: 1px solid var(--line);
  box-shadow: 0 -2px 10px rgba(15, 30, 55, 0.08);
}
.with-sidebar .edit-bar { left: 214px; }   /* clears the fixed sidebar */
.edit-bar-inner {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  max-width: 1360px; margin: 0 auto;       /* lines up with .container */
  padding: 0.45rem 1.7rem;
}
.edit-bar .btn { min-height: 2rem; padding: 0.3rem 0.9rem; }
.edit-bar form.inline { display: contents; }
.edit-bar-label { font-size: var(--fs-sm); white-space: nowrap; }
/* Spacer keeps the last card clear of the fixed bar. */
.edit-bar-spacer { height: 3.4rem; }
@media (max-width: 840px) {   /* matches where the sidebar collapses */
  .with-sidebar .edit-bar { left: 0; }
  .edit-bar-label { display: none; }
  .edit-bar-inner { padding: 0.45rem 0.9rem; }
}

/* Topper specs on a purchase-order line — a tinted strip under the line so it
   reads as belonging to that line rather than as a separate section. */
.po-line-specs {
  margin-top: 0.6rem; padding: 0.6rem 0.7rem;
  background: var(--accent-soft); border-radius: var(--radius);
}
.po-specs-head {
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
  font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--muted); margin-bottom: 0.5rem;
}
.po-specs-hint { text-transform: none; letter-spacing: 0; font-weight: 400; }
.po-specs-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.55rem 0.7rem; }
.po-spec-field { display: flex; flex-direction: column; gap: 0.2rem; margin: 0; font-size: var(--fs-xs); }
.po-spec-field > span:first-child { color: var(--muted); font-weight: 500; }

/* Per-unit detail on a stock adjustment line */
.adj-unit-card { padding: 0.5rem 0; border-top: 1px solid var(--line-soft); }
.adj-unit-card:first-child { border-top: none; padding-top: 0; }
.adj-unit-head {
  font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.4rem;
}
/* Serial numbers: one per row, down the page. The specs above are a grid
   because they are different questions; these are the same question asked N
   times, so they read as a list and each row says which topper it is. */
.adj-serial-list { display: flex; flex-direction: column; gap: 0.35rem; }
.adj-serial-row {
  display: flex; align-items: center; gap: 0.7rem; margin: 0; font-size: var(--fs-xs);
}
.adj-serial-row > span {
  color: var(--muted); font-weight: 500;
  flex: 0 0 7.5rem;          /* wide enough for "Topper 10 of 12" not to wrap */
}
/* Override the global input: full width and a top margin are for a field under
   its label, and here the label is beside it. Capped because a serial is short
   and a 900px box invites the eye to expect more than it holds. */
.adj-serial-row input { margin-top: 0; max-width: 20rem; }
@media (max-width: 620px) {
  .adj-serial-row { flex-direction: column; align-items: stretch; gap: 0.15rem; }
  .adj-serial-row > span { flex: none; }
  .adj-serial-row input { max-width: none; }
}

.adj-unit-pick {
  display: flex; align-items: flex-start; gap: 0.5rem; margin: 0;
  padding: 0.4rem 0; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-sm);
}
.adj-unit-pick:last-child { border-bottom: none; }
.adj-unit-pick input { margin: 0.15rem 0 0; flex: none; }

/* Stocked topper units */
.unit-specs { display: flex; flex-wrap: wrap; gap: 0.3rem 0.5rem; margin-top: 0.2rem; }
.unit-spec { font-size: var(--fs-xs); color: var(--muted); }
.unit-spec b { color: var(--ink); font-weight: 500; }
.unit-blank { font-style: italic; opacity: 0.7; }

/* Record-payment: amount box with "Pay in full" sitting beside it. */
.pay-amount-row { display: flex; gap: 0.5rem; align-items: center; }
.pay-amount-row input { margin: 0; flex: 1; min-width: 0; }
.pay-amount-row .btn { white-space: nowrap; }

@media (max-width: 820px) {
  .reader { grid-template-columns: 1fr; height: auto; }
  .reader-list { max-height: 48vh; }
  .reader-pdf { height: 75vh; }
  .item-pane { overflow-y: visible; }
  .item-cards { grid-template-columns: 1fr; }
}

/* ---- Home search ---- */
.home-search-hero { max-width: 660px; margin: 8vh auto 0; text-align: center; }
.home-search-hero h1 { font-size: var(--fs-2xl); letter-spacing: -0.024em; }
.home-logo { display: flex; justify-content: center; margin: 0 auto 1.6rem; max-width: 100%; }
/* The shop's own uploaded logo still comes through as an <img> here. */
img.home-logo { display: block; max-width: 260px; }
.big-search { display: flex; gap: 0.6rem; margin-bottom: 1.4rem; }
.big-search input[type="search"] {
  min-width: 0; min-height: var(--control-h-lg);
  font-size: var(--fs-lg); padding: 0.55rem 0.95rem; border-radius: var(--radius-ctl); margin: 0;
}
.big-search .btn { min-height: var(--control-h-lg); padding: 0 1.4rem; }
.quick-actions { display: flex; gap: 0.6rem; justify-content: center; flex-wrap: wrap; margin-top: 1.6rem; }
.home-search section.card { text-align: left; }

/* ---- Grids ---- */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); align-items: start; }
.grid-doc { display: grid; grid-template-columns: 2fr 1fr; gap: var(--sp-4); align-items: start; }
.doc-lower { display: grid; grid-template-columns: 1fr 360px; gap: var(--sp-4); align-items: start; }
@media (max-width: 1000px) { .grid-2, .grid-doc, .form-grid, .doc-lower { grid-template-columns: 1fr; } }
.details-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 var(--sp-4); }
.details-grid.details-notes { grid-template-columns: 1fr 1fr; }
/* Inside the narrow settings measure a repeat(3) grid holding two fields made
   both of them a third of an already-narrow column. */
.settings-narrow .details-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 0 0.8rem; }
/* A grid item is min-width:auto by default, so a cell holding a control that
   refuses to shrink (a combo label is nowrap) makes its column wider than its
   1fr share — which pushes the last column past the container's right edge,
   where it is clipped. Letting the items shrink makes 1fr mean 1fr. */
.details-grid > * { min-width: 0; }
/* Three columns need room to stay readable; drop to two before dropping to one. */
@media (max-width: 1080px) { .details-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 800px) { .details-grid, .details-grid.details-notes { grid-template-columns: 1fr; } }

/* ---- Stat tiles ---- */
.stat-row {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(165px, 1fr));
  gap: var(--sp-3); margin: var(--sp-3) 0 var(--sp-4);
}
.stat {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.7rem 0.9rem; min-width: 0; box-shadow: var(--shadow);
}
.stat-label {
  display: block; font-weight: 600; font-size: var(--fs-2xs); color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.15rem;
  /* Two lines' worth, always: a tile whose label wraps must not be taller
     than the tile beside it whose label does not. */
  min-height: 2.2em;
}
.stat-value { display: block; font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.022em; font-variant-numeric: tabular-nums; line-height: 1.2; }

/* The column gap used to run *through* the rule on the total line, leaving two
   stubs with a hole between them. The gutter lives in the dd's padding now, so
   the two borders abut into one continuous rule. */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0; margin: 0; }
.kv dd { padding-left: 1rem; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.kv .strong { font-weight: 600; border-top: 1px solid var(--line); padding-top: 0.3rem; }
.totals .kv dd { font-weight: 500; }
/* The editable rows of the totals block (Labor / Trade in / Shipping). These had
   no rule of their own, so their inputs filled the column and left-aligned their
   figures inside a stack whose whole point is a right-hand column of money. */
.kv-inputs { align-items: center; row-gap: 0.4rem; }
.kv-inputs dd { display: flex; justify-content: flex-end; }
.kv-inputs dd input { margin: 0; width: 100%; max-width: 8.5rem; text-align: right; font-variant-numeric: tabular-nums; }

.spacer { flex: 1; }

/* ---- Pickers & widgets ---- */
.picker-row { display: flex; gap: 0.45rem; align-items: center; position: relative; }
.search-results {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 10;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  max-height: 260px; overflow-y: auto; box-shadow: var(--shadow-pop);
}
.search-results button {
  display: block; width: 100%; text-align: left; padding: 0.5rem 0.75rem;
  background: none; border: none; border-bottom: 1px solid var(--line-soft);
  cursor: pointer; font-size: var(--fs-md); color: var(--ink);
}
.search-results button:hover { background: var(--hover-bg); }
.search-results button:last-child { border-bottom: none; }

.addline-row { display: flex; gap: 0.45rem; align-items: center; flex-wrap: wrap; margin-top: 0.4rem; }
.addline-row input { margin-top: 0; }
.addline-search-wrap { position: relative; flex: 1 1 190px; min-width: 170px; }
.addline-row .w-sku { flex: 0 1 130px; min-width: 110px; }
.addline-row .w-desc { flex: 2 1 220px; min-width: 160px; }
.addline-row .w-qty { flex: 0 0 62px; text-align: right; }
.addline-row .w-money { flex: 0 1 92px; min-width: 80px; text-align: right; }
.addline-row .w-po { flex: 0 1 110px; min-width: 90px; }
.addline-row label.check { margin: 0; white-space: nowrap; }
.addline-note { margin: 0.4rem 0 0; font-size: var(--fs-sm); color: var(--muted); }

.addline-fields { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; margin-top: 0.55rem; }
.addline-fields input, .addline-fields select { width: auto; flex: 1; min-width: 110px; margin-top: 0; }
.addline-fields input.narrow { flex: 0 1 120px; }
@media (max-width: 560px) {
  .addline-fields input, .addline-fields select { min-width: 130px; }
  .addline-fields input.narrow { flex: 1 1 130px; min-width: 130px; }
}
.addline-fields label.check { margin: 0; }
/* A label that wraps its own control is a caption stacked over a box, never the
   two side by side. Left inline, the caption and the box shared a line and the
   box dropped below it only once the caption was long enough to overflow — so
   "Qty" and "PO#" kept their boxes beside the text while "Shipping charge $"
   and "Additional labor $" pushed theirs underneath, landing four controls on
   three different baselines. Bottom-aligned as well as stacked, so a caption
   that wraps on a narrow screen still leaves every box on one line.
   Scoped to labels: the other .addline-fields rows are bare inputs and a
   button, which the row's own centring already lines up. */
.addline-fields > label:not(.check) {
  display: flex; flex-direction: column; gap: 0.25rem;
  flex: 0 1 auto; min-width: 110px; align-self: end; margin-bottom: 0;
}
/* The row's flex sizing applies to a bare input; inside a stacked label the box
   fills the caption's column instead. Without this the row's `flex-basis` would
   be read down the label's column axis and set the box's HEIGHT. */
.addline-fields > label:not(.check) > input,
.addline-fields > label:not(.check) > select { flex: none; width: 100%; min-width: 0; }
.quick-line { margin-top: 0.7rem; }
/* Every other disclosure in the app draws its own chevron and hides the UA
   marker; this one was showing the raw browser triangle. */
.quick-line > summary {
  cursor: pointer; color: var(--muted); font-size: var(--fs-md); font-weight: 500;
  width: fit-content; list-style: none; display: inline-flex; align-items: center; gap: 0.4rem;
}
.quick-line > summary::-webkit-details-marker { display: none; }
.report-group > summary::-webkit-details-marker { display: none; }
.report-group > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 0.5rem;
  font-size: var(--fs-lg); font-weight: 600; margin: 0 0 0.7rem;
}
.report-group > summary::before {
  content: ""; width: 0; height: 0; flex: none;
  border-left: 5px solid var(--muted); border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform 0.12s;
}
.report-group[open] > summary::before { transform: rotate(90deg); }
.quick-line > summary::before { content: "+"; font-weight: 600; font-size: var(--fs-lg); line-height: 1; }
.quick-line[open] > summary::before { content: "\2212"; }
.quick-line > summary:hover { color: var(--accent); }
.quick-line[open] > summary { margin-bottom: 0.2rem; }
/* A topper line is 250px tall once its specs are listed; middle-aligning its
   qty/price/total left them floating level with nothing. Anchor them to the
   first line of the item, which is what they price. */
.lines-editable tbody td.num, .lines-editable tbody td.tax-col { vertical-align: top; padding-top: 0.8rem; }
tr.addrow td { vertical-align: middle; }
tr.addrow > td { border-top: 1px solid var(--line); background: var(--thead-bg); }
tr.addrow input, tr.addrow label.check { margin: 0; }
/* An add-row is a form line, so its submit matches the fields beside it
   rather than shrinking to the small size used for in-row actions. */
tr.addrow .btn.small { min-height: var(--control-h); padding: 0.35rem 0.85rem; font-size: var(--fs-md); }
@media (max-width: 560px) {
  table tfoot { display: block; width: 100%; }
  tr.addrow { display: block; width: 100%; padding: 0.5rem 0; }
  tr.addrow td { display: block; width: 100%; padding: 0.25rem 0; border: 0; }
  tr.addrow input { width: 100%; max-width: none; box-sizing: border-box; }
  tr.addrow .btn { width: 100%; }
}

.chip-list { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.45rem; }
.chip {
  display: inline-flex; gap: 0.35rem; align-items: center; border: 1px solid var(--line);
  border-radius: var(--radius-ctl); padding: 0.18rem 0.6rem; background: var(--card); font-size: var(--fs-sm);
}
.chip button { background: none; border: none; cursor: pointer; color: var(--muted); padding: 0; font-size: var(--fs-sm); }
.chip button:hover { color: var(--danger); }

/* Recipient fields (email-chips.js): the box grows with the addresses inside it
   and the typing input sits in the gap, so it reads as one ordinary field. */
.chips-field {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem;
  width: 100%; min-height: var(--control-h); margin-top: 0.18rem;
  padding: 0.2rem 0.35rem; cursor: text;
  border: 1px solid var(--input-border); border-radius: var(--radius-ctl);
  background: var(--input-bg);
  transition: border-color 0.12s, box-shadow 0.12s;
}
.chips-field:hover { border-color: var(--input-hover); }
.chips-field:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.chips-field .chips-entry {
  flex: 1 1 9rem; min-width: 7rem; width: auto;
  margin: 0; padding: 0.1rem 0.15rem; min-height: 0;
  border: 0; background: none;
}
.chips-field .chips-entry:hover { border: 0; }
.chips-field .chips-entry:focus { outline: none; border: 0; box-shadow: none; }
.chip.email-chip {
  padding: 0.1rem 0.3rem 0.1rem 0.55rem; max-width: 100%;
  background: var(--accent-soft); border-color: transparent; color: var(--ink);
}
.chip.email-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip.email-chip.bad { background: var(--danger-soft); color: var(--danger); }
.chip.email-chip button { font-size: var(--fs-md); line-height: 1; }

.logo-preview { max-height: 80px; max-width: 240px; display: block; margin-bottom: 0.6rem; }
.bare-fieldset { border: none; padding: 0; margin: 0; min-width: 0; }
.bare-fieldset:disabled { opacity: 0.6; }
details summary { cursor: pointer; font-weight: 500; margin: 0.5rem 0; font-size: var(--fs-md); }
details { margin-bottom: 0.5rem; }

/* ---- Modals ---- */
.sp-modal {
  border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 0;
  box-shadow: 0 28px 64px -16px rgba(8, 14, 24, 0.45);
  max-width: 440px; width: calc(100vw - 3rem);
  background: var(--card); color: var(--ink);
}
.sp-modal::backdrop { background: rgba(12, 18, 27, 0.5); backdrop-filter: blur(3px); }
.sp-modal-wide { max-width: 640px; }
.sp-modal-xl { max-width: 960px; }
.sp-modal-body { padding: 1.4rem 1.5rem; max-height: 92vh; overflow-y: auto; position: relative; }
#browse-modal .sp-modal-body { overflow: visible; max-height: none; min-height: 460px; }
/* Wider than the other XL modals: a topper can carry a dozen dropdown menus in
   a three-column grid, and 960px squeezed every value down to an ellipsis. */
#browse-modal { max-width: 1180px; max-height: 95dvh; overflow-y: auto; }
.sp-modal-x {
  position: absolute; top: 0.85rem; right: 0.9rem;
  width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: var(--radius-ctl);
  color: var(--muted); font-size: var(--fs-md); cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.sp-modal-x:hover { color: var(--ink); background: var(--hover-bg); }
.sp-modal-title {
  margin: 0 0 0.3rem; font-family: var(--font-body); font-weight: 600;
  font-size: var(--fs-lg); letter-spacing: -0.018em; text-transform: none; color: var(--ink);
}
.sp-modal-hint { margin: 0 0 0.8rem; font-size: var(--fs-sm); color: var(--muted); }
.sp-modal-input { margin-bottom: 1rem; }
.sp-modal-select { margin-bottom: 1rem; }
.sp-modal-actions { display: flex; justify-content: flex-end; gap: 0.55rem; margin-top: 1.1rem; }

/* ---- Import Accessories ---- */
.import-card { max-width: var(--measure-list); }
.import-card .import-step {
  margin: 1.4rem 0 0.4rem; font-weight: 600; font-size: var(--fs-md);
  letter-spacing: -0.01em; text-transform: none; color: var(--ink);
}
.import-card .import-step:first-of-type { margin-top: 0.2rem; }
.import-card .import-step em { color: var(--accent); font-style: normal; }
.import-card .import-step .muted-step { color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
.import-mfr, .import-file { max-width: 420px; display: block; }
.import-file input[type="file"] {
  width: 100%; padding: 0.6rem; border: 1px dashed var(--input-border);
  border-radius: var(--radius-ctl); background: var(--bg-soft); cursor: pointer;
}
.import-headmode { display: flex; flex-direction: column; gap: 0.3rem; margin: 0.3rem 0 0.5rem; }
.import-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 0.5rem 1rem; margin-bottom: 0.4rem; }
.import-preview table { min-width: 100%; }
.import-preview th, .import-preview td { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Opened up to 200 rows the preview would run off the bottom of the page and
   bury the import button, so it scrolls in place instead. The box already
   scrolls sideways; this is the same box scrolling both ways, not a second one
   nested inside it. The header stays put, or by row 60 nobody knows which
   column they are reading. */
.import-preview { max-height: min(60vh, 620px); overflow-y: auto; }
.import-preview thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--card); box-shadow: inset 0 -1px 0 var(--line);
}
.import-more { display: flex; align-items: center; gap: 0.6rem; margin: 0.6rem 0 0; }
.import-more .btn { margin-top: 0; }
.import-col-letter {
  display: inline-block; margin-right: 0.45rem; padding: 0 0.35rem; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--accent); font-size: var(--fs-xs); font-weight: 600;
}
.import-warning {
  margin-top: 1rem; padding: 0.6rem 0.8rem; border: 1px solid var(--danger-line);
  border-radius: var(--radius-ctl); background: var(--danger-soft); color: var(--danger);
}

/* ---- Per-item dropdown menus ---- */
.menu-hint-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.menu-hint-row p { margin: 0 0 0.6rem; }
.menu-hint-row .btn { flex: none; margin-left: auto; }
.cm-section {
  margin: 1.2rem 0 0.5rem; font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
  border-bottom: 1px solid var(--line-soft); padding-bottom: 0.3rem;
}
.client-modal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.15rem 1rem; margin-top: 0.4rem; }
@media (max-width: 860px) { .client-modal-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .client-modal-grid { grid-template-columns: 1fr; } }

/* ---- Comboboxes (select-or-add) ---- */
.combo { position: relative; }
.combo-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  width: 100%; min-height: var(--control-h); margin-top: 0.18rem; padding: 0.4rem 0.6rem;
  border: 1px solid var(--input-border); border-radius: var(--radius-ctl);
  background: var(--input-bg); color: var(--ink); cursor: pointer;
  font: inherit; font-size: var(--fs-md); line-height: 1.35; text-align: left;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.combo-trigger:hover { border-color: var(--input-hover); }
.combo-trigger.open { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.combo-trigger.combo-invalid { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(194, 57, 52, 0.16); }
.combo-trigger svg { width: 15px; height: 15px; color: var(--muted); flex-shrink: 0; transition: transform 0.15s; }
.combo-trigger.open svg { transform: rotate(180deg); }
/* min-width:0 so this flex item can shrink below its text — without it the
   ellipsis never engages and the trigger forces its whole column wider. */
.combo-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.combo-panel {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 60;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-pop); overflow: hidden;
}
.combo-search { padding: 0.5rem; border-bottom: 1px solid var(--line-soft); }
.combo-search input { margin: 0; }
.combo-list { max-height: 290px; overflow-y: auto; }
.combo-empty { padding: 0.8rem 1rem; margin: 0; color: var(--muted); }
.combo-row {
  display: flex; gap: 0.7rem; align-items: center; width: 100%;
  padding: 0.5rem 0.75rem; background: none; border: none;
  border-bottom: 1px solid var(--line-soft); cursor: pointer;
  text-align: left; color: var(--ink); font: inherit; font-size: var(--fs-md);
  transition: background 0.1s;
}
.combo-row:hover, .combo-row:focus-visible { background: var(--hover-bg); }
.combo-row:last-child { border-bottom: none; }
.combo-avatar {
  flex-shrink: 0; width: 32px; height: 32px; display: flex; align-items: center;
  justify-content: center; border-radius: var(--radius-ctl);
  background: var(--accent-soft); color: var(--accent);
  font-weight: 600; font-size: var(--fs-md);
}
.combo-text { display: flex; flex-direction: column; min-width: 0; }
.combo-text strong { display: flex; gap: 0.5rem; align-items: baseline; font-weight: 600; }
.combo-text strong em {
  font-style: normal; font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--accent);
}
.combo-text small { color: var(--muted); font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.combo-new {
  display: flex; gap: 0.45rem; align-items: center; justify-content: center; width: 100%;
  padding: 0.6rem; background: var(--bg-soft); color: var(--accent);
  border: none; border-top: 1px solid var(--line); cursor: pointer;
  font: inherit; font-weight: 600; font-size: var(--fs-md);
  text-transform: none; letter-spacing: -0.006em;
  transition: background 0.12s;
}
.combo-new:hover { background: var(--accent-soft); }
.combo-new svg { width: 14px; height: 14px; }

/* Next page of a server-searched combo (customers, invoices). Scrolling to the
   bottom of the rows loads it — this is the marker that says so while the fetch
   is out, sitting inside the scroll area, not pinned like .combo-new. */
.combo-more-sentinel {
  display: block; width: 100%; padding: 0.5rem; margin: 0.15rem 0 0;
  color: var(--muted); border-top: 1px solid var(--line);
  font-weight: 600; font-size: var(--fs-sm); text-align: center;
}
/* Only when there's no IntersectionObserver to do it automatically. */
.combo-more-sentinel.combo-more-tap { color: var(--accent); cursor: pointer; }
.combo-more-sentinel.combo-more-tap:hover { background: var(--bg-soft); }

.addline-item-row { display: flex; align-items: flex-end; gap: 0.6rem; }
.addline-item-row .addline-item-label { flex: 1 1 auto; max-width: 560px; margin: 0; }
.addline-cancel {
  margin-left: auto; flex-shrink: 0;
  width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
  background: var(--card); border: 1px solid var(--input-border); border-radius: var(--radius-ctl);
  color: var(--muted); font-size: var(--fs-md); cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.addline-cancel:hover { color: var(--danger); background: var(--danger-soft); border-color: var(--danger-line); }

.addline-item-label { max-width: 560px; }
.addline-item-btns { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.2rem; }
.addline-item-btns .item-browse-btn { flex: 1 1 220px; margin-top: 0; }
.item-browse-btn {
  display: flex; align-items: center; gap: 0.55rem; width: 100%; margin-top: 0.2rem;
  min-height: var(--control-h); padding: 0.35rem 0.75rem;
  justify-content: flex-start; text-align: left;
  font-weight: 500; font-size: var(--fs-md); text-transform: none; letter-spacing: -0.006em;
}
.item-browse-btn svg { width: 15px; height: 15px; color: var(--muted); flex-shrink: 0; }
.pack-details { display: contents; }
.pq-hint { display: block; font-weight: 400; font-size: var(--fs-sm); margin-top: 0.25rem; color: var(--muted); }
.pq-list { margin: 0.6rem 0 0.4rem; }
.pq-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.45rem 0; border-bottom: 1px solid var(--line-soft); }
.pq-row:last-child { border-bottom: none; }
.pq-head { color: var(--muted); font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; padding-bottom: 0.2rem; }
.pq-edit-form { display: flex; align-items: center; gap: 0.5rem; flex: 1; min-width: 0; margin: 0; }
.pq-edit-form input { width: auto; margin-top: 0; }
.pq-label { flex: 0 0 32%; min-width: 110px; }
.pq-type-badge { flex: 0 0 68px; color: var(--muted); font-size: var(--fs-sm); }
.pq-opts { flex: 1; min-width: 90px; }
.pq-save { flex: none; }
.pq-tools { flex: none; display: flex; gap: 0.3rem; align-items: center; }
.pq-tools-head { flex: none; }
/* Four fixed-basis columns need ~400px; a phone has ~360. Let each question
   become its own stacked block instead of pushing the page sideways. */
@media (max-width: 700px) {
  .pq-head { display: none; }
  .pq-row { flex-wrap: wrap; gap: 0.45rem; padding: 0.7rem 0; }
  .pq-edit-form { flex-wrap: wrap; gap: 0.45rem; }
  .pq-label { flex: 1 1 100%; min-width: 0; }
  .pq-opts { flex: 1 1 100%; min-width: 0; }
  .pq-type-badge { flex: 0 0 auto; }
  .pq-tools { margin-left: auto; }
}
.pk-table { table-layout: fixed; }
.item-combo .combo-trigger { padding: 0.52rem 0.65rem; }
.combo-action {
  display: flex; gap: 0.45rem; align-items: center; justify-content: center; width: 100%;
  padding: 0.55rem; background: none; color: var(--ink-soft); cursor: pointer;
  border: none; border-top: 1px solid var(--line-soft);
  font: inherit; font-weight: 500; font-size: var(--fs-md);
  text-transform: none; letter-spacing: -0.006em; transition: background 0.12s, color 0.12s;
}
.combo-action:hover { background: var(--hover-bg); color: var(--ink); }
.combo-action svg { width: 15px; height: 15px; color: var(--muted); }
.item-row {
  display: flex; gap: 0.6rem; align-items: baseline; width: 100%; text-align: left;
  padding: 0.5rem 0.75rem; background: none; border: none;
  border-bottom: 1px solid var(--line-soft); cursor: pointer; color: var(--ink);
  font: inherit; font-size: var(--fs-md);
}
.item-row:hover { background: var(--hover-bg); }
.item-row strong { font-variant-numeric: tabular-nums; flex-shrink: 0; font-weight: 600; }
.item-row span { flex: 1; min-width: 0; }
.item-row em { font-style: normal; color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; }
.combo-custom { color: var(--accent); font-weight: 600; }
.addline-numbers { display: flex; gap: 0.7rem; flex-wrap: wrap; align-items: flex-end; margin-top: 0.6rem; }
.addline-numbers label { margin: 0; font-size: var(--fs-sm); }
.addline-numbers .taxable-check { flex-direction: row; align-items: center; padding-bottom: 0.5rem; }
#addline-desc-wrap { max-width: 560px; margin-top: 0.6rem; margin-bottom: 0; }
#addline-desc-wrap[hidden] { display: none; }

.combo-slim .combo-trigger { padding: 0.4rem 0.55rem; font-size: var(--fs-md); }
.combo-slim .combo-search { padding: 0.4rem; }
.combo-slim .combo-search input { padding: 0.32rem 0.5rem; font-size: var(--fs-sm); }
.combo-slim .combo-list { max-height: 220px; }
.combo-row-slim {
  display: block; width: 100%; padding: 0.36rem 0.7rem;
  background: none; border: none; border-bottom: 1px solid var(--line-soft);
  cursor: pointer; text-align: left; color: var(--ink); font: inherit; font-size: var(--fs-md);
  transition: background 0.1s;
}
.combo-row-slim:hover, .combo-row-slim:focus-visible { background: var(--hover-bg); }
.combo-row-slim:last-child { border-bottom: none; }
.combo-row-slim.combo-row-rich { display: flex; min-width: 0; }
.combo-row-rich .combo-text { flex: 1; min-width: 0; }
.combo-row-wrap .combo-row-rich { min-width: 0; }

.combo-row-wrap { display: flex; align-items: stretch; border-bottom: 1px solid var(--line-soft); }
.combo-row-wrap:last-child { border-bottom: none; }
.combo-row-wrap .combo-row-slim { flex: 1; border-bottom: none; }
.combo-row-tools { display: flex; align-items: center; gap: 2px; padding-right: 0.35rem; opacity: 0.5; transition: opacity 0.1s; }
.combo-row-wrap:hover .combo-row-tools, .combo-row-tools:focus-within { opacity: 1; }
.combo-tool {
  display: flex; align-items: center; justify-content: center; width: 26px; height: 26px;
  background: none; border: none; border-radius: var(--radius-sm); cursor: pointer;
  color: var(--muted); font-size: var(--fs-sm);
}
.combo-tool svg { width: 14px; height: 14px; }
.combo-tool:hover { background: var(--accent-soft); color: var(--accent); }
.combo-tool-del:hover { background: var(--danger-soft); color: var(--danger); }
.req { color: var(--danger); font-weight: 600; }
.sp-modal-price-wrap { display: block; margin-top: 0.6rem; }
.sp-modal-price-wrap .sp-modal-price { width: 100%; margin-top: 0.18rem; }
.combo-meta { color: var(--muted); font-style: normal; margin-left: 0.45rem; font-size: 0.86em; }

/* The menu's own name + its rename/remove tools. Capped to the cell so a long
   menu name truncates instead of widening the column past its share. */
.menu-head { display: inline-flex; align-items: center; gap: 0.15rem; max-width: 100%; }
.menu-head > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.menu-tool {
  display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  background: none; border: none; border-radius: var(--radius-sm); cursor: pointer;
  color: var(--muted); font-size: var(--fs-xs); opacity: 0.7;
}
.menu-tool svg { width: 12px; height: 12px; }
.menu-tool:hover { opacity: 1; background: var(--accent-soft); color: var(--accent); }
.menu-tool-price { opacity: 1; font-weight: 600; color: var(--accent); }

/* Drag handle for reordering a topper's dropdown menus (item form only). */
.menu-grip { cursor: grab; touch-action: none; }
.menu-grip:active { cursor: grabbing; }
.menu-grip svg { width: 13px; height: 13px; }
.topt-menu.menu-dragging {
  opacity: 0.6; border-radius: var(--radius-sm);
  outline: 2px dashed var(--accent); outline-offset: 3px;
}
/* Text selection during a drag makes the whole grid look like it's being copied. */
.details-grid.menu-reordering { user-select: none; -webkit-user-select: none; }
.details-grid.menu-reordering .menu-grip { cursor: grabbing; }
.menu-add-cell { display: flex; flex-direction: column; justify-content: flex-end; margin-bottom: 0.65rem; }
.menu-add-cell .btn.menu-add {
  display: flex; align-items: center; justify-content: center; gap: 0.35rem;
  width: 100%; min-height: 0; padding: 0.4rem 0.55rem; font-size: var(--fs-md); line-height: 1.5;
  border-radius: var(--radius-ctl);
}
.menu-add-cell .btn.menu-add svg { width: 14px; height: 14px; flex: none; }
.combo-slim .combo-new { padding: 0.45rem; font-size: var(--fs-sm); }
.combo-slim .combo-empty { padding: 0.55rem 0.75rem; font-size: var(--fs-sm); }

/* ---- Settings hub ---- */
.crumbs { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.4rem; font-size: var(--fs-sm); }
.crumbs a { text-decoration: none; font-weight: 500; }
.crumbs span { color: var(--muted); }
.settings-group { margin-bottom: var(--sp-5); max-width: var(--measure-list); }
.settings-list { padding: 0; overflow: hidden; }
.settings-row {
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.85rem 1.1rem; text-decoration: none; color: var(--ink);
  border-bottom: 1px solid var(--line-soft);
  transition: background 0.12s;
}
.settings-row:last-child { border-bottom: none; }
.settings-row:hover { background: var(--hover-bg); }
.settings-row:hover .sr-chevron { color: var(--accent); transform: translateX(2px); }
.sr-icon {
  flex-shrink: 0; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); background: var(--bg-soft);
  border: 1px solid var(--line); color: var(--muted);
  transition: color 0.12s, border-color 0.12s;
}
.settings-row:hover .sr-icon { color: var(--accent); border-color: var(--accent); }
.sr-icon svg { width: 16px; height: 16px; stroke-width: 1.7; }
.sr-text { min-width: 0; }
.sr-title { display: block; font-weight: 600; font-size: var(--fs-md); }
.sr-desc { display: block; font-size: var(--fs-sm); color: var(--muted); margin-top: 0.08rem; }
.sr-chevron { margin-left: auto; flex-shrink: 0; width: 16px; height: 16px; color: var(--muted); transition: color 0.12s, transform 0.12s; }

/* ---- Document builder ---- */
.builder2 { display: grid; grid-template-columns: minmax(380px, 460px) 1fr; gap: 1.6rem; align-items: start; }
@media (max-width: 1100px) { .builder2 { grid-template-columns: 1fr; } }
.builder2-controls { min-width: 0; display: flex; flex-direction: column; gap: var(--sp-4); }

/* NOTE: .bcard* and .toggle* used to live here — a second card style and a
   toggle-chip style, ~45 lines, that no template ever rendered (the builder
   uses plain .card). Removed rather than left to look like a house style
   somebody might adopt by accident. .color-row below sat inside that block and
   IS rendered, by settings/document.html, so it stays. */
.color-row { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.2rem; }
.color-row input[type="color"] {
  width: 46px; height: var(--control-h); padding: 2px; cursor: pointer;
  flex: none; border-radius: var(--radius-ctl);
}
.color-row input[type="text"] { flex: 0 1 10rem; margin: 0; }

/* NOTE: .builder2-actions is gone with the buttons it held — Save template and
   Back to settings live in the pinned .edit-bar now, the same one the quote
   editor uses, so the way out of a long form is always the same shape and
   always on screen. */

/* Preview column */
.builder2-preview {
  position: sticky; top: 1rem; border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--line); box-shadow: var(--shadow-md); background: var(--card);
}
.bpreview-bar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.6rem 0.85rem; background: var(--chrome); color: #fff;
}
.bpreview-title { font-weight: 600; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.7); }
/* Segmented switch — a rectangle, not a pill. */
.seg { display: inline-flex; padding: 2px; background: rgba(255, 255, 255, 0.1); border-radius: var(--radius-ctl); gap: 2px; }
.seg-btn {
  border: none; background: transparent; color: rgba(255, 255, 255, 0.72);
  font: inherit; font-size: var(--fs-sm); font-weight: 500; cursor: pointer;
  padding: 0.25rem 0.75rem; border-radius: var(--radius-sm); transition: background 0.12s, color 0.12s;
}
.seg-btn:hover { color: #fff; }
.seg-btn.is-on { background: #fff; color: var(--chrome); font-weight: 600; }
.bpreview-stage { padding: 1.1rem; background: var(--paper-bg); }
/* Sized off the viewport MINUS everything around it, so the sheet stands clear
   of the pinned Save bar instead of disappearing behind it.

   The 16rem is measured from the page's UNSTUCK position — the worst case, at
   the top of the page, before the column sticks: the head above it (~8rem),
   this panel's own bar and padding (~4.2rem), the action bar (3.4rem) and a
   little slack. Both terms scale with the viewport, so the gap under the sheet
   comes out the same on a laptop and on a big monitor; once the page scrolls
   and the column sticks at 1rem, there is simply more room than it needs. */
.bpreview-frame {
  display: block; width: 100%; height: calc(100vh - 16rem); min-height: 380px;
  border: none; border-radius: var(--radius-sm); background: #fff;
  box-shadow: 0 4px 16px rgba(16, 24, 40, 0.18);
}

/* ------------------------------------------------------------
   The mini document preview below mirrors the real printed PDF
   (print.css). It is deliberately NOT restyled — changing it here
   would make the preview stop matching what the customer receives.
   ------------------------------------------------------------ */
.doc-preview {
  --dp-accent: #1d64b8;
  background: #fff; color: #16202b; padding: 1.4rem 1.5rem;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25); font-size: 11px; line-height: 1.45;
  max-width: 640px; margin: 0 auto;
}
.doc-preview.dp-serif { font-family: "Source Serif 4", Georgia, serif; }
.dp-head { display: flex; justify-content: space-between; gap: 1rem; align-items: flex-start; }
.dp-head[data-align="center"] { text-align: center; align-items: center; }
.dp-head[data-align="center"] .dp-meta { text-align: center; }
.dp-head[data-align="right"] { flex-direction: row-reverse; }
.dp-company { display: flex; gap: 0.7rem; align-items: flex-start; }
.dp-company img { max-height: 54px; max-width: 130px; }
.dp-company strong { font-size: 13px; }
.dp-muted { color: #67737f; font-size: 10px; }
.dp-meta h2 {
  margin: 0 0 0.3rem; font-family: "Source Serif 4", Georgia, serif; color: var(--dp-accent);
  font-size: 20px; text-transform: none; letter-spacing: 0.02em;
}
.dp-metarow { display: flex; justify-content: flex-end; gap: 0.6rem; font-size: 10px; }
.dp-metarow span { color: #67737f; text-transform: uppercase; letter-spacing: 0.05em; }
.dp-rule { height: 3px; background: var(--dp-accent); margin: 0.6rem 0 0.7rem; }
.dp-vehicle { border: 1px solid #c9d2da; border-radius: 5px; padding: 0.4rem 0.6rem;
  background: #f7f9fb; margin-bottom: 0.7rem; font-size: 10.5px; }
.dp-intro { margin: 0 0 0.6rem; color: #33404d; }
.dp-lines { width: 100%; border-collapse: collapse; margin-bottom: 0.7rem; }
.dp-lines th { border-bottom: 1.5px solid var(--dp-accent); text-align: left;
  font-size: 8.5px; text-transform: uppercase; letter-spacing: 0.05em; padding: 0.25rem 0.35rem; }
.dp-lines td { border-bottom: 0.6px solid #dbe2e8; padding: 0.28rem 0.35rem; }
.dp-lines .num { text-align: right; font-variant-numeric: tabular-nums; }
.dp-detail { font-size: 9px; color: #67737f; margin-top: 0.1rem; }
.dp-bottom { display: flex; justify-content: space-between; gap: 1.5rem; align-items: flex-start; }
.dp-terms { flex: 1; }
.dp-terms b { font-size: 8.5px; text-transform: uppercase; letter-spacing: 0.08em; color: #67737f; }
.dp-terms p { margin: 0.1rem 0 1rem; font-size: 10px; color: #4a5866; }
.dp-sig { display: flex; gap: var(--sp-4); margin-top: 1.2rem; }
.dp-sig span { flex: 1; font-size: 8.5px; color: #67737f; text-transform: uppercase; }
.dp-line { display: block; border-bottom: 0.8px solid #16202b; height: 1.2rem; margin-bottom: 0.15rem; }
.dp-totals { border-collapse: collapse; min-width: 2.4in; font-size: 10.5px; }
.dp-totals td:first-child { color: #4a5866; padding: 0.1rem 0.9rem 0.1rem 0; }
.dp-totals td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
.dp-totals tr.dp-grand td { font-weight: 700; color: #16202b;
  border-top: 1.2px solid #16202b; padding-top: 0.2rem; }
.dp-footer { margin-top: 0.9rem; padding-top: 0.4rem; border-top: 0.6px solid #dbe2e8;
  font-size: 9px; color: #67737f; text-align: center; }

/* ---- Toasts ---- */
.toast-tray {
  position: fixed; bottom: 1.1rem; right: 1.1rem; z-index: 200;
  display: flex; flex-direction: column; gap: 0.5rem; max-width: min(360px, calc(100vw - 2rem));
}
.toast {
  background: var(--chrome); color: #fff; padding: 0.7rem 0.9rem; border-radius: var(--radius-ctl);
  box-shadow: var(--shadow-pop); font-size: var(--fs-md); cursor: pointer;
  opacity: 0; transform: translateY(8px); transition: opacity 0.22s, transform 0.22s;
  border-left: 3px solid rgba(255, 255, 255, 0.35);
}
.toast.in { opacity: 1; transform: translateY(0); }
.toast-success { border-left-color: #4ea87a; }
.toast-error { border-left-color: #e0685e; }
@media (prefers-reduced-motion: reduce) { .toast { transition: opacity 0.15s; transform: none; } }

/* ---- Empty states ---- */
.empty-state { text-align: center; padding: 2.4rem 1.2rem; max-width: 34rem; margin-inline: auto; }
.empty-icon {
  margin: 0 auto 0.7rem; display: flex; align-items: center; justify-content: center;
  color: var(--muted);
}
.empty-icon svg { width: 24px; height: 24px; stroke-width: 1.5; }
.empty-title { font-weight: 600; font-size: var(--fs-lg); letter-spacing: -0.012em; margin: 0 0 0.3rem; }
.empty-body { margin: 0 auto 1.1rem; max-width: 36ch; color: var(--muted); }

/* ---- Dashboard viz ---- */
.dash-top { display: grid; grid-template-columns: 1.3fr 1.3fr 0.9fr; gap: var(--sp-4); align-items: stretch; margin: 0.9rem 0 1.2rem; }
@media (max-width: 900px) { .dash-top { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .dash-top { grid-template-columns: 1fr; } }
.metric-card { display: flex; flex-direction: column; margin: 0; }
.metric-value { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.026em; font-variant-numeric: tabular-nums; line-height: 1.1; margin-top: 0.15rem; }
.metric-sub { font-size: var(--fs-sm); color: var(--muted); margin-bottom: 0.8rem; }
.dash-ministats { display: grid; grid-template-rows: 1fr 1fr; gap: var(--sp-4); }
@media (max-width: 620px) { .dash-ministats { grid-template-rows: none; grid-template-columns: 1fr 1fr; } }
.ministat {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 0.75rem 1.05rem;
  display: flex; flex-direction: column; justify-content: center;
}

.sparkline { width: 100%; height: 54px; margin-top: auto; display: block; overflow: visible; }
.spark-area { fill: var(--accent); opacity: 0.09; }
.spark-line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.spark-dot { fill: var(--accent); stroke: var(--card); stroke-width: 1.5; }

.aging { margin-top: auto; }
.aging-track { display: flex; gap: 2px; height: 10px; border-radius: var(--radius-sm); overflow: hidden; background: var(--line-soft); }
.aging-seg { display: block; }
.aging-seg.seg-empty { background: var(--line); }
.seg-current { background: var(--ok-ink); }
.seg-mid { background: var(--warn); }
.seg-old { background: var(--danger); }
.seg-overdue { background: var(--danger); }
.aging-legend { display: flex; flex-wrap: wrap; gap: 0.3rem 1.1rem; margin-top: 0.6rem; font-size: var(--fs-sm); color: var(--muted); }
.aging-legend b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.aging-legend .dot { display: inline-block; width: 8px; height: 8px; border-radius: var(--radius-sm); margin-right: 0.35rem; vertical-align: 0.02em; }

/* ---- Responsive tables ---- */
.table-scroll { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll > table.card { margin-bottom: 0; }
@media (max-width: 760px) {
  .table-scroll { margin-bottom: 1.1rem; border-radius: var(--radius); }
  .table-scroll > table.card { min-width: 620px; border-radius: 0; box-shadow: none; }
  /* A table that still scrolls sideways should say so — the scrollbar is the
     only thing telling you the remaining columns exist. */
  .table-scroll { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
  .table-scroll::-webkit-scrollbar { height: 7px; }
  .table-scroll::-webkit-scrollbar-track { background: transparent; }
  .table-scroll::-webkit-scrollbar-thumb { background: var(--line); border-radius: var(--radius-sm); }
}

.nowrap { white-space: nowrap; }

/* Sortable table headers */
th.sortable { padding: 0; }
th.sortable a {
  display: flex; align-items: center; gap: 0.3rem; padding: 0.58rem 0.8rem;
  color: inherit; text-decoration: none; white-space: nowrap;
}
th.sortable.num a { justify-content: flex-end; }
th.sortable a:hover { color: var(--accent); }
th.sortable.sorted a { color: var(--accent); }
.sort-arrow { font-size: 0.85em; }
.list-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; margin: 0.2rem 0 1rem; flex-wrap: wrap; color: var(--muted); font-size: var(--fs-sm); }
.pager { display: flex; gap: var(--sp-4); align-items: center; justify-content: center; margin: 1rem 0; flex-wrap: wrap; }
.pager-jump { display: flex; align-items: center; gap: 0.4rem; margin: 0; font-size: var(--fs-sm); }
.pager-jump label { color: var(--muted); margin: 0; }
.pager-jump input[type="number"] {
  width: 3.6rem; min-height: var(--control-h-sm); height: var(--control-h-sm);
  padding: 0 0.35rem; text-align: center;
  font-variant-numeric: tabular-nums; margin: 0;
}
.history-facts { margin-bottom: 0.9rem; }
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
  display: flex; gap: 0.7rem; padding: 0.4rem 0;
  border-bottom: 1px solid var(--line-soft); font-size: var(--fs-md); line-height: 1.4;
}
.timeline li:last-child { border-bottom: none; }
.tl-when { flex-shrink: 0; width: 4.5rem; color: var(--muted); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); padding-top: 0.05rem; }
.tl-what { min-width: 0; }

.line-config { border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-soft); margin: 0.7rem 0; overflow: hidden; }
.config-section { padding: 0.75rem 1rem; border-bottom: 1px solid var(--line-soft); }
.config-section:last-child { border-bottom: none; }
.config-section h4 {
  margin: 0 0 0.45rem; font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
}
/* ---- Segmented choice groups: selected = filled accent, nothing else ---- */
.pack-choices { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.4rem; }
.pack-choices button {
  padding: 0.3rem 0.75rem; border-radius: var(--radius-ctl); border: 1px solid var(--input-border);
  background: var(--card); color: var(--ink); cursor: pointer; font-size: var(--fs-md); font-weight: 500;
  transition: border-color 0.12s, background 0.12s, color 0.12s;
}
.pack-choices button:hover { border-color: var(--input-hover); background: var(--hover-bg); }
.pack-choices button.selected {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600;
}
.line-sub { font-size: var(--fs-sm); margin-top: 0.15rem; line-height: 1.35; color: var(--muted); }
.ro-cell {
  display: inline-block; padding: 0.12rem 0.5rem; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--bg-soft);
  font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--ink);
}

/* ---- Document line: the item cell ----
   The line leads with what it IS — brand, then the item, bold — with the
   identifiers under it in small type and the whole breakdown in a quiet block
   below that. SKU, PO# and Category were columns of their own until this;
   folding them in is what freed the width, and it puts every detail about a
   line inside the cell somebody is already reading. */
table.lines td.li-cell { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.li-top { display: flex; align-items: flex-start; gap: 0.6rem; }
.li-title { flex: 1; min-width: 0; line-height: 1.3; }
.li-mfr {
  display: block; font-weight: 600; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
}
.li-name { font-weight: 600; font-size: var(--fs-md); color: var(--ink); overflow-wrap: break-word; }
/* The row's own controls, kept out of the flow of what is being read. */
.li-acts { display: flex; align-items: center; gap: 0.3rem; flex: none; }
.li-meta {
  display: flex; flex-wrap: wrap; gap: 0.2rem 0.85rem; margin-top: 0.2rem;
  font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums;
}
/* Not a text box. It looks like the description panel it replaces because that
   is where the eye expects the detail, but nothing in it is editable — pricing
   a change has to go through the item window, which is the only thing that can
   work out what the change costs. */
.li-detail {
  margin-top: 0.5rem; padding: 0.5rem 0.65rem;
  background: var(--bg-soft); border-radius: var(--radius-sm);
}
.li-detail .spec-row { font-size: var(--fs-sm); line-height: 1.45; }
.li-detail .spec-row .spec-k { flex: 0 0 116px; }
.li-detail .line-charge { font-size: var(--fs-sm); }
.li-detail .line-detail { margin-top: 0.3rem; }
.li-detail > :first-child { margin-top: 0; }
.li-cat {
  display: inline-flex; align-items: center; gap: 0.4rem; margin: 0.5rem 0 0;
  font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--muted);
}
.li-cat select { margin: 0; font-size: var(--fs-sm); min-width: 9rem; }
/* Sits under the quantity, quiet enough to ignore while typing and there the
   moment you wonder. Wraps to its own two lines rather than widening the
   column — the td is nowrap, so this has to opt back out. */
.li-stock {
  display: block; margin-top: 0.3rem; white-space: normal;
  font-size: var(--fs-2xs); line-height: 1.35; color: var(--muted);
}
.li-stock strong {
  display: inline; margin-left: 0.3rem; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* A negative shelf is a real problem, not a rounding artefact — the whole
   figure goes red, value included. */
.li-stock.text-danger, .li-stock.text-danger strong { color: var(--danger); }

/* Fixed widths on the figures so the item cell takes everything left over —
   otherwise a long spec line squeezes the money into two digits a row. */
.lines .qty-col { width: 6.5rem; }
.lines .price-col { width: 7rem; }
.lines th.tax-col { width: 3.75rem; }
.lines .total-col { width: 7.5rem; }
.lines .icon-btn { padding: 0.2rem 0.45rem; line-height: 1.2; }

/* Itemized line display */
.line-specs { margin-bottom: 0.2rem; }
.spec-row { display: flex; gap: 0.5rem; align-items: flex-start; font-size: var(--fs-md); line-height: 1.5; }
.spec-row .spec-k { flex: 0 0 116px; color: var(--muted); overflow-wrap: break-word; }
.spec-row .spec-v { flex: 1; min-width: 0; overflow-wrap: break-word; }
.spec-row .lc-amt { flex: none; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A serial is read off a sticker and typed into a search box, so it is set as
   an identifier rather than as prose: the digits line up and a 0 cannot be
   mistaken for an O. */
.spec-row .spec-v.li-serial { font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
.line-charge {
  display: flex; justify-content: space-between; gap: 0.6rem;
  font-size: var(--fs-sm); color: var(--ink); margin-top: 0.12rem; padding-left: 0.7rem; position: relative;
}
.line-charge::before { content: "+"; position: absolute; left: 0; color: var(--muted); }
.line-charge .lc-amt { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.line-detail { font-size: var(--fs-sm); color: var(--muted); margin-top: 0.1rem; }

textarea.autogrow { resize: none; overflow: hidden; min-height: 34px; line-height: 1.35; white-space: pre-wrap; word-break: break-word; }
.line-desc-ta { min-width: 190px; margin-top: 0; }
.prev-vehicles { margin-bottom: 0.7rem; }
.chip-btn { cursor: pointer; font: inherit; font-size: var(--fs-sm); color: var(--ink); }
.chip-btn:hover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }

/* ---- Dashboard ---- */
.kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(165px, 1fr));
  gap: var(--sp-3); margin: var(--sp-3) 0 var(--sp-4);
}
.kpi { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.7rem 0.9rem; box-shadow: var(--shadow); }
.kpi-label {
  display: flex; align-items: center; gap: 0.5rem; font-size: var(--fs-2xs); color: var(--muted);
  font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.65rem;
}
.kpi-ic { width: 16px; height: 16px; display: grid; place-items: center; color: var(--muted); flex: none; }
.kpi-ic svg { width: 14px; height: 14px; stroke-width: 1.75; }
.kpi-value { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.026em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.kpi-sub { margin-top: 0.4rem; font-size: var(--fs-sm); color: var(--muted); }
.kpi-trend { font-weight: 600; font-variant-numeric: tabular-nums; }
.kpi-trend.up { color: var(--ok-ink); }
.kpi-trend.down { color: var(--danger); }
.kpi-trend.flat { color: var(--muted); }
.kpi-warn .kpi-ic { color: var(--danger); }
.kpi-warn .kpi-value { color: var(--danger); }

.dash-charts { display: grid; grid-template-columns: 1.5fr 1fr; gap: var(--sp-4); margin-bottom: 1.1rem; align-items: start; }
.dash-aging-total { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; margin: 0.1rem 0 0.9rem; }

.bars { display: flex; align-items: flex-end; gap: 0.7rem; padding-top: 1.3rem; }
.bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 0.45rem; height: 100%; min-width: 0; }
/* The track used to be a filled grey column, so a month with no money showed a
   full-height grey bar — louder than the months that had some. It is a baseline
   now, and only the value has weight. */
.bar-track { position: relative; width: 100%; max-width: 46px; flex: 1; background: none; border-bottom: 1px solid var(--line); }
.bar-fill {
  position: absolute; bottom: 0; left: 0; right: 0; min-height: 3px; border-radius: var(--radius-sm);
  background: var(--accent); background: color-mix(in srgb, var(--accent) 38%, var(--card));
}
.bar-fill.bar-current { background: var(--accent); }
.bar-val { position: absolute; top: -1.15rem; left: -0.4rem; right: -0.4rem; text-align: center; font-size: var(--fs-2xs); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bar-label { font-size: var(--fs-2xs); color: var(--muted); font-weight: 500; }

.bar-pair { display: flex; align-items: flex-end; gap: 4px; width: 100%; max-width: 56px; flex: 1; }
.bar-pair .bar-track { flex: 1; height: 100%; max-width: none; border-bottom: 0; }
.bar-pair { border-bottom: 1px solid var(--line); }
.bar-fill.bar-a { background: var(--accent); }
.bar-fill.bar-b { background: var(--ok-ink); }
.bars-legend { display: flex; gap: var(--sp-4); justify-content: center; margin-top: 0.65rem; font-size: var(--fs-xs); color: var(--muted); }
.bars-legend .lg { display: inline-block; width: 9px; height: 9px; border-radius: var(--radius-sm); margin-right: 0.35rem; vertical-align: -1px; }
.bars-legend .lg-a { background: var(--accent); }
.bars-legend .lg-b { background: var(--ok-ink); }

.pnl, .pipeline { width: 100%; border-collapse: collapse; }
.pnl td, .pipeline td { padding: 0.42rem 0.6rem; border-bottom: 1px solid var(--line-soft); }
.pnl tr:last-child td, .pipeline tr:last-child td { border-bottom: none; }
.pnl .num, .pipeline .num { font-variant-numeric: tabular-nums; }
/* A value that is words, not figures. `num` right-aligns for a column of money;
   applied to a product name it wrapped the name ragged-left against the panel's
   right edge. A text value reads left-to-right from a label column sized to the
   longest label. */
.pnl:has(td.val) td:first-child { width: 1%; white-space: nowrap; padding-right: 1rem; }
.pnl td.val { text-align: left; }
.pnl-neg td { color: var(--muted); }
/* A brand sitting under its rolled-up group total (card types on the day end). */
.pnl-child td { color: var(--muted); border-bottom: none; padding-top: 0.12rem; padding-bottom: 0.12rem; }
.pnl-child td:first-child { padding-left: 1.1rem; }
.pnl-child + tr:not(.pnl-child) td { border-top: 1px solid var(--line-soft); }
.pnl-sum td { font-weight: 600; border-top: 1px solid var(--line); border-bottom: none; }
.pipeline .pl-head td {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); font-weight: 600; padding-top: 0.75rem; border-bottom: none;
}

.ar-figures { display: flex; gap: 1.9rem; margin: 0.2rem 0 1rem; }
.ar-figures-wide { gap: 3rem; }
.ar-table { margin-top: 1rem; }
.ar-table td, .ar-table th { padding: 0.4rem 0.5rem; }
.ar-fig { display: flex; flex-direction: column; gap: 0.15rem; }
.ar-num { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.022em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.ar-num.ar-over { color: var(--danger); }
.text-danger { color: var(--danger); }

.dash-body { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--sp-4); align-items: start; margin-top: 1.1rem; }
.dash-main, .dash-rail { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }
.dash-main > .card, .dash-rail > .card { margin-bottom: 0; }
@media (max-width: 950px) { .dash-body { grid-template-columns: 1fr; } }

.feed { list-style: none; margin: 0; padding: 0; }
.feed-row { padding: 0.55rem 0; border-bottom: 1px solid var(--line-soft); }
.feed-row:last-child { border-bottom: none; }
.feed-main { display: flex; justify-content: space-between; gap: 0.6rem; align-items: baseline; }
.feed-main a { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-amt { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.feed-sub { font-size: var(--fs-sm); color: var(--muted); margin-top: 0.12rem; }
.feed-empty { padding: 0.4rem 0; color: var(--muted); }

@media (max-width: 900px) { .kpi-grid { grid-template-columns: 1fr 1fr; } .dash-charts { grid-template-columns: 1fr; } }
/* One KPI per row turned the top of the dashboard into four screens of
   scrolling. Keep the 2×2 block and scale the type down to fit it. */
@media (max-width: 620px) {
  .kpi-grid { grid-template-columns: 1fr 1fr; gap: 0.7rem; }
  .kpi { padding: 0.8rem 0.85rem; }
  .kpi-value { font-size: var(--fs-lg); letter-spacing: -0.02em; }
  .kpi-sub { margin-top: 0.3rem; font-size: var(--fs-xs); }
}

/* ---- Calendar ----
   A month grid is read by scanning, not by reading: the eye lands on a cell,
   takes in how busy it is, and only then reads a title. So the day number is
   quiet, the events carry the color, and the grid keeps one rhythm — cells all
   the same height, because a grid whose rows jump around stops being a grid and
   becomes a list of unequal boxes. The three-event cap in the template is what
   makes that rhythm possible. */
.cal-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 0.7rem; margin: 0.3rem 0 1rem;
}
/* The month stepper reads as one control, not three loose buttons sitting near
   each other: shared border, dividers between, one radius round the outside. */
.cal-nav { display: flex; align-items: stretch; }
.cal-nav .cal-step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; border: 1px solid var(--line); background: var(--card);
  color: var(--ink-soft); text-decoration: none; font-size: var(--fs-lg); line-height: 1;
}
.cal-nav .cal-step:hover { background: var(--hover-bg); color: var(--ink); }
.cal-nav .cal-step:first-child { border-radius: var(--radius-ctl) 0 0 var(--radius-ctl); }
.cal-nav .cal-step:last-child { border-radius: 0 var(--radius-ctl) var(--radius-ctl) 0; border-left: 0; }
.cal-month {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 0.9rem; min-width: 168px;
  border-block: 1px solid var(--line);
  font-size: var(--fs-lg); font-weight: 600; letter-spacing: -0.014em;
  font-variant-numeric: tabular-nums;
}
.cal-meta { display: flex; align-items: center; gap: 0.7rem; font-size: var(--fs-sm); color: var(--muted); }

.cal-grid {
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--card);
}
.cal-head {
  display: grid; grid-template-columns: repeat(7, 1fr);
  background: var(--thead-bg); border-bottom: 1px solid var(--line);
}
.cal-head span {
  padding: 0.45rem 0.6rem; font-size: var(--fs-2xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted);
}
/* 1fr, not auto: every week is the same height whatever it holds. */
.cal-week { display: grid; grid-template-columns: repeat(7, 1fr); grid-auto-rows: minmax(116px, 1fr); }
.cal-week:not(:last-child) { border-bottom: 1px solid var(--line-soft); }
.cal-day {
  position: relative; min-height: 0; min-width: 0;
  padding: 0.3rem 0.3rem 0.35rem;
  border-right: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 0.25rem;
  transition: background 0.1s;
}
.cal-day:nth-child(7n) { border-right: none; }
/* Saturday and Sunday get a whisper of tint. It is not decoration: a month is
   scanned by week, and the eye needs the week boundary without counting cells. */
.cal-day:nth-child(7n+1), .cal-day:nth-child(7n) { background: var(--bg-soft); }
.cal-day:hover { background: var(--hover-bg); }
.cal-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Days outside the month stay legible — anything on them is still real work —
   but recede, so the month reads as one block rather than six weeks of noise. */
.cal-other { background: var(--bg-soft); }
.cal-other .cal-daynum { color: var(--muted); }
.cal-other .cal-events { opacity: 0.55; }

.cal-daynum {
  align-self: flex-start; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.55rem; height: 1.55rem; padding: 0 0.3rem;
  border-radius: 999px;                    /* a circle, the universal "today" */
  font-size: var(--fs-sm); font-weight: 500; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  transition: background 0.1s, color 0.1s;
}
.cal-daynum:hover { background: var(--hover-bg); color: var(--ink); }
.cal-today .cal-daynum { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.cal-today .cal-daynum:hover { background: var(--accent-hover); color: var(--accent-ink); }

/* The "click an empty day to add" affordance. It was clickable already but said
   so nowhere, which is a feature nobody finds. Hidden until hover so it costs
   the grid nothing at rest, and hidden from assistive tech, which already has
   the cell's own label. */
.cal-day::after {
  content: "+"; position: absolute; top: 0.3rem; right: 0.4rem;
  font-size: var(--fs-md); line-height: 1.55rem; color: var(--muted);
  opacity: 0; transition: opacity 0.1s; pointer-events: none;
}
.cal-day:hover::after { opacity: 0.65; }

.cal-events { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow: hidden; }
.cal-event {
  display: flex; align-items: baseline; gap: 0.35rem;
  padding: 0.16rem 0.35rem 0.16rem 0.4rem;
  border-radius: var(--radius-sm);
  border: 0; border-left: 3px solid var(--ev-bar, var(--muted));
  background: var(--ev-bg, var(--hover-bg)); color: var(--ev-ink, var(--ink));
  font: inherit; font-size: var(--fs-xs); line-height: 1.45;
  text-align: left; text-decoration: none; cursor: pointer;
  min-width: 0; width: 100%;
}
.cal-event:hover { filter: brightness(0.96); }
.cal-event:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The title is the content and the time is its label, so the weight goes to the
   title. It was the other way round, which made a column of bold clock times
   the first thing the eye found in every cell. */
.cal-ev-time { flex: none; font-weight: 400; opacity: 0.75; font-variant-numeric: tabular-nums; }
.cal-ev-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.cal-event-allday .cal-ev-time { text-transform: uppercase; letter-spacing: 0.04em; font-size: var(--fs-2xs); }

.cal-more {
  align-self: flex-start; padding: 0.1rem 0.35rem;
  font-size: var(--fs-2xs); font-weight: 600; color: var(--muted);
  text-decoration: none; border-radius: var(--radius-sm);
}
.cal-more:hover { background: var(--hover-bg); color: var(--accent); }

/* Capacity for the day. A hairline pinned to the bottom of the cell rather than
   a bar under the number: it answers "how full is this day" at a glance from
   across the month, and never competes with the date for the same corner. */
.cal-load {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--line-soft); overflow: hidden;
}
.cal-load-fill { display: block; height: 100%; background: var(--accent); }
.cal-load.is-over .cal-load-fill { background: var(--danger); }

/* ---- Event colors ----
   A left bar plus a soft ground, not a saturated fill: the fill would have to
   fight for text contrast at every one of eight hues in two themes, and would
   lose somewhere. This way the hue is carried by 3px of bar that has no text on
   it, and the title stays at full reading contrast whatever color it is. */
.cal-c-default { --ev-bar: var(--muted); --ev-bg: var(--hover-bg); --ev-ink: var(--ink); }
.cal-c-slate   { --ev-bar: #64748b; --ev-bg: #eef1f5; --ev-ink: #334155; }
.cal-c-blue    { --ev-bar: #2563eb; --ev-bg: #e7eefc; --ev-ink: #1e40af; }
.cal-c-teal    { --ev-bar: #0d9488; --ev-bg: #e2f5f2; --ev-ink: #115e59; }
.cal-c-green   { --ev-bar: #16a34a; --ev-bg: #e6f6ec; --ev-ink: #166534; }
.cal-c-amber   { --ev-bar: #d97706; --ev-bg: #fdf1de; --ev-ink: #92400e; }
.cal-c-red     { --ev-bar: #dc2626; --ev-bg: #fdeaea; --ev-ink: #991b1b; }
.cal-c-purple  { --ev-bar: #7c3aed; --ev-bg: #f0e9fd; --ev-ink: #5b21b6; }
.cal-c-pink    { --ev-bar: #db2777; --ev-bg: #fce8f1; --ev-ink: #9d174d; }
[data-theme="dark"] .cal-c-slate  { --ev-bar: #94a3b8; --ev-bg: #1e293b; --ev-ink: #cbd5e1; }
[data-theme="dark"] .cal-c-blue   { --ev-bar: #60a5fa; --ev-bg: #17273c; --ev-ink: #bfdbfe; }
[data-theme="dark"] .cal-c-teal   { --ev-bar: #2dd4bf; --ev-bg: #0f2e2b; --ev-ink: #99f6e4; }
[data-theme="dark"] .cal-c-green  { --ev-bar: #4ade80; --ev-bg: #122c1c; --ev-ink: #bbf7d0; }
[data-theme="dark"] .cal-c-amber  { --ev-bar: #fbbf24; --ev-bg: #33240c; --ev-ink: #fde68a; }
[data-theme="dark"] .cal-c-red    { --ev-bar: #f87171; --ev-bg: #331416; --ev-ink: #fecaca; }
[data-theme="dark"] .cal-c-purple { --ev-bar: #a78bfa; --ev-bg: #241a3d; --ev-ink: #ddd6fe; }
[data-theme="dark"] .cal-c-pink   { --ev-bar: #f472b6; --ev-bg: #331428; --ev-ink: #fbcfe8; }

/* Scheduled documents keep the app's status colors — they are not free to be
   recolored, because the color IS the status. */
.cal-event.st-open, .cal-event.st-sent { --ev-bar: var(--ok-ink); --ev-bg: var(--ok-soft); --ev-ink: var(--ok-ink); }
.cal-event.st-draft { --ev-bar: var(--muted); --ev-bg: var(--line-soft); --ev-ink: var(--ink-soft); }
.cal-event.st-paid, .cal-event.st-accepted { --ev-bar: var(--info-ink); --ev-bg: var(--info-soft); --ev-ink: var(--info-ink); }

/* ---- The color picker in the event window ---- */
.cal-colors { border: 0; padding: 0; margin: 0.15rem 0 0.9rem; display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.cal-colors legend {
  width: 100%; padding: 0; margin-bottom: 0.35rem;
  /* The app's <legend> is an uppercase section heading; here the fieldset is a
     field among fields, so it has to match "Title" and "Date" beside it rather
     than shout over them. */
  font-size: var(--fs-sm); font-weight: 500; color: var(--ink);
  text-transform: none; letter-spacing: normal;
}
.cal-swatch { position: relative; margin: 0; cursor: pointer; line-height: 0; }
/* Off-screen, not display:none — a hidden input is not focusable, and the whole
   control would then be unreachable from the keyboard. */
.cal-swatch input {
  position: absolute; width: 1px; height: 1px; margin: 0; padding: 0;
  opacity: 0; pointer-events: none;
}
.cal-swatch span {
  display: block; width: 24px; height: 24px; border-radius: 999px;
  background: var(--ev-bar, var(--muted));
  box-shadow: inset 0 0 0 1px rgba(16, 24, 40, 0.12);
  transition: transform 0.1s;
}
.cal-swatch:hover span { transform: scale(1.1); }
.cal-swatch input:checked + span { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--ev-bar, var(--muted)); }
.cal-swatch input:focus-visible + span { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--accent); }
/* "Default" means no color, so it is drawn as an absence — a hollow ring. As a
   filled grey circle it was indistinguishable from the slate swatch next to it,
   which made the first choice in the row look like a duplicate of the second. */
.cal-swatch.cal-c-default span {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--line);
}
.cal-swatch.cal-c-default input:checked + span {
  box-shadow: inset 0 0 0 2px var(--muted), 0 0 0 2px var(--card), 0 0 0 4px var(--muted);
}

@media (max-width: 720px) {
  /* A seven-column month is unreadable on a phone; the day view is the answer
     there, and this keeps the grid usable enough to reach it. */
  .cal-week { grid-auto-rows: minmax(78px, 1fr); }
  .cal-day { padding: 0.2rem; }
  .cal-ev-time { display: none; }
  .cal-head span { padding: 0.35rem 0.2rem; text-align: center; }
}

/* ---- Single-day (agenda) view ----
   One list, hairline-ruled, rather than a stack of boxed cards each wearing a
   coloured bar down its left edge. Five cards for five one-line events is five
   times the chrome the content needs, and the bar was the loudest thing on the
   row while saying nothing: every event that had not been given a colour still
   got one, so the stripe read as decoration and the one real colour in the list
   had nothing to stand out against.

   Full width, matching the hour grid above it and every other list in the app.
   It was capped at a reading measure for one pass, which left it visibly
   narrower than the grid showing the SAME day directly above — two widths for
   one thing. Right-aligned actions across a wide row are the app's own list
   convention, not the problem the bars were. */
.cal-day-date { display: block; font-size: var(--fs-md); font-weight: 400; color: var(--muted); margin-top: 0.1rem; }
.cal-day-view {
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
}
.agenda-row {
  display: grid; grid-template-columns: 84px minmax(0, 1fr) auto; gap: 1rem;
  align-items: start; padding: 0.7rem 0.9rem;
  border-top: 1px solid var(--line-soft);
}
.agenda-row:first-child { border-top: 0; }
.agenda-row:hover { background: var(--hover-bg); }
@media (max-width: 700px) {
  .agenda-row { grid-template-columns: 1fr; gap: 0.5rem; }
  .agenda-side { flex-direction: row; align-items: center; gap: 0.6rem; }
}
.agenda-when { display: flex; flex-direction: column; gap: 0.1rem; }
.agenda-time { font-weight: 600; font-size: var(--fs-md); white-space: nowrap; font-variant-numeric: tabular-nums; }
.agenda-ampm { font-size: var(--fs-xs); font-weight: 500; margin-left: 0.1rem; color: var(--muted); }
.agenda-allday-label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 600; }
.agenda-end { font-size: var(--fs-xs); white-space: nowrap; color: var(--muted); }
.agenda-title { font-weight: 600; display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.agenda-title a { text-decoration: none; }
/* The chosen color, and ONLY when one was chosen. A dot beside the title is
   enough to group a day by color at a glance, and an event nobody colored shows
   nothing at all — which is what makes the ones that are colored mean something. */
.agenda-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--ev-bar, var(--muted)); }
.agenda-meta { font-size: var(--fs-sm); margin-top: 0.15rem; color: var(--muted); }
.agenda-notes { font-size: var(--fs-sm); margin-top: 0.3rem; white-space: pre-line; color: var(--muted); }
.agenda-lines { margin: 0.4rem 0 0; padding-left: 1.1rem; font-size: var(--fs-sm); color: var(--muted); }
.agenda-lines li { margin-bottom: 0.1rem; }
.agenda-side { display: flex; flex-direction: column; align-items: flex-end; gap: 0.35rem; white-space: nowrap; }
.agenda-amt { font-weight: 600; font-size: var(--fs-md); font-variant-numeric: tabular-nums; }
.agenda-out { font-size: var(--fs-sm); color: var(--muted); }
/* A scheduled job's status is already spelled out in the tag beside its number,
   so it needs no stripe of its own to repeat it in a second alphabet. */

/* ---- Hour grid (day view) ---- */
.daygrid { padding: 0.5rem 0.75rem 0.75rem; overflow: hidden; }
.daygrid-body { position: relative; }
.daygrid-lines { position: absolute; inset: 0; }
.daygrid-hour { position: relative; border-top: 1px solid var(--line); }
.daygrid-hour:last-child { border-bottom: 1px solid var(--line); }
.daygrid-hour::after { content: ""; position: absolute; left: 58px; right: 0; top: 50%; border-top: 1px dashed var(--line-soft); }
.daygrid-label {
  position: absolute; left: 0; top: -0.62rem; width: 50px; text-align: right;
  font-size: var(--fs-2xs); color: var(--muted); background: var(--card); padding-right: 0.35rem;
}
.daygrid-hour:first-child .daygrid-label { top: 0; }
.daygrid-events { position: absolute; inset: 0 0 0 58px; }
.daygrid-block {
  position: absolute; overflow: hidden; display: flex; flex-direction: column; gap: 0.02rem;
  padding: 0.2rem 0.4rem; border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-xs);
  text-align: left; text-decoration: none; cursor: pointer;
  background: var(--accent-soft); color: var(--ink);
  border: 1px solid var(--line); border-left: 2px solid var(--accent);
  box-sizing: border-box; margin-right: 4px;
}
.daygrid-block:hover { filter: brightness(0.97); z-index: 2; }
.dg-time { font-weight: 600; }
.dg-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dg-sub { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.daygrid-block.st-open, .daygrid-block.st-sent { background: var(--ok-soft); border-left-color: var(--ok-ink); }
.daygrid-block.st-draft { background: var(--line-soft); border-left-color: var(--muted); }
.daygrid-block.st-paid, .daygrid-block.st-accepted { background: var(--info-soft); border-left-color: var(--info-ink); }
.dg-event {
  background: var(--ev-bg, var(--hover-bg)); color: var(--ev-ink, var(--ink));
  border-left-color: var(--ev-bar, var(--muted));
}
.dg-short { flex-direction: row; align-items: baseline; gap: 0.35rem; }
.dg-short .dg-sub { display: none; }

.preview-cta { display: none; }
@media (max-width: 820px) {
  .reader-pdf iframe, iframe.po-frame { display: none; }
  .reader-pdf { height: auto; }
  .preview-cta { display: block; padding: 1.4rem 1.2rem; text-align: center; }
  .preview-cta-btns { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; margin-top: 0.7rem; }
}
.daygrid-now { position: absolute; left: 0; right: 0; height: 0; border-top: 2px solid var(--danger); z-index: 3; }
.daygrid-now span { position: absolute; left: -4px; top: -4px; width: 8px; height: 8px; border-radius: 50%; background: var(--danger); }
.daygrid-allday {
  display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 0.5rem;
  padding: 0.45rem 0.75rem; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
}
.daygrid-allday-label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 600; padding-top: 0.2rem; min-width: 50px; }
.daygrid-allday-items { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.daygrid-chip {
  font: inherit; font-size: var(--fs-xs); cursor: pointer; padding: 0.2rem 0.55rem;
  border-radius: var(--radius-sm);
  background: var(--ev-bg, var(--hover-bg)); color: var(--ev-ink, var(--ink));
  border: 1px solid transparent; border-left: 2px solid var(--ev-bar, var(--muted));
}
.day-detail-head { margin: 1.4rem 0 0.6rem; }

.cal-day { cursor: pointer; transition: background 0.1s; }
.cal-day:hover { background: var(--hover-bg); }
.cal-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal-event { cursor: pointer; }

.cal-modal .cal-field { display: block; margin-bottom: 0.75rem; font-size: var(--fs-sm); font-weight: 500; }
.cal-modal .cal-field input, .cal-modal .cal-field textarea { width: 100%; margin-top: 0.25rem; }
.cal-modal .cal-allday-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; font-weight: 500; }
.cal-modal .cal-allday-row input { width: auto; margin: 0; }
.cal-time-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.8rem; }
.cal-time-row[hidden] { display: none; }
.cal-actions-spacer { flex: 1; }

@media (max-width: 720px) {
  .cal-day { min-height: 74px; padding: 0.2rem; }
  .cal-ev-title { display: none; }
  /* A day cell is ~45px wide on a phone. The time was flex:none, so it spilled
     over the cell's right border into the next day — let it ellipsis instead. */
  .cal-event { padding: 0.14rem 0.25rem; font-size: var(--fs-2xs); }
  .cal-ev-time { font-weight: 600; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* ---- Reports ---- */
.stat.stat-accent { background: var(--accent-soft); border-color: var(--accent-soft); }
.report-sections { display: flex; flex-direction: column; gap: 1.4rem; margin-top: 1rem; }
/* Group heading on a hub screen. Shared with the settings hub so the two
   read as the same kind of page. */
.report-cat, .settings-group > h2 {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); font-weight: 600; margin: 0 0 var(--sp-2);
}
.report-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 0.8rem; }
.report-card {
  display: flex; align-items: flex-start; gap: 0.85rem; padding: 0.9rem 1rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  text-decoration: none; color: var(--ink); box-shadow: var(--shadow);
  transition: border-color 0.12s, box-shadow 0.12s;
}
.report-card:hover { border-color: var(--accent); background: var(--hover-bg); }
.report-ic {
  flex: none; display: grid; place-items: center; width: 30px; height: 30px; margin-top: 1px;
  border-radius: var(--radius-sm); background: var(--bg-soft);
  border: 1px solid var(--line); color: var(--muted);
  transition: color 0.12s, border-color 0.12s;
}
.report-card:hover .report-ic { color: var(--accent); border-color: var(--accent); }
.report-ic svg { width: 16px; height: 16px; stroke-width: 1.7; }
.report-txt { display: flex; flex-direction: column; gap: 0.12rem; min-width: 0; }
.report-t { font-weight: 600; font-size: var(--fs-md); letter-spacing: -0.008em; }
/* Two lines, always: descriptions of three different lengths gave one row of
   cards three different heights. */
.report-d {
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.report-range {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 0.7rem 1rem; margin: 0.6rem 0 1.2rem; padding: 0.65rem 0.8rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
}
/* One joined strip rather than four separate buttons with air between them —
   they are one choice, so they read as one control. */
.report-presets { display: inline-flex; flex-wrap: wrap; gap: 0; }
.report-presets .chip-btn {
  display: inline-flex; align-items: center; min-height: var(--control-h);
  padding: 0.3rem 0.8rem; border: 1px solid var(--input-border); border-radius: 0;
  margin-left: -1px; background: var(--card); text-decoration: none;
  white-space: nowrap; color: var(--ink-soft);
}
.report-presets .chip-btn:first-child { margin-left: 0; border-radius: var(--radius-ctl) 0 0 var(--radius-ctl); }
.report-presets .chip-btn:last-child { border-radius: 0 var(--radius-ctl) var(--radius-ctl) 0; }
.report-presets .chip-btn.on { position: relative; z-index: 1; }
.report-presets .chip-btn:hover { border-color: var(--input-hover); background: var(--hover-bg); color: var(--ink); }
.report-presets .chip-btn.on { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.report-range-dates { display: flex; align-items: flex-end; gap: 0.6rem; }
.report-range-dates label { margin: 0; display: flex; flex-direction: column; gap: 0.2rem; font-size: var(--fs-xs); }
.report-range-dates input { margin: 0; }
/* Run submits the two fields beside it, so it is a full-size control, not a
   small in-row action. */
/* (Removed: .report-range-dates .btn.small used to re-inflate .small to 34px.) */

/* Day-end close-out: one day at a time, so the range picker is replaced by a
   prev/next stepper with the day itself as the headline. */
.day-nav {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.7rem;
  margin: 0.6rem 0 1.2rem; padding: 0.55rem 0.8rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
}
.day-nav .chip-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--control-h); min-width: var(--control-h); padding: 0.3rem 0.7rem;
  border: 1px solid var(--input-border); border-radius: var(--radius-ctl);
  background: var(--card); text-decoration: none; color: var(--ink-soft);
}
.day-nav .chip-btn:hover { border-color: var(--input-hover); background: var(--hover-bg); color: var(--ink); }
.day-nav-now { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.25; }
.day-nav-now strong { font-size: var(--fs-lg); letter-spacing: -0.012em; }
.day-nav-now .muted { font-size: var(--fs-xs); }
.day-nav label { margin: 0; display: flex; flex-direction: column; gap: 0.2rem; font-size: var(--fs-xs); }
.day-nav input { margin: 0; }
/* (Removed: .day-nav .btn.small used to re-inflate .small to 34px.) */
@media (max-width: 620px) {
  .day-nav .spacer { display: none; }
  .day-nav-now { flex: 1; min-width: 0; }
}

/* ---- Owner portal (/admin) ---- */
.sys-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--sp-4); align-items: start; margin-top: 1rem; }
.sys-grid > .card { margin-bottom: 0; }
.svc-strip { display: flex; flex-wrap: wrap; gap: 1.2rem 2rem; padding: 0.9rem 1.25rem; margin-bottom: 1.1rem; }
.svc { display: flex; align-items: center; gap: 0.5rem; font-weight: 500; font-size: var(--fs-md); }
.status-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 20%, transparent); }
.sd-up { background: var(--ok-ink); color: var(--ok-ink); }
.sd-down { background: var(--danger); color: var(--danger); }
.sd-unknown { background: var(--muted); color: var(--muted); }
.cert-days { font-size: var(--fs-2xl); font-weight: 600; line-height: 1; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; margin: 0.2rem 0 0.9rem; display: flex; align-items: baseline; gap: 0.5rem; }
.cert-days span { font-size: var(--fs-2xs); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.cert-days.ok { color: var(--ok-ink); }
.cert-days.warn { color: var(--warn); }
.cert-days.crit { color: var(--danger); }
.sys-kv { margin: 0; }
.sys-kv > div { display: flex; justify-content: space-between; gap: 1rem; padding: 0.36rem 0; border-bottom: 1px solid var(--line-soft); }
.sys-kv > div:last-child { border-bottom: none; }
.sys-kv dt { color: var(--muted); font-size: var(--fs-sm); margin: 0; font-weight: 400; }
.sys-kv dd { margin: 0; text-align: right; font-weight: 500; overflow-wrap: anywhere; }
.meter-row { display: flex; justify-content: space-between; font-size: var(--fs-sm); margin: 0.85rem 0 0.3rem; }
.meter { height: 6px; border-radius: var(--radius-sm); background: var(--line-soft); overflow: hidden; }
.meter-fill { height: 100%; background: var(--ok-ink); border-radius: var(--radius-sm); }
.meter-fill.meter-warn { background: var(--warn); }
.meter-fill.meter-crit { background: var(--danger); }
.mini-table { width: 100%; border-collapse: collapse; margin-top: 0.6rem; font-size: var(--fs-sm); }
.mini-table th { text-align: left; color: var(--muted); font-weight: 600; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; padding: 0.3rem; border-bottom: 1px solid var(--line); }
.mini-table td { padding: 0.36rem 0.3rem; border-bottom: 1px solid var(--line-soft); }
.mini-table tr:last-child td { border-bottom: none; }
.mono { font-family: var(--font-mono); font-size: 0.92em; }
.small { font-size: var(--fs-sm); }
.sub-h { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 600; margin: 1rem 0 0.2rem; }
.sec-stats, .data-grid { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 0.3rem 0 0.6rem; }
.data-grid { gap: 1.2rem 1.7rem; }
.sec-stats > div, .data-grid > div { display: flex; flex-direction: column; gap: 0.1rem; }
.sec-num { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.022em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.posture { list-style: none; margin: 0; padding: 0; }
.posture li { display: grid; grid-template-columns: 1.3rem 1fr auto; align-items: center; gap: 0.5rem; padding: 0.34rem 0; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-md); }
.posture li:last-child { border-bottom: none; }
.posture .p-mark { font-weight: 700; text-align: center; }
.posture .p-ok .p-mark { color: var(--ok-ink); }
.posture .p-no .p-mark { color: var(--muted); }
.posture .p-label { font-weight: 500; }
.posture .p-note { font-size: var(--fs-xs); text-align: right; color: var(--muted); }
@media (max-width: 460px) { .posture li { grid-template-columns: 1.3rem 1fr; } .posture .p-note { display: none; } }

/* ---- Mobile refinements ---- */
@media (max-width: 560px) {
  .report-range { flex-direction: column; align-items: stretch; }
  .report-range-dates { flex-wrap: wrap; }
  .report-range-dates label { flex: 1 1 46%; }
  .report-range-dates input[type="date"] { width: 100%; }
  .report-range-dates .btn { flex: 1 1 100%; margin-top: 0.15rem; }
}
@media (max-width: 720px) {
  .user-table.stack-mobile td.col-actions { margin-top: 0.5rem; padding-top: 0.55rem; border-top: 1px solid var(--line-soft); }
  .user-table.stack-mobile td.col-actions .row-tools { justify-content: flex-start; width: 100%; }
}

/* ---- Find-or-add browse modal ---- */
.bm-section { margin-top: 1.1rem; padding-top: 0.9rem; border-top: 1px solid var(--line-soft); }
.bm-section h4 { margin: 0 0 0.5rem; font-weight: 600; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.bm-create { background: var(--bg-soft); border-radius: var(--radius); padding: 0.9rem 1rem 1rem; border-top: none; }

.browse-crumbs { display: flex; gap: 0.35rem; align-items: center; flex-wrap: wrap; margin: 0.4rem 0 0.7rem; }
.browse-crumbs button {
  background: none; border: none; color: var(--accent); cursor: pointer;
  padding: 0.1rem 0.2rem; font: inherit; font-weight: 500; font-size: var(--fs-md);
  text-transform: none; letter-spacing: -0.006em;
}
.browse-crumbs button:hover { text-decoration: underline; }
.browse-crumbs span { color: var(--muted); }
.browse-items { display: flex; flex-direction: column; gap: 0.35rem; margin-top: 0.8rem; max-height: 340px; overflow-y: auto; }
.browse-item {
  display: flex; gap: 0.7rem; align-items: baseline; width: 100%;
  text-align: left; padding: 0.5rem 0.75rem; cursor: pointer; font: inherit; font-size: var(--fs-md);
  border: 1px solid var(--line); border-radius: var(--radius-ctl); background: var(--card); color: var(--ink);
  transition: border-color 0.12s, background 0.12s;
}
.browse-item:hover { border-color: var(--input-hover); background: var(--hover-bg); }
.browse-item.selected { border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 0 0 1px var(--accent); }
.browse-item strong { font-variant-numeric: tabular-nums; font-weight: 600; }
.browse-item span { flex: 1; }
.browse-item em { font-style: normal; color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; }
.browse-item-row { display: flex; gap: 0.4rem; align-items: stretch; }
.browse-item-row .browse-item { flex: 1; }
.browse-item-edit {
  flex-shrink: 0; display: flex; align-items: center; padding: 0 0.7rem;
  border: 1px solid var(--line); border-radius: var(--radius-ctl); background: var(--card);
  color: var(--muted); font-size: var(--fs-sm); text-decoration: none;
  transition: border-color 0.12s, color 0.12s;
}
.browse-item-edit:hover { border-color: var(--input-hover); color: var(--ink); }
.bm-selected { flex: 1; text-align: left; font-size: var(--fs-md); color: var(--accent); }
.browse-item-row .bm-row-price { flex: none; width: 110px; align-self: center; margin: 0; }
.le-head { display: flex; justify-content: space-between; align-items: center; gap: 0.6rem; margin: 0.2rem 0 0.6rem; }
.le-head #le-current { font-weight: 600; }

/* ---- Small-screen polish ---- */
@media (max-width: 700px) {
  /* Touch targets grow as a set: bumping the three tokens moves buttons,
     fields and comboboxes together, so a row of controls stays flush. */
  :root { --control-h-sm: 32px; --control-h: 40px; --control-h-lg: 46px; }
  input, select, textarea { font-size: 16px; }
  .btn { padding: 0.45rem 1rem; }
  .btn.small { padding: 0.25rem 0.6rem; }
  /* A stretched flex line must not stretch the buttons on it. */
  .searchbar .btn { align-self: flex-end; }

  /* Container + card + fieldset each took a full desktop inset, so a field on a
     390px screen had ~290px to live in. Tighten the nested gutters. */
  .card { padding: 1rem 0.95rem; }
  .form-grid fieldset { padding: 0.85rem 0.8rem; }

  /* Dashboard mini-tables: a document number or a dollar figure must not break
     mid-token ("INV-" on one line, the digits on the next). Let the client and
     vehicle columns absorb the wrapping instead. */
  .dash-table td:first-child, .dash-table td:last-child,
  .ar-table td:first-child, .ar-table td.num { white-space: nowrap; }
  /* Recover the width that buys: 0.8rem of gutter on both sides of every cell
     is desktop spacing, and the outer edges already have the card's padding. */
  .dash-table th, .dash-table td, .ar-table th, .ar-table td { padding-left: 0.4rem; padding-right: 0.4rem; }
  .dash-table th:first-child, .dash-table td:first-child,
  .ar-table th:first-child, .ar-table td:first-child { padding-left: 0; }
  .dash-table th:last-child, .dash-table td:last-child,
  .ar-table th:last-child, .ar-table td:last-child { padding-right: 0; }

  table.card, .card > table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* ...but never inside a .table-scroll. Two nested scrollers meant the right-hand
     columns (total, status, actions) could only be reached by scrolling the inner
     table, which had no visible affordance — they read as simply missing. */
  .table-scroll > table.card { display: table; overflow: visible; }
  th { white-space: nowrap; }

  .searchbar { align-items: stretch; }
  .searchbar input[type="search"] { flex: 1 1 100%; max-width: none; }
  .search-field { flex: 1 1 100%; max-width: none; }
  .searchbar select { flex: 1; max-width: none; }

  .big-search { flex-direction: column; }
  .home-search-hero { margin-top: 3vh; }
  img.home-logo { max-width: 210px; }
  .auth-card { width: min(400px, calc(100vw - 2rem)); margin: 6vh auto; padding: 1.6rem 1.4rem; }
  .stat { flex: 1 1 40%; min-width: 0; }

  .builder2-preview { position: static; }
  .bpreview-frame { height: 60vh; }

  .sp-modal { width: calc(100vw - 1.6rem); }
  .sp-modal-body { padding: 1.1rem 1.2rem; }
}

/* ---- Mobile: stack document line items as cards ---- */
@media (max-width: 700px) {
  .card > table.lines { display: block; overflow: visible; }
  table.lines thead { display: none; }
  table.lines tbody { display: block; }
  table.lines tr {
    display: block; padding: 0.75rem 0.85rem; margin: 0 0 0.7rem;
    border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
  }
  table.lines tr:last-child { margin-bottom: 0; }
  table.lines td { display: block; width: auto; border: none; padding: 0.15rem 0; text-align: left; white-space: normal; }
  table.lines td:first-child:empty { display: none; }
  table.lines td.line-desc-cell { padding: 0 0 0.55rem; margin-bottom: 0.5rem; border-bottom: 1px solid var(--line-soft); }
  table.lines td[data-label] { display: flex; align-items: center; gap: 0.5rem; padding: 0.2rem 0; }
  table.lines td[data-label]::before {
    content: attr(data-label); flex: 0 0 4.5rem;
    color: var(--muted); font-weight: 600; font-size: var(--fs-xs);
    text-transform: uppercase; letter-spacing: 0.05em;
  }
  table.lines td[data-label] .ro-cell { margin: 0; }
  table.lines td.row-actions {
    display: flex; flex-wrap: wrap; gap: 0.45rem; white-space: normal;
    margin-top: 0.6rem; padding-top: 0.55rem; border-top: 1px solid var(--line-soft);
  }
  table.lines td.row-actions .btn { flex: 0 1 auto; }
  table.lines td[colspan] { display: block; text-align: center; }

  .pack-choices, .sp-modal-actions, .form-actions,
  .danger-zone, .page-head .actions { flex-wrap: wrap; }
  .btn { white-space: normal; }

  .card > table.stack-mobile { display: block; overflow: visible; }
  table.stack-mobile thead { display: none; }
  table.stack-mobile tbody { display: block; }
  table.stack-mobile tr {
    display: block; position: relative;
    padding: 0.6rem 2.4rem 0.6rem 0.85rem; margin: 0 0 0.55rem;
    border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
  }
  table.stack-mobile tr:last-child { margin-bottom: 0; }
  table.stack-mobile td { display: flex; align-items: baseline; gap: 0.5rem; border: none; padding: 0.14rem 0; text-align: left; white-space: normal; }
  table.stack-mobile td[data-label]::before {
    content: attr(data-label); flex: 0 0 5.5rem;
    color: var(--muted); font-weight: 600; font-size: var(--fs-xs);
    text-transform: uppercase; letter-spacing: 0.05em;
  }
  table.stack-mobile td.row-x { position: absolute; top: 0.45rem; right: 0.5rem; padding: 0; display: block; }
  table.stack-mobile td.row-x .inline { margin: 0; }
  table.stack-mobile td[colspan] { display: block; text-align: center; padding: 0.3rem 0; }
}

/* ---- Mobile: the big list tables (quotes, invoices, clients, items, POs)
   become one card per record. Eight-to-ten columns cannot be squeezed into a
   phone; stacking them keeps every field — total, status, actions — on screen
   instead of parked off the right edge behind a hidden scroller. ---- */
@media (max-width: 700px) {
  .table-scroll:has(> table.list-stack) { overflow-x: visible; margin-bottom: 1.1rem; }
  .table-scroll > table.list-stack,
  table.list-stack {
    display: block; overflow: visible; min-width: 0;
    border: 0; background: none; box-shadow: none; border-radius: 0;
  }
  table.list-stack thead { display: none; }
  table.list-stack tbody { display: block; }
  table.list-stack tr {
    display: block; position: relative;
    padding: 0.75rem 0.9rem; margin: 0 0 0.6rem;
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--card); box-shadow: var(--shadow);
  }
  table.list-stack tr:hover,
  table.list-stack tbody tr:hover td { background: var(--card); }
  table.list-stack tr:last-child { margin-bottom: 0; }
  table.list-stack td {
    display: flex; align-items: baseline; gap: 0.6rem;
    border: none; padding: 0.13rem 0; text-align: left; white-space: normal;
    /* Unbreakable values — IPv6 addresses, VINs, long emails — have to wrap
       here or a single cell widens the card past the viewport. */
    min-width: 0; overflow-wrap: anywhere;
  }
  table.list-stack td.nowrap { white-space: normal; }
  table.list-stack td[data-label]::before {
    content: attr(data-label); flex: 0 0 5.5rem;
    color: var(--muted); font-weight: 600; font-size: var(--fs-2xs);
    text-transform: uppercase; letter-spacing: 0.05em; line-height: 1.9;
  }
  /* Numbers are right-aligned in a grid because they stack into a column;
     in a card they are single values, so they read left like everything else. */
  table.list-stack td.num { text-align: left; justify-content: flex-start; }

  /* The record's own number is the card's headline, not a labelled field. */
  table.list-stack td.stack-title {
    display: block; padding: 0 2.2rem 0.5rem 0; margin-bottom: 0.45rem;
    border-bottom: 1px solid var(--line-soft);
    font-size: var(--fs-lg); font-weight: 600; letter-spacing: -0.012em;
  }
  table.list-stack td.stack-title a { text-decoration: none; }
  table.list-stack td.stack-title .tag { margin-left: 0.4rem; vertical-align: 0.14em; }

  /* Select-all / row checkbox pins to the card's top-right corner. */
  table.list-stack td.checkcol { position: absolute; top: 0.7rem; right: 0.8rem; width: auto; padding: 0; display: block; }

  /* Row buttons sit on their own footer rule. */
  table.list-stack td.stack-actions {
    margin-top: 0.55rem; padding-top: 0.55rem; border-top: 1px solid var(--line-soft);
    gap: 0.45rem; flex-wrap: wrap;
  }
  table.list-stack td[colspan] { display: block; text-align: center; padding: 0.4rem 0; }
}

/* ---- Legal pages ---- */
.legal-doc { max-width: 780px; margin: 0 auto; line-height: 1.65; }
.legal-doc h1 { margin-bottom: 0.3rem; }
.legal-doc h2 { margin: 1.7rem 0 0.4rem; font-size: var(--fs-lg); }
.legal-doc ul { padding-left: 1.2rem; }
.legal-doc li { margin-bottom: 0.35rem; }
.legal-doc code { font-family: var(--font-mono); background: var(--bg-soft); padding: 0.05rem 0.3rem; border-radius: var(--radius-sm); font-size: 0.9em; }
.legal-draft-banner {
  border: 1px solid transparent; background: var(--warn-bg); color: var(--warn-ink);
  border-radius: var(--radius); padding: 0.9rem 1rem; margin-bottom: 1.4rem; line-height: 1.5;
}
.legal-foot { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line-soft); color: var(--muted); }

.accept-gate { max-width: 560px; margin: 3vh auto; }
.accept-card { padding: 1.6rem 1.75rem; }
.accept-card h1 { margin-bottom: 0.4rem; }
.accept-docs { margin: 1rem 0; padding-left: 1.1rem; }
.accept-docs li { margin-bottom: 0.3rem; }
.accept-check { align-items: flex-start; gap: 0.6rem; margin: 1.2rem 0; line-height: 1.5; }
.accept-check input { margin-top: 0.2rem; }
.accept-actions { display: flex; align-items: center; gap: 1rem; margin-top: 0.5rem; }

/* ---- Purchase orders ---- */
.po-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-bottom: var(--sp-4); }
/* .po-tabs a shares the one tab rule above. */
.po-count { font-size: var(--fs-xs); opacity: 0.8; font-variant-numeric: tabular-nums; }
/* Orders had their own badge component, byte-identical in geometry to .tag but
   declared separately — so the two drifted. .po-status is now .tag; only the
   five order-specific colors live here. */
.po-status {
  display: inline-block; font-family: var(--font-body); font-weight: 600;
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em;
  padding: 0.1rem 0.4rem; border-radius: var(--radius-sm); line-height: 1.5;
  border: 1px solid transparent; vertical-align: 0.08em; white-space: nowrap;
  background: var(--line-soft); color: var(--muted);
}
.po-draft { background: var(--line-soft); color: var(--muted); }
.po-issued { background: var(--info-soft); color: var(--info-ink); }
.po-partial { background: var(--warn-soft); color: var(--warn); }
.po-received { background: var(--ok-soft); color: var(--ok-ink); }
.po-cancelled { background: var(--danger-soft); color: var(--danger); }
.po-lines-edit input, .po-receive input, .po-receive textarea { margin: 0; }
.po-receive textarea { min-height: 2.4rem; }

.po-workspace { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: 1.25rem; align-items: start; }
@media (max-width: 900px) { .po-workspace { grid-template-columns: 1fr; } }

.po-list { display: flex; flex-direction: column; gap: 0.5rem; max-height: calc(100vh - 190px); overflow-y: auto; padding-right: 0.15rem; }
.po-list-row {
  display: block; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); padding: 0.7rem 0.85rem; text-decoration: none;
  color: var(--ink); transition: border-color 0.12s, box-shadow 0.12s, background 0.12s;
}
.po-list-row:hover { border-color: var(--input-hover); background: var(--hover-bg); }
.po-list-row.active { border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 0 0 1px var(--accent); }
.po-list-top { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.po-list-top .po-num { font-weight: 600; font-variant-numeric: tabular-nums; }
.po-list-sub { font-size: var(--fs-md); color: var(--ink); margin-top: 0.15rem; }
/* A reference is metadata, not an action — it was rendering in link blue,
   directly under an actual link. */
.po-ref-line { font-size: var(--fs-sm); font-weight: 400; color: var(--muted); margin-top: 0.15rem; }

.po-lines-stack { display: flex; flex-direction: column; gap: 0.55rem; margin: 0.5rem 0 0.7rem; }
.po-line-card {
  border: 1px solid var(--line); border-radius: var(--radius-ctl); padding: 0.6rem 0.7rem;
  background: var(--card); display: flex; flex-direction: column; gap: 0.55rem;
}
.po-line-top, .po-line-bottom { display: flex; gap: 0.7rem; align-items: flex-end; flex-wrap: wrap; }
.po-line-field { display: flex; flex-direction: column; gap: 0.22rem; flex: 1 1 130px; min-width: 0; }
.po-line-field > label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 600; margin: 0; }
.po-line-field input, .po-line-field textarea { margin: 0; width: 100%; }
.po-line-field .combo, .po-line-field .combo-trigger { width: 100%; }
.po-line-field-item { flex: 2 1 190px; }
.po-line-field-part { flex: 1 1 110px; }
.po-line-field-narrow { flex: 0 1 100px; }
.po-line-field-serials { flex: 3 1 210px; }
.po-line-static, .po-line-part { padding: 0.32rem 0; font-size: var(--fs-md); min-height: 1.25em; overflow-wrap: anywhere; }
.po-line-top > .po-line-del,
.po-line-top > .po-remove-line,
.po-line-top > .po-line-lock { flex: 0 0 auto; align-self: flex-end; margin-bottom: 0.15rem; }
.po-list-meta { display: flex; justify-content: space-between; gap: 0.5rem; font-size: var(--fs-xs); color: var(--muted); margin-top: 0.25rem; }
.po-list-empty { padding: 1rem 0.2rem; color: var(--muted); }
.po-list-back { display: block; padding: 0.4rem 0.2rem 0.5rem; font-size: var(--fs-sm); font-weight: 500; color: var(--accent); text-decoration: none; }
.po-list-back:hover { text-decoration: underline; }

.po-pane { border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); overflow: hidden; min-height: 60vh; box-shadow: var(--shadow); }
.po-pane-empty { padding: 3rem 1.5rem; text-align: center; color: var(--muted); }
.po-toolbar .reader-title { font-size: var(--fs-md); }
.po-toolbar .tb-btn.on { background: var(--accent-soft); color: var(--accent); }
.po-toolbar .tb-btn.on svg { color: var(--accent); }
.po-pane-body { padding: 1.1rem 1.2rem 1.4rem; }
.po-frame { display: block; width: 100%; height: calc(100vh - 240px); min-height: 520px; border: none; background: var(--paper-bg); }
.serial-panel { margin-top: 1rem; }
.serial-row { display: flex; align-items: flex-end; gap: 0.6rem; margin-bottom: 0.5rem; }
.serial-item { flex: 1; margin: 0; }
.serial-row textarea { min-height: 2rem; }
.serial-saved { align-self: center; color: var(--ok-ink); font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; }
.po-link-list { list-style: none; padding: 0; margin: 0.4rem 0 0; display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; }
.po-link-list a { font-weight: 500; }
.po-link-list li { display: flex; align-items: center; gap: 0.4rem; }

/* ---- Live search suggestions ---- */
.suggest-box {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-pop); overflow: hidden auto; max-height: 360px;
}
.suggest-item {
  display: flex; flex-direction: column; gap: 0.05rem;
  padding: 0.5rem 0.8rem; text-decoration: none; color: var(--ink);
  border-bottom: 1px solid var(--line-soft);
}
.suggest-item:last-child { border-bottom: none; }
.suggest-item:hover, .suggest-item.active { background: var(--hover-bg); }
.suggest-label { font-weight: 500; font-size: var(--fs-md); }
.suggest-sub { font-size: var(--fs-sm); color: var(--muted); }
.suggest-msg { display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.8rem; font-size: var(--fs-md); color: var(--muted); }
.suggest-msg.suggest-loading { color: var(--ink); }

.sp-spinner {
  width: 15px; height: 15px; flex: none; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: sp-spin 0.6s linear infinite;
}
.combo-loading { display: flex; align-items: center; gap: 0.5rem; }
@keyframes sp-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .sp-spinner { animation-duration: 1.4s; } }

/* ---- Catalog ---- */
.catalog-picker { margin-bottom: 1.2rem; }
.catalog-ymm { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: flex-end; }
.catalog-ymm label { margin: 0; flex: 1 1 180px; }
.catalog-ymm .btn { flex: 0 0 auto; }
.catalog-status { margin: 0.8rem 0 0; color: var(--muted); }
.catalog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1rem; }
.catalog-card {
  display: flex; flex-direction: column; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow); transition: box-shadow 0.12s, border-color 0.12s;
}
.catalog-card:hover { box-shadow: var(--shadow-md); border-color: var(--input-hover); }
.catalog-thumb { aspect-ratio: 4 / 3; background: #fff; display: flex; align-items: center; justify-content: center; border-bottom: 1px solid var(--line-soft); }
.catalog-thumb img { width: 100%; height: 100%; object-fit: contain; }
.catalog-noimg { width: 100%; height: 100%; background: var(--bg-soft); }
.catalog-body { padding: 0.7rem 0.8rem 0.8rem; display: flex; flex-direction: column; gap: 0.3rem; flex: 1; }
.catalog-brand { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 600; }
.catalog-name { font-size: var(--fs-sm); line-height: 1.4; color: var(--ink); flex: 1; }
.catalog-meta { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; margin-top: 0.15rem; }
.catalog-pn { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.catalog-price { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.catalog-card .btn { margin-top: 0.5rem; text-align: center; }

/* Truck picker: same shell as .combo, but its rows stack two lines. */
.truck-picker { position: relative; }
.truck-picker .combo-panel { max-height: 22rem; }
.combo-row-sub { display: block; font-size: var(--fs-sm); line-height: 1.3; }
.combo-row { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }


/* ── One layout for a document, whether it exists yet or not ───────────────
   The new-document page and the editor render from the same header and foot
   partials; these are the classes both use, so the two pages line up. */
.doc-page { display: flex; flex-direction: column; gap: 1rem; }
.doc-foot {
  display: grid; gap: 1rem; align-items: start;
  grid-template-columns: minmax(0, 1.4fr) minmax(18rem, 1fr);
  margin-top: 1rem;
}
@media (max-width: 56rem) { .doc-foot { grid-template-columns: minmax(0, 1fr); } }
.doc-notes { display: flex; flex-direction: column; gap: 0.75rem; }
.doc-notes .bare-fieldset { display: flex; flex-direction: column; gap: var(--sp-4); }
.doc-totals { display: flex; flex-direction: column; gap: 0.75rem; }

/* ── Document header: customer and truck lead, the rest sits in a grid ─────
   Same on the new-document page and the editor — one partial renders both. */
.doc-head { display: grid; gap: 0.9rem 1.5rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.doc-head-customer, .doc-head-truck { min-width: 0; }
.doc-head-grid {
  grid-column: 1 / -1;
  display: grid; gap: 0.9rem 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
  align-items: start;
}
@media (max-width: 46rem) { .doc-head { grid-template-columns: minmax(0, 1fr); } }
.doc-head .req { color: var(--danger); }
.doc-head-truck small.req { display: block; margin-top: 0.2rem; font-weight: 600; }


/* Each saved truck in the dropdown carries a pencil on its right, revealed on
   hover, for editing that truck. The row itself stays a plain button (a button
   cannot nest inside another), so the pencil is a sibling laid over it. */
.truck-row { position: relative; }
.truck-row .combo-row { padding-right: 2.4rem; }
.truck-row-edit {
  position: absolute; top: 0; right: 0.25rem; bottom: 0;
  display: flex; align-items: center; justify-content: center;
  width: 2rem; padding: 0; border: none; background: none;
  color: var(--muted); cursor: pointer; opacity: 0;
  transition: opacity 0.12s, color 0.12s;
}
.truck-row-edit svg { width: 0.95rem; height: 0.95rem; }
.truck-row-edit:hover { color: var(--accent); }
.truck-row:hover .truck-row-edit,
.truck-row-edit:focus-visible { opacity: 1; }
.truck-row-edit:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-sm);
}
/* No hover to reveal it on a tablet. */
@media (hover: none) { .truck-row-edit { opacity: 1; } }

/* Pencil on a customer row in the picker — same treatment as the truck rows. */
.client-row { position: relative; }
.client-row .combo-row { padding-right: 2.4rem; }
.client-row-edit {
  position: absolute; top: 0; right: 0.25rem; bottom: 0;
  display: flex; align-items: center; justify-content: center;
  width: 2rem; padding: 0; border: none; background: none;
  color: var(--muted); cursor: pointer; opacity: 0;
  transition: opacity 0.12s, color 0.12s;
}
.client-row-edit svg { width: 0.95rem; height: 0.95rem; }
.client-row-edit:hover { color: var(--accent); }
.client-row:hover .client-row-edit,
.client-row-edit:focus-visible { opacity: 1; }
.client-row-edit:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-sm);
}
@media (hover: none) { .client-row-edit { opacity: 1; } }

/* Quiet link beside a header field's label — "Open profile" on the customer. */
.head-link {
  float: right; font-weight: 500; font-size: var(--fs-sm); text-decoration: none;
  color: var(--accent);
}
.head-link:hover { text-decoration: underline; }

/* Optional date + its Clear. The button only appears when there is a date to
   clear, so the row stays quiet when the field is already blank. */
.date-field { display: flex; align-items: center; gap: 0.4rem; }
.date-field input[type=date] { flex: 1 1 auto; min-width: 0; }
.date-clear {
  flex: 0 0 auto; padding: 0.15rem 0.4rem; border: none; background: none;
  color: var(--accent); cursor: pointer; font: inherit; font-size: var(--fs-sm);
  font-weight: 500;
}
.date-clear:hover { text-decoration: underline; }
.date-clear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* ---- Discount, in the totals card --------------------------------------
   A figure and the unit it was typed in, joined so the pair reads as one
   control: a number, and the two answers to "of what?". Nothing here submits —
   the discount is a value on the document like the three boxes above it, and
   the totals follow it as it is typed. */
.discount-field { display: flex; align-items: center; justify-content: flex-end; gap: 0.35rem; }
.discount-field input { flex: 1 1 auto; min-width: 0; text-align: right; }
.discount-units { display: flex; flex: none; }
.du-opt { display: flex; margin: 0; cursor: pointer; }
.du-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.du-opt > span {
  display: flex; align-items: center; justify-content: center;
  min-width: 2.1rem; min-height: var(--control-h-sm);
  border: 1px solid var(--input-border); background: var(--card); color: var(--ink-soft);
  font-weight: 600; font-size: var(--fs-sm); line-height: 1;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.du-opt:hover > span { border-color: var(--input-hover); background: var(--hover-bg); }
/* Joined into one pair: square off the shared edge, and overlap the borders so
   the seam is one line rather than two. */
.du-opt:first-child > span { border-radius: var(--radius-ctl) 0 0 var(--radius-ctl); }
.du-opt:last-child > span { margin-left: -1px; border-radius: 0 var(--radius-ctl) var(--radius-ctl) 0; }
.du-opt input:checked + span {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink); position: relative;
}
.du-opt input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

/* A discount line's label, typed straight into the items table: it is the only
   place the promotion can be named now, and it saves like any other inline
   edit. Plain until touched, so the row still reads as text. */
.li-discount-desc {
  font-weight: 500; font-size: var(--fs-md); width: 100%; max-width: 22rem;
  padding: 0.1rem 0.3rem; border-color: transparent; background: transparent;
}
.li-discount-desc:hover { border-color: var(--input-border); background: var(--card); }
.li-discount-desc:focus { border-color: var(--input-border); background: var(--card); }

/* A header value that is shown rather than edited — the document's tax. */
.readonly-field {
  display: flex; align-items: center; gap: 0.4rem; min-height: var(--control-h);
  margin-top: 0.18rem; padding: 0.4rem 0; font-size: var(--fs-md); font-weight: 500;
}
.doc-head-tax small { display: block; margin-top: -0.2rem; }

/* ── Items table: get the figures under their headings ────────────────────
   A numeric column's heading is right-aligned, and so is the cell — but the
   text typed INTO an input is left-aligned by default, so "QTY" sat hard right
   while the digit under it sat hard left, and Price read the same way. Aligning
   the input's own text puts every figure under its heading. */
table.lines td.num input { text-align: right; }
/* Tax is a tick rather than a figure: centre the box under the word. */
table.lines th.tax-col, table.lines td.tax-col { text-align: center; }
/* Category picker on a line: wide enough to read a category name, no wider. */
table.lines td.cat-col select { min-width: 8.5rem; max-width: 12rem; }
table.lines td.tax-col input { margin: 0 auto; }
/* Keep the money columns from being squeezed by a long description. */
table.lines th.num, table.lines td.num { white-space: nowrap; }

/* The text inside an input sits inset from the cell edge by the input's own
   padding and border (9.6px + 1px). A heading, and a plain-text figure like the
   line total, sit flush — which is why "QTY" and "PRICE" hung ~10px right of the
   values beneath them. Giving those the same inset puts every figure in the
   column on one vertical line. Only on the editable table: the read-only one has
   no inputs, so everything there is already flush with everything else. */
.lines-editable { --input-text-inset: 10.6px; }
.lines-editable thead th.num { padding-right: calc(0.8rem + var(--input-text-inset)); }
.lines-editable tbody td.num .line-total { margin-right: var(--input-text-inset); }
