/* The look (desk v2 spec › 3, Decision 118: dense-desk's frame and page shapes, portal-panel's bottom bar; DESIGN 1
   to 18): the site's own stylesheet, the only one the base loads. Every rule lives here: the Content-Security-Policy
   allows only this file, no inline style, and only the site's own script files (js/desk.js and its modules, which
   set no style). A dark frame (the header across the top, the navigation down
   the left) round a pale workspace of white panels, one accent, the system font. Computer first (Decision 102): the
   desk layout from 960 CSS pixels; below 960 the navigation is a bar at the bottom, below 760 a grid's rows are
   cards, the phone the floor at 375. Rests on WCAG 2.2 (SC 1.4.3 and 1.4.11 contrast, 1.4.10 reflow, 2.4.1 bypass
   blocks, 2.4.7 and 2.4.11 focus, 2.5.5 and 2.5.8 target size), the Web Interface Guidelines (tabular figures,
   reduced motion, a skip link) and MDN on `position: sticky`, `:has()` and `<details>`. */

/* --- The tokens. Every colour a six-digit hex so tests/test_look.py measures each pair with the WCAG relative-luminance
   formula: text, muted text and the accent at least 4.5:1 on the page, the canvas, the surface, the zebra stripe and
   the accent's soft tint; white on the accent fill 6.75:1; the control border and the focus ring at least 3:1; the
   frame's words on its two darks at least 4.5:1; each chip's text on its own background at least 4.5:1; text and
   muted text 4.5:1 on an input's field, its border and the mark at least 3:1 on it. In dark mode the surfaces step up
   in tone with elevation (Material 3's surface tones; Apple's base and elevated backgrounds): the frame darkest, then
   the canvas, the panels, the table's header row and the inputs' field; the mark (a set filter, the open row, a
   button's edge) is the accent at reduced strength, so nothing set reads as a stuck white focus ring. */
:root {
  color-scheme: light dark;
  --bg: #ffffff;
  --canvas: #f3f5f8;
  --surface: #f7f8fa;
  --field: #ffffff;
  --text: #172033;
  --muted: #586274;
  --rule: #e1e5eb;
  --control: #848e9f;
  --zebra: #f8f9fb;
  --hover: #eef3fc;
  --accent: #2453c4;
  --accent-fill: #2453c4;
  --accent-soft: #e7eefb;
  --on-accent: #ffffff;
  --focus: #2453c4;
  --mark: #2453c4;
  --chrome: #0f1b2d;
  --chrome-2: #1a2a43;
  --on-chrome: #eaf0f8;
  --chrome-muted: #a3b2c7;
  --chrome-accent: #8fb4ff;
  --dealer: #0b4f8a;
  --dealer-bg: #e3effa;
  --end-user: #7a3e00;
  --end-user-bg: #fdf0e0;
  --tyre: #24532a;
  --tyre-bg: #e4f3e6;
  --recon: #5b2a86;
  --recon-bg: #f0e7f8;
  --check: #6b4f00;
  --check-bg: #fff4cc;
  --wrong: #9b1c1c;
  --wrong-bg: #fde8e8;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --radius: 6px;
  --top-h: 3.25rem;
  --nav-w: 12.5rem;
  --bar-h: 3.5rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #202939;
    --canvas: #182030;
    --surface: #273143;
    --field: #2c3749;
    --text: #e6eaf0;
    --muted: #aab4c4;
    --rule: #34405a;
    --control: #8391a7;
    --zebra: #242d3e;
    --hover: #2b3a58;
    --accent: #8fb4ff;
    --accent-fill: #2453c4;
    --accent-soft: #283c64;
    --on-accent: #ffffff;
    --focus: #8fb4ff;
    --mark: #6d93e3;
    --chrome: #0a0f17;
    --chrome-2: #1e2b40;
    --on-chrome: #e6eaf0;
    --chrome-muted: #97a6bc;
    --chrome-accent: #8fb4ff;
    --dealer: #9cc8f0;
    --dealer-bg: #1e3f5f;
    --end-user: #f2c48d;
    --end-user-bg: #4a3418;
    --tyre: #9fd6a6;
    --tyre-bg: #1f4528;
    --recon: #cfb0ef;
    --recon-bg: #3e2b5c;
    --check: #f0d36b;
    --check-bg: #463c0e;
    --wrong: #f4a3a3;
    --wrong-bg: #552424;
  }
}

/* --- The page and the type scale (DESIGN 15 and 16): the system font stack, nothing loaded; body 0.9375rem, tables
   0.875rem, small 0.8125rem, h1 1.375rem, weights 400, 500 and 600. */
*,
*::before,
*::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--canvas);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 0.9375rem;
  line-height: 1.5;
}
h1, h2, h3 { line-height: 1.25; margin: var(--space-4) 0 var(--space-2); text-wrap: balance; overflow-wrap: anywhere; font-weight: 600; }
h1 { font-size: 1.375rem; }
h2 { font-size: 1.0625rem; }
h3 { font-size: 0.9375rem; }
small { font-size: 0.8125rem; }
p, ul, ol, dl, form, fieldset, details, article { margin: 0 0 var(--space-3); }
ul, ol { padding-left: 1.25rem; }
main > p { max-width: 75ch; }
main > :first-child { margin-top: 0; }
.muted { color: var(--muted); }
a { color: var(--accent); text-underline-offset: 0.15em; }
a:hover { text-decoration-thickness: 2px; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.875em; }
/* a long refusal message wraps at 375 rather than pushing the page sideways (DESIGN 1) */
mark { color: var(--check); background: var(--check-bg); padding: 0 0.2em; border-radius: 2px; overflow-wrap: anywhere; }
del { color: var(--muted); }
del a { text-decoration-line: line-through; color: var(--muted); } /* a decoration never reaches an inline-flex box
   (CSS Text Decoration 3, 2.1), so a struck linked description is struck here */
hr { border: 0; border-top: 1px solid var(--rule); }
.sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* An icon from the sprite (screens/static/screens/icons.svg), drawn in its text's colour beside the words it stands
   for, so it is hidden from assistive technology. */
.icon { flex: none; width: 1.125rem; height: 1.125rem; vertical-align: -0.2em; }

/* The skip link (SC 2.4.1): the keyboard's first stop, out of sight until focused, to `<main id="main">`. */
.skip { position: absolute; left: var(--space-2); top: -4rem; z-index: 30; padding: var(--space-2) var(--space-3); color: var(--accent); background: var(--bg); border-radius: var(--radius); }
.skip:focus { top: var(--space-2); }
main:focus:not(:focus-visible) { outline: none; } /* the skip link moves focus here; a mouse never rings the page */

/* --- The frame (DESIGN 10 and 17; spec › 3): the dark header across the top holds the name, the search, the record's
   as-of time and Sign out. */
body > header {
  display: flex;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--chrome);
  color: var(--on-chrome);
}
body > header form { margin: 0; }
body > header .name { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 600; font-size: 1.0625rem; color: var(--on-chrome); text-decoration: none; }
/* The mark: a letter in the accent's fill, its alternative text empty, so a screen reader reads the name once. */
body > header .name::before { content: "A" / ""; display: inline-grid; place-items: center; width: 1.625rem; height: 1.625rem; border-radius: var(--radius); background: var(--accent-fill); color: var(--on-accent); font-size: 0.875rem; font-weight: 600; }
body > header [role=search] { flex: 1 1 auto; flex-wrap: nowrap; align-items: center; min-width: 0; max-width: 30rem; }
body > header .field input { color: var(--on-chrome); background: var(--chrome-2); border-color: var(--chrome-muted); } /* its edge
   at 3:1 on the header (SC 1.4.11; the look panel's M-9) */
body > header .field input::placeholder { color: var(--chrome-muted); opacity: 1; }
body > header .field .icon { color: var(--chrome-muted); }
body > header button.secondary,
body > header .signout button { color: var(--on-chrome); background: transparent; border-color: var(--chrome-muted); }
body > header .signout { margin-left: auto; }
body > header button { white-space: nowrap; }
.asof { display: none; align-items: center; gap: 0.375rem; margin: 0 0 0 auto; font-size: 0.8125rem; color: var(--chrome-muted); white-space: nowrap; }
.asof time { color: var(--on-chrome); }
body > header :focus-visible,
.side :focus-visible,
.bottom-bar :focus-visible { outline-color: var(--chrome-accent); }

/* A search field with its icon inside (the header's): the field takes the room, the button keeps its width. */
.field { position: relative; display: flex; flex: 1 1 auto; min-width: 0; align-items: center; }
.field .icon { position: absolute; left: 0.5rem; pointer-events: none; }
.field input[type=search] { width: 100%; min-width: 0; padding-left: 1.875rem; }

/* The left navigation from 960 pixels: dark, grouped Record, Work and Admin (a disclosure open on every page), the
   page shown marked by an accent bar and a lighter fill (aria-current), the role and address at its foot. */
.side {
  display: none;
  flex-direction: column;
  padding: var(--space-3) var(--space-2);
  border-right: 1px solid var(--chrome-2); /* its edge in dark mode, where the frame and the canvas are near */
  background: var(--chrome);
  color: var(--on-chrome);
}
.side ul { list-style: none; margin: 0 0 var(--space-2); padding: 0; display: flex; flex-direction: column; gap: 2px; }
.nav-h { margin: var(--space-2) 0.625rem var(--space-1); font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--chrome-muted); }
.side a,
.side summary {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.375rem 0.625rem;
  border-radius: var(--radius);
  color: var(--on-chrome);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
}
.side .icon { color: var(--chrome-muted); }
.side a:hover,
.side summary:hover { background: var(--chrome-2); }
.side a[aria-current=page] { background: var(--chrome-2); box-shadow: inset 3px 0 0 var(--chrome-accent); font-weight: 600; }
.side a[aria-current=page] .icon { color: var(--chrome-accent); }
.side details { margin: var(--space-2) 0 0; padding: 0; border: 0; background: none; }
.side summary { list-style: none; cursor: pointer; }
.side summary::-webkit-details-marker { display: none; }
.side summary::after { content: "▾" / ""; margin-left: auto; font-size: 0.75rem; color: var(--chrome-muted); }
.side details:not([open]) > summary::after { content: "▸" / ""; }
.side details ul { margin: 2px 0 0; padding-left: 1.75rem; }
.side details a { padding-block: 0.25rem; font-weight: 400; color: var(--chrome-muted); }
.side details a:hover,
.side details a[aria-current=page] { color: var(--on-chrome); }
.side .who { margin: auto 0 0; padding: 0.75rem 0.625rem 0; border-top: 1px solid var(--chrome-2); font-size: 0.75rem; color: var(--chrome-muted); overflow-wrap: anywhere; }
.side .who span { color: var(--on-chrome); }
main { flex: 1 0 auto; min-width: 0; padding: var(--space-3); }

/* From 960 pixels the desk: the header sticky across the top, the navigation sticky at the left at the viewport's
   height, the content using the remaining width; the record's as-of time shows in the header. */
.bottom-bar { display: none; }
@media (min-width: 960px) {
  body.desk {
    display: grid;
    grid-template-columns: var(--nav-w) minmax(0, 1fr);
    grid-template-rows: var(--top-h) 1fr;
  }
  /* A focused control scrolled into view clears the sticky header (SC 2.4.11; WCAG Technique C43). */
  html { scroll-padding-top: var(--top-h); }
  body.desk > header { grid-column: 1 / -1; position: sticky; top: 0; z-index: 10; height: var(--top-h); padding-block: 0; }
  body.desk > header .name { min-width: calc(var(--nav-w) - var(--space-3)); }
  body.desk > header .signout { margin-left: 0; }
  body.desk > .side { display: flex; position: sticky; top: var(--top-h); align-self: start; height: calc(100vh - var(--top-h)); overflow-y: auto; }
  body.desk > main { padding: 1.25rem 1.25rem 2.5rem; }
}

/* The record's as-of time shows in the header from 760 pixels (DESIGN 8), where the slim header has the room; on the
   phone it shows on home. */
@media (min-width: 760px) {
  .asof { display: flex; }
  body.desk > header .asof + .signout { margin-left: 0; } /* the as-of time takes the free room */
}

/* Below 960 pixels the navigation is a bar at the bottom of the screen (from portal-panel), in thumb reach (DESIGN 1):
   an icon over a word per screen, the page shown in the frame's accent; Admin a menu whose list opens above the bar.
   The page keeps the bar's height free at its foot, so its last line is never under the bar. */
@media (max-width: 959.98px) {
  body.desk > header .name { font-size: 0; gap: 0; } /* the mark alone: the slim header gives the search the room */
  body.desk > header .name::before { font-size: 0.875rem; }
  body.desk > header { gap: var(--space-2); }
  body.desk > main { padding-bottom: calc(var(--bar-h) + var(--space-4) + env(safe-area-inset-bottom)); }
  /* A focused control scrolled into view clears the bottom bar (SC 2.4.11; WCAG Technique C43). */
  html { scroll-padding-bottom: calc(var(--bar-h) + env(safe-area-inset-bottom)); }
  .desk > .bottom-bar {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 20;
    padding: 0 0 env(safe-area-inset-bottom);
    background: var(--chrome);
    border-top: 1px solid var(--chrome-2);
  }
  .bottom-bar > ul { list-style: none; margin: 0; padding: 0; display: flex; }
  .bottom-bar > ul > li { flex: 1 1 0; min-width: 0; }
  .bottom-bar > ul > li > a,
  .bottom-bar summary {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: var(--bar-h);
    padding: 0.25rem 0.125rem;
    color: var(--chrome-muted);
    font-size: 0.6875rem;
    font-weight: 500;
    text-decoration: none;
    list-style: none;
    cursor: pointer;
  }
  .bottom-bar summary::-webkit-details-marker { display: none; }
  .bottom-bar .icon { width: 1.375rem; height: 1.375rem; }
  .bottom-bar span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bottom-bar a[aria-current=page],
  .bottom-bar details[open] > summary,
  .bottom-bar details:has([aria-current=page]) > summary { color: var(--on-chrome); box-shadow: inset 0 2px 0 var(--chrome-accent); }
  .bottom-bar a[aria-current=page] .icon,
  .bottom-bar details:has([aria-current=page]) > summary .icon { color: var(--chrome-accent); }
  .bottom-bar details { margin: 0; padding: 0; border: 0; background: none; }
  .bottom-bar details[open] > ul {
    position: fixed;
    right: var(--space-2);
    bottom: calc(var(--bar-h) + var(--space-2) + env(safe-area-inset-bottom));
    width: min(16rem, calc(100vw - 2 * var(--space-2)));
    margin: 0;
    padding: var(--space-1);
    list-style: none;
    background: var(--chrome);
    border: 1px solid var(--chrome-2);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgb(0 0 0 / 0.3);
  }
  .bottom-bar details[open] > ul a { display: flex; align-items: center; min-height: 2.75rem; padding: 0 0.75rem; border-radius: var(--radius); color: var(--on-chrome); text-decoration: none; }
  .bottom-bar details[open] > ul a[aria-current=page] { background: var(--chrome-2); box-shadow: inset 3px 0 0 var(--chrome-accent); }
}

/* --- A page's head (spec › 3): a breadcrumb, the title and a line of facts; the customer's figures as tiles; the
   invoice and the Questions' groups as panels. */
.crumbs ol { list-style: none; margin: 0 0 var(--space-1); padding: 0; display: flex; flex-wrap: wrap; font-size: 0.8125rem; color: var(--muted); }
.crumbs li + li::before { content: "/" / ""; margin: 0 0.375rem; }
.crumbs a { color: var(--muted); }
.page-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-3); margin: 0 0 var(--space-3); }
.page-head h1 { margin: 0; }
.page-head p { margin: 0; }
.page-head .totals { flex: 1 1 100%; font-size: 0.875rem; }
dl.facts { display: flex; flex-wrap: wrap; gap: var(--space-2) 2rem; margin: 0 0 var(--space-3); }
dl.facts dt { font-size: 0.75rem; color: var(--muted); }
dl.facts dd { margin: 0; overflow-wrap: anywhere; }
dl.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--space-2); max-width: 50rem; margin: 0 0 var(--space-3); }
dl.stats > div,
.tiles > li { padding: 0.625rem 0.875rem; background: var(--bg); border: 1px solid var(--rule); border-radius: 8px; }
dl.stats dt { font-size: 0.75rem; color: var(--muted); }
dl.stats dd { margin: 0; font-size: 1.125rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.panel,
.hero { padding: var(--space-3) 1.25rem; background: var(--bg); border: 1px solid var(--rule); border-radius: 10px; margin: 0 0 var(--space-3); }
.panel > :last-child,
.hero > :last-child { margin-bottom: 0; }
.panel > h2:first-child { margin-top: 0; }
.panel .page-head .btn .icon { width: 1rem; height: 1rem; }
ul.flags { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.q-actions { align-items: center; margin: 0 0 var(--space-3); }
.q-actions p { margin: 0; }

/* Home: the greeting and the search in a panel, the record's state as tiles, the questions open as links. */
.hero h1 { margin: 0 0 var(--space-3); }
.hero [role=search] input,
.hero [role=search] button { min-height: 2.5rem; }
.tiles { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: var(--space-2); margin: 0 0 var(--space-3); }
.tiles > li { display: flex; flex-direction: column; gap: 2px; }
.tiles strong { font-size: 1.125rem; white-space: nowrap; font-weight: 600; font-variant-numeric: tabular-nums; order: 1; }
.tiles span { font-size: 0.8125rem; color: var(--muted); order: 0; }
.tiles strong + span { order: 2; }

/* --- Tables (DESIGN 5 and 16): each in its own scroll box, so the page never scrolls sideways (SC 1.4.10); from 960
   pixels the box is bounded by the viewport and scrolls both ways, so its header rows stick inside it; dense rows,
   zebra stripes, numbers right-aligned in tabular figures, long text cut to one line (the full text in its title). */
.table-box {
  overflow-x: auto;
  width: max-content;
  max-width: 100%;
  margin: 0 0 var(--space-3);
  border: 1px solid var(--rule);
  border-radius: 8px;
  /* A shadow at the right edge while there is more to the right (DESIGN 1): the cover travels with the content
     (local) and hides the shadow once the end is reached; CSS only, as the CSP allows no script. */
  background-color: var(--bg);
  background-image: linear-gradient(to left, var(--bg) 30%, transparent),
    radial-gradient(farthest-side at 100% 50%, var(--control), transparent);
  background-position: right center, right center;
  background-size: 2.5rem 100%, 0.75rem 100%;
  background-repeat: no-repeat;
  background-attachment: local, scroll;
}
.table-box.wide { width: 100%; } /* a record grid uses the width, not its content's (the look panel, plan C) */
.table-box :focus { scroll-margin-top: 4.5rem; } /* SC 2.4.11: a focused control clears the sticky header rows */
@media (min-width: 960px) {
  .table-box { overflow: auto; max-height: calc(100vh - var(--top-h) - 2rem); }
}
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.875rem; line-height: 1.35; }
th, td { padding: 0.375rem 0.625rem; text-align: left; vertical-align: top; border-bottom: 1px solid var(--rule); }
thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface);
  font-weight: 600;
  white-space: nowrap;
  border-bottom: 2px solid var(--control);
}
thead th a { color: var(--text); text-decoration: none; }
thead th a:hover { color: var(--accent); text-decoration: underline; }
/* The sorted column says so beside its label, in the accent; aria-sort carries it for a screen reader, so the arrow's
   own alternative text is empty. */
thead th[aria-sort] a { color: var(--accent); }
thead th[aria-sort=ascending] a::after { content: " ▲" / ""; font-size: 0.75em; }
thead th[aria-sort=descending] a::after { content: " ▼" / ""; font-size: 0.75em; }
/* The stripes count a table's rows but a lines table's opened rows (`tr.more`, desk v2 spec › 4.4), so they alternate
   line by line whether a row is open or not; every other table keeps its stripes (the `of S` form: Baseline 2023). */
tbody tr:nth-child(even of :not(.more)) { background: var(--zebra); }
tbody tr:last-child > * { border-bottom: 0; }
tfoot th, tfoot td { border-top: 1px solid var(--rule); border-bottom: 0; }
tfoot tr.total > * { font-weight: 600; border-top: 2px solid var(--control); }
td.date { white-space: nowrap; }
th.num,
td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
td.wrap { overflow-wrap: anywhere; min-width: 12rem; }
.cut { max-width: 24rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
:not(td, th).cut { display: inline-block; vertical-align: bottom; } /* beats `td a`, so a cut link shows its "…" */
/* A record grid's columns fit inside the box at 1280 with the navigation open (desk v2 Task 5 measured it with a
   flagged row): each cell on one line, narrower padding, the description, the customer's name and a wrong line's
   reason cut shorter, each full text in its title (a re-saved copy's link to the counted copy stays whole beside its
   cut words); from 1500 the description, the name and the reason take the room there is. */
.wide th, .wide td { padding-inline: 0.35rem; white-space: nowrap; }
.wide .cut { max-width: 11rem; }
.wide .cut.desc { max-width: 11rem; } /* with its "sales ›" beside it */
.wide .cut.name { max-width: 8rem; }
.wide .cut.why { max-width: 4rem; }
/* The Customers and Items grids' five or six columns leave their name or description the room of a plain cut, so two
   labels that share their first 11rem still tell apart; at phone width they are cards. */
@media (min-width: 960px) {
  .wide .cut.label { max-width: 24rem; }
}
@media (min-width: 1500px) {
  .wide .cut,
  .wide .cut.desc { max-width: 20rem; }
  .wide .cut.name { max-width: 12rem; }
  .wide .cut.why { max-width: 20rem; }
}

/* --- A description and the row that opens (desk v2 spec › 4.4, 4.9, 4.10; plan Task 1). A description is text in its
   lines (`.lines`, never on a cut element: a grid cell keeps one line); its item's "sales ›" after it is left out of a
   drag, so a drag inside the text selects the text alone. A lines table's first column holds a chevron, a native
   disclosure (`<details name="row">`, one open at a time; the APG Disclosure pattern), and the row beneath each line
   shows only while the line's disclosure is open. Its cell wraps within the table (`contain: inline-size` gives the
   panel no width of its own), so no column moves when it opens; where the table scrolls in its box, the panel stays
   at the box's left at the box's width (a container query unit), so its text reads without scrolling sideways. */
.lines { white-space: pre-line; }
.sales { font-size: 0.75rem; white-space: nowrap; -webkit-user-select: none; user-select: none; }
.wide .cut.desc,
.wide .sales { vertical-align: top; } /* level with the row's other cells, whatever the link's height */
/* The "sales ›" link is 24 pixels tall on a mouse (DESIGN 12; SC 2.5.8) and 44 on a touch screen (SC 2.5.5) without
   moving its line: a 1rem line and padding above and below, which a matching negative margin takes back out of the
   line box, so a row stays one line tall and a description's last line keeps its height. */
td a.sales { display: inline-block; min-height: 0; line-height: 1rem; padding-block: 4px; margin-block: -4px; }
.grid { container-type: inline-size; }
.grid .c-x { width: 1.625rem; padding-inline: 0.125rem 0; text-align: center; }
.grid td.c-x details,
.grid td.c-x details[open] { margin: 0; padding: 0; border: 0; min-width: 0; background: none; }
.grid td.c-x summary { display: inline-grid; place-items: center; width: 1.5rem; height: 1.5rem; padding: 0; color: var(--muted); border-radius: var(--radius); list-style: none; }
.grid td.c-x summary::-webkit-details-marker { display: none; }
.grid td.c-x summary:hover { color: var(--text); background: var(--rule); }
.grid td.c-x details[open] summary { color: var(--accent); }
.chev { width: 1rem; height: 1rem; }
.grid td.c-x details[open] .chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: no-preference) {
  .grid td.c-x .chev { transition: transform 120ms ease-out; }
}
.grid tbody tr.row:hover { background: var(--hover); }
.grid tbody tr.row:has(details[open]) { background: var(--accent-soft); }
.grid tr.more { display: none; }
.grid tr.row:has(details[open]) + tr.more { display: table-row; }
.grid tr.more > td { white-space: normal; padding: 0; background: var(--surface); box-shadow: inset 3px 0 0 var(--mark); }
.grid .more-panel { contain: inline-size; position: sticky; left: 0; max-width: 100cqi; padding: var(--space-2) var(--space-3); }
.grid tbody tr.row:nth-last-child(2):not(:has(details[open])) > * { border-bottom: 0; } /* the last line, its panel shut */
.more-panel p { max-width: 75ch; margin: 0 0 var(--space-2); }
.more-panel .facts { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin: 0 0 var(--space-2); }
.more-panel .facts div { display: flex; gap: var(--space-1); }
.more-panel dl.facts dt { font-size: inherit; color: var(--muted); }
.more-panel dd { margin: 0; }
.more-panel .actions { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); margin: 0; }

/* --- Chips (DESIGN 13 and 16): a small rounded label per meaning, always holding its words, so colour never carries
   the meaning alone; the Kind chip says "dealer" or "end user" (desk v2 spec › 3). */
.chip {
  display: inline-block;
  padding: 0 0.5em;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
}
.chip:not([class*=" chip-"]) { color: var(--text); background: var(--bg); box-shadow: inset 0 0 0 1px var(--control); }
.chip abbr,
abbr.chip { text-decoration: none; cursor: help; } /* the lab's D and E chips are the abbr itself */
del .chip { text-decoration-line: line-through; } /* a chip is an atomic box: a struck row's decoration skips it */
.chip-dealer { color: var(--dealer); background: var(--dealer-bg); }
.chip-end-user { color: var(--end-user); background: var(--end-user-bg); }
.chip-tyre { color: var(--tyre); background: var(--tyre-bg); }
.chip-recon { color: var(--recon); background: var(--recon-bg); }
.chip-check { color: var(--check); background: var(--check-bg); }
.chip-wrong { color: var(--wrong); background: var(--wrong-bg); }

/* A grid's pager: a row of links with a gap, the page shown marked (aria-current). */
nav.pager { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); margin: 0 0 var(--space-3); font-size: 0.875rem; }
nav.pager [aria-current=page] { font-weight: 600; }

/* The run log's counts: one short line, its parts separated by a dot, the full record one click away. */
.counts > span + span::before { content: " · "; }
.counts { white-space: nowrap; }
td pre { margin: var(--space-1) 0 0; font-size: 0.8rem; white-space: pre-wrap; overflow-wrap: anywhere; max-width: 40rem; }

/* --- The toolbar (desk v2 spec › 3 and 4.1): the grid's one GET form (From and To, Apply, Clear), each control
   keeping its own width, so a date input shows its whole year (#167); on the phone the Filters disclosure. Under it
   the filters in force as chips, each a link that drops its own filter. */
.toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-3); margin: 0 0 var(--space-2); }
.filter-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-3); margin: 0; }
.filter-bar { align-items: center; }
.filter-bar label { flex: 0 0 auto; display: flex; align-items: center; gap: 0.375rem; margin: 0; font-size: 0.8125rem; color: var(--muted); }
.filter-bar label > input { margin: 0; color: var(--text); }
.filter-bar label > input[type=date] { width: auto; }
details.filters { display: none; }
.chips { list-style: none; margin: 0 0 var(--space-2); padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); font-size: 0.8125rem; }
.fchip { display: inline-flex; align-items: center; gap: 0.375rem; min-height: 24px; padding: 0 0.625rem; border: 1px solid var(--mark); border-radius: 999px; background: var(--accent-soft); color: var(--accent); text-decoration: none; }
.fchip:hover { text-decoration: underline; }
.hint { margin: 0 0 var(--space-2); font-size: 0.8125rem; } /* how a number filter reads (the look panel's M-1) */
/* The filter row under a grid's headers (desk v2 spec › 4.1): one small control a column, joined to the toolbar's
   form by `form="grid"`; each takes its column's width, from a floor that still shows a few characters, so the row
   widens no column but the Customers grid's dates, which stack From over To. A filter holding a value carries the
   accent's edge. The Kind select reads its choice whole once the column is as wide as its "end user" chip. */
thead .filters td { background: var(--surface); padding: 0.2rem; border-bottom: 2px solid var(--control); }
thead .filters input,
thead .filters select { display: block; width: 100%; min-width: 2rem; min-height: 24px; padding: 0 0.2rem; font-size: 0.75rem; }
thead .filters select { min-width: 2.25rem; padding-inline: 0; } /* "any" and its arrow */
.filters select[name=kind],
.filters select[name=class] { min-width: 4rem; } /* "end user" and "recon" read whole */
thead tr:has(+ tr.filters) th { border-bottom-width: 1px; } /* one heavy rule, under the filters (review M-3) */
.filters input[type=date] { width: auto; }
.filters input[type=date] + input[type=date] { margin-top: 2px; }
.grid tr.filters td.c-apply { display: none; } /* the phone's Filters panel's own Apply, below 760 */
.filters .set,
.filter-bar .set { border-color: var(--mark); box-shadow: inset 0 0 0 1px var(--mark); }
.totals { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin: 0 0 var(--space-2); font-variant-numeric: tabular-nums; }
.answers { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.answers form { margin: 0; }
/* In a wide table a row's answers wrap inside their cell, the why box on its own line when the row is full, and a
   row's flags wrap too, so the buttons stay in the box at 1280; the cut columns keep their one line (review I-1). */
.wide td .answers { white-space: normal; min-width: 12rem; }
.wide td.flags { white-space: normal; min-width: 8rem; }  /* a row's flag chips wrap, a long one inside itself */
.wide td.flags .chip { white-space: normal; border-radius: var(--radius); }
.answers input[name=why] { width: 12rem; }

/* --- Elements styled one by one, since no framework does it: forms, buttons, disclosures, fieldsets, cards. */
label { display: block; margin: 0 0 0.75rem; }
input,
select,
textarea,
button { font: inherit; }
input:not([type=checkbox], [type=radio], [type=hidden]),
select,
textarea {
  max-width: 100%;
  padding: var(--space-1) var(--space-2);
  color: var(--text);
  background: var(--field);
  border: 1px solid var(--control);
  border-radius: var(--radius);
  min-height: 24px;
}
label > input:not([type=checkbox], [type=radio]),
label > select,
label > textarea { display: block; width: 100%; max-width: 32rem; margin-top: var(--space-1); }
[type=checkbox],
[type=radio] { width: 1rem; height: 1rem; margin: 0; accent-color: var(--accent-fill); }
button,
[role=button],
a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-height: 24px;
  min-width: 24px;
  padding: 0.3125rem 0.875rem;
  color: var(--on-accent);
  background: var(--accent-fill);
  border: 1px solid var(--mark); /* dark mode's edge in #6d93e3, 4.82:1 on a panel, the fill being 2.16:1 */
  border-radius: var(--radius);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.35;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
}
/* A secondary button (Find, Filter, sign-out) is an outline; a destructive one (Remove, Revoke) an outline in the
   wrong pair (6.94:1 light, 8.06:1 dark), so neither reads as the page's main action. */
.signout button,
button.secondary { background: transparent; color: var(--accent); border-color: var(--control); }
button.danger { color: var(--wrong); background: var(--wrong-bg); border-color: var(--wrong); }
button:hover,
a.btn:hover { box-shadow: inset 0 0 0 100vmax rgb(0 0 0 / 0.15); }
form.inline { display: inline; margin: 0; }
form > button + button { margin-left: var(--space-1); }
header a,
nav a,
td a,
.way a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  min-width: 24px;
}
details { border: 1px solid var(--rule); border-radius: var(--radius); padding: 0 0.75rem; background: var(--bg); }
details[open] { padding-bottom: var(--space-2); }
summary { padding-block: var(--space-1); cursor: pointer; }
details.panel > summary { font-weight: 600; }
/* A row's correction behind a disclosure (the invoice's Mark wrong): the row stays one line until it is opened. */
td details { margin: 0; border: 0; padding: 0; white-space: nowrap; background: none; } /* a row stays one line tall */
td details[open] { white-space: normal; min-width: 14rem; padding-bottom: 0; }
td details form { margin: var(--space-1) 0 0; }
td details label { margin-bottom: var(--space-1); }
/* A line's ✓ and ✗ in the table (plan C spec › 3.6) sit after its PDF link, two buttons wide, so the row stays one
   line tall and the box fits at 1280; the Questions cell's 12rem is a div's (review I-1). */
.wide td span.answers { display: inline-flex; vertical-align: middle; min-width: 0; flex-wrap: nowrap; }
.wide td span.answers button { padding: 0 var(--space-1); } /* 24 px wide at least */
fieldset { border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--space-2) 0.75rem; }
legend { padding: 0 var(--space-1); font-weight: 600; }
/* A radio or checkbox and its label are one target; a count after the text (the customer's names) its own line. */
fieldset label {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: var(--space-2);
  min-height: 24px;
  margin: 0;
}
fieldset label > small { grid-column: 2; }
article { padding: 0.75rem var(--space-3); background: var(--bg); border: 1px solid var(--rule); border-radius: 8px; }
article > :last-child { margin-bottom: 0; }

/* A search form (the header's and home's): the field and the button side by side; the button keeps its width, since a
   min-width replaces a flex item's automatic minimum (it read "Sea / rch" at 375). */
[role=search] { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2); }
[role=search] label { flex: 0 1 32rem; margin: 0; }
[role=search] button { flex: none; }

/* --- Focus is always visible (DESIGN 14; SC 2.4.7): a solid ring in the focus token, 6.75:1 light and 8.35:1 dark;
   on the dark frame its light accent (above). */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* --- Below 760 pixels a grid's rows are cards (DESIGN 1 as amended; spec › 3), so nothing scrolls sideways: a lines
   card holds the invoice, the date and the amount on its first line, then the customer, the description over two
   lines at most, the chips, quantity and price, and the chevron at its lower right, its opened row beneath it; the
   Customers' and Items' cards their name first. The roles table, row and cell in the markup keep the table's meaning
   once its display changes [assumed: holds in VoiceOver on iOS; plan Task 15 checks by hand]. The header row becomes
   a strip of sort pills that scrolls in its own box; the filter row shows when the Filters disclosure is open. An
   invoice's lines become a list, so its amount is never off-screen. */
@media (max-width: 759.98px) {
  .grid.table-box { overflow: visible; max-height: none; border: 0; background: none; }
  .wide.grid > table,
  .wide.grid > table > thead,
  .wide.grid > table > tbody { display: block; }
  .wide.grid thead tr:first-child { display: flex; gap: var(--space-1); overflow-x: auto; padding: 0 0 var(--space-2); }
  .wide.grid thead th { position: static; display: block; padding: 0; background: none; border: 0; }
  .wide.grid thead th.c-x { display: none; }
  .wide.grid thead th a { display: inline-flex; align-items: center; min-height: 2rem; padding: 0 0.75rem; border: 1px solid var(--rule); border-radius: 999px; background: var(--bg); font-weight: 500; font-size: 0.8125rem; white-space: nowrap; }
  .wide.grid thead th[aria-sort] a { border-color: var(--mark); background: var(--accent-soft); }
  .wide.grid tr.filters { display: none; }
  main:has(details.filters[open]) .wide.grid tr.filters { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); margin: 0 0 var(--space-2); padding: var(--space-2); background: var(--bg); border: 1px solid var(--rule); border-radius: 8px; }
  .wide.grid tr.filters td { display: block; padding: 0; border: 0; background: none; white-space: normal; } /* a long label wraps */
  .wide.grid tr.filters td:empty { display: none; }
  .wide.grid tr.filters input[type=date] { width: 100%; min-width: 0; } /* a 16 px date box keeps to its column */
  .wide.grid tr.filters td[data-label]::before { content: attr(data-label); display: block; margin-bottom: 2px; font-size: 0.75rem; color: var(--muted); } /* the control's name, seen (its aria-label is heard) */
  .wide.grid tr.filters input,
  .wide.grid tr.filters select { min-height: 2.5rem; font-size: max(1rem, 16px); padding-inline: var(--space-2); }
  details.filters { display: block; margin: 0; padding: 0; border: 0; background: none; }
  details.filters > summary { display: inline-flex; align-items: center; gap: 0.375rem; min-height: 2rem; padding: 0 0.75rem; border: 1px solid var(--control); border-radius: var(--radius); background: var(--bg); font-size: 0.875rem; list-style: none; }
  details.filters > summary::-webkit-details-marker { display: none; }
  details.filters[open] > summary { border-color: var(--mark); background: var(--accent-soft); color: var(--accent); }
  /* From and To and the number filters' hint show while Filters is open, so the first purchase or sale on a customer
     or item page sits higher (a date in force still shows as its chip; the look panel's M-2). Apply shows in the
     toolbar only where its form holds a control of its own (the form, not the toolbar: the Columns menu beside it
     holds a checkbox per column) and while Filters is closed; the panel ends in its own Apply, the one Apply while it
     is open (M-4). */
  main:not(:has(details.filters[open])) .filter-bar label:has(> input[type=date]),
  main:not(:has(details.filters[open])) .hint { display: none; }
  .filter-bar:not(:has(label > input)) > button,
  main:has(details.filters[open]) .filter-bar > button { display: none; }
  .wide.grid tr.filters td.c-apply { grid-column: 1 / -1; }
  .wide.grid tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.625rem;
    margin: 0 0 var(--space-2);
    padding: 0.625rem 0.75rem;
    background: var(--bg);
    border: 1px solid var(--rule);
    border-radius: 8px;
  }
  .wide.grid tbody tr > td { display: block; padding: 0; border: 0; white-space: normal; }
  .wide.grid tbody .cut { max-width: none; white-space: normal; }
  .wide.grid tbody .cut.desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
  .wide.grid tbody tr > .c-invoice,
  .wide.grid tbody tr > .c-name { font-weight: 600; }
  .wide.grid tbody tr > .c-invoice { order: 1; }
  .wide.grid tbody tr > .c-date { order: 2; color: var(--muted); font-size: 0.8125rem; }
  .wide.grid tbody tr > .c-amount { order: 3; margin-left: auto; font-weight: 600; font-size: 0.9375rem; }
  .wide.grid tbody tr > .c-name,
  .wide.grid tbody tr > .c-customer,
  .wide.grid tbody tr > .c-description { order: 4; flex: 1 1 100%; } /* a Customers card's name heads it, alone on its line */
  .wide.grid tbody tr > .c-name { order: 1; }
  .wide.grid tbody td.num { text-align: left; }
  .wide.grid tbody tr > .c-kind,
  .wide.grid tbody tr > .c-class,
  .wide.grid tbody tr > .c-flags { order: 5; }
  .wide.grid tbody tr.row::after { content: ""; order: 6; flex: 1 1 100%; } /* quantity and price start a line */
  .wide.grid tbody tr > .c-qty,
  .wide.grid tbody tr > .c-price,
  .wide.grid tbody tr > .c-invoices,
  .wide.grid tbody tr > .c-first,
  .wide.grid tbody tr > .c-last,
  .wide.grid tbody tr > .c-size,
  .wide.grid tbody tr > .c-brand,
  .wide.grid tbody tr > .c-sold,
  .wide.grid tbody tr > .c-questions { order: 7; color: var(--muted); }
  .wide.grid tbody tr > .c-year { order: 8; color: var(--muted); }
  .wide.grid tbody tr > td.c-x { order: 9; margin-left: auto; width: auto; padding: 0; }
  .wide.grid tbody td.c-qty::before { content: "Qty " / ""; }
  .wide.grid tbody td.c-price::before { content: "at " / ""; }
  .wide.grid tbody td.c-invoices::before { content: "Invoices " / ""; }
  .wide.grid tbody td.c-first::before { content: "First " / ""; }
  .wide.grid tbody td.c-last::before { content: "Last " / ""; }
  .wide.grid[aria-label=Items] tbody td.c-last::before { content: "Last sold " / ""; }
  .wide.grid tbody td.c-sold::before { content: "Sold " / ""; }
  .wide.grid tbody td.c-questions:not(:empty)::before { content: "Questions " / ""; }
  .wide.grid td.c-x summary { width: 2.25rem; height: 2.25rem; border: 1px solid var(--rule); }
  .wide.grid td.c-flags .answers { min-width: 0; }
  .wide.grid tbody tr.row:has(details[open]) { margin-bottom: 0; border-color: var(--mark); border-radius: 8px 8px 0 0; }
  .wide.grid tbody tr.more { display: none; margin: 0; padding: 0; }
  .wide.grid tbody tr.row:has(details[open]) + tr.more { display: block; margin: 0 0 var(--space-2); border: 1px solid var(--mark); border-top: 0; border-radius: 0 0 8px 8px; overflow: hidden; }
  .wide.grid tbody tr.more > td { display: block; }
  .wide.grid tbody tr > td[colspan]:not(.c-x) { flex: 1 1 100%; }
  /* An invoice's lines: the description full width, then quantity, price and amount at the right, each labelled; the
     totals a line each; the header row visually hidden, read as before. */
  .table-box:has(> table.invoice-lines) { overflow: visible; border: 0; background: none; width: 100%; }
  table.invoice-lines,
  table.invoice-lines > tbody,
  table.invoice-lines > tfoot { display: block; }
  table.invoice-lines > thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  table.invoice-lines tr { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: baseline; gap: 0 0.75rem; padding: 0.5rem; border-bottom: 1px solid var(--rule); background: none; }
  table.invoice-lines tr > * { display: block; padding: 0; border: 0; }
  table.invoice-lines td.desc,
  table.invoice-lines td.correct { flex: 1 1 100%; }
  table.invoice-lines td.qty::before { content: "Qty " / ""; color: var(--muted); }
  table.invoice-lines td.price::before { content: "at " / ""; color: var(--muted); }
  table.invoice-lines td.amount { font-weight: 600; }
  table.invoice-lines tfoot tr { justify-content: space-between; }
  table.invoice-lines td:empty { display: none; }
}

/* --- Targets fit the pointer (DESIGN 12, Decision 102): at least 24 by 24 CSS pixels everywhere (SC 2.5.8, AA),
   above; on a coarse pointer (a phone or a touch screen) buttons, inputs and links that stand alone at least 44 by 44
   (SC 2.5.5), the phone's sort pills and the filter chips among them. A link inside a sentence keeps its line, so no
   rule sizes a bare a. */
@media (pointer: coarse) {
  button,
  [role=button],
  a.btn,
  header a,
  nav a,
  td a,
  th a,
  .way a,
  .fchip {
    display: inline-flex;
    min-height: 44px;
    min-width: 44px;
  }
  input:not([type=checkbox], [type=radio], [type=hidden]),
  select,
  summary,
  fieldset label { min-height: 44px; }
  .grid td.c-x summary,
  .wide.grid td.c-x summary { width: 44px; height: 44px; } /* DESIGN 12; the cards' 2.25rem has this weight, so the
     later rule wins (review I-1) */
  .wide.grid thead th a { min-height: 44px; } /* the phone's sort pills, past the cards' 2rem (spec › 3) */
  .wide.grid tr.filters input,
  .wide.grid tr.filters select { min-height: 44px; } /* the Filters panel's controls, past the cards' 2.5rem */
  .toolbar a,
  .chips a { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; } /* Clear, Clear all */
  td a.sales { padding-block: 14px; margin-block: -14px; }
  details.filters > summary { min-height: 44px; }
  th, td { padding-block: 0.5rem; }
  /* A form control is at least 16 pixels on a touch screen, so iOS Safari never zooms the page when one takes focus
     (the look panel's S-1); the viewport never blocks zoom (SC 1.4.4). The rules that size a control smaller are
     named here, after them, so at equal weight this one wins. */
  input:not([type=checkbox], [type=radio], [type=hidden]),
  select,
  textarea,
  .filter-bar label > input,
  thead .filters input,
  thead .filters select,
  .wide.grid tr.filters input,
  .wide.grid tr.filters select { font-size: max(1rem, 16px); }
}

/* --- Forced colours (Windows High Contrast): box-shadow and the author's colours are dropped, so the page shown in
   the navigation and a filter holding a value keep a mark of their own, an outline in the system's Highlight. */
@media (forced-colors: active) {
  .side a[aria-current=page],
  .bottom-bar a[aria-current=page],
  .bottom-bar details:has([aria-current=page]) > summary,
  .filters .set,
  .filter-bar .set { outline: 2px solid Highlight; outline-offset: -2px; }
}

/* --- Task 13, same words (desk v2 spec › 4.3): the badge beside its switch while it is on, in the accent so it reads
   apart from the line chips; its words say it, so colour never carries it alone (DESIGN 13). */
.filter-bar .badge { color: var(--accent); box-shadow: inset 0 0 0 1px var(--mark); }

/* --- Desk v2 Task 8 (spec › 4.7, 4.8): the Columns menu, a disclosure under the toolbar holding one POST form, and
   the header's switch that narrows the navigation to an icon rail from 960 pixels: each word kept as the link's
   accessible name and tooltip (the template marks it `sr`), Admin a popover menu beside the rail (the Popover API,
   no script). Below 960 the navigation is the bottom bar, so the switch is not offered. */
details.columns, details.columns[open] { margin: 0; padding: 0; border: 0; background: none; } /* a button that opens a panel, not the bordered disclosure */
details.columns > summary { display: inline-flex; align-items: center; min-height: 24px; padding: 0 var(--space-2); border: 1px solid var(--control); border-radius: var(--radius); color: var(--accent); cursor: pointer; }
@media (pointer: coarse) { details.columns > summary { min-height: 44px; } } /* DESIGN 12: the site's coarse rule, which the line above outranks */
details.columns form { width: max-content; max-width: 100%; margin: var(--space-1) 0 0; padding: var(--space-2) var(--space-3); border: 1px solid var(--rule); border-radius: var(--radius); background: var(--bg); }
details.columns fieldset { margin: 0 0 var(--space-2); padding: 0; border: 0; }
details.columns ol { list-style: none; margin: 0; padding: 0; }
details.columns li { display: flex; align-items: center; gap: var(--space-1); }
details.columns li label { flex: 1 1 auto; margin: 0 var(--space-2) 0 0; }
details.columns li button { padding-inline: var(--space-2); }
details.columns li button:disabled { opacity: 0.4; cursor: default; box-shadow: none; } /* the first can go no higher, the last no lower */
.nav-switch { margin: 0; }
@media (max-width: 959px) {
  .nav-switch { display: none; }
}
@media (min-width: 960px) {
  /* From 960 the open menu floats under its button, so the toolbar and the table do not move. */
  details.columns { position: relative; }
  details.columns form { position: absolute; top: 100%; left: 0; z-index: 20; max-width: none; box-shadow: 0 8px 24px rgb(0 0 0 / 0.18); }
  body.desk.narrow { --rail-w: 3.5rem; grid-template-columns: var(--rail-w) minmax(0, 1fr); }
  body.desk.narrow > header .name { min-width: 0; }
  body.desk.narrow > .side { padding-inline: 0.375rem; }
  .narrow .side ul { margin-bottom: var(--space-3); }
  .narrow .side a,
  .narrow .side .nav-admin { justify-content: center; padding: 0.5rem 0; }
  .narrow .side .icon { width: 1.25rem; height: 1.25rem; }
  .side .nav-admin { display: flex; align-items: center; width: 100%; margin: 0; border: 0; border-radius: var(--radius); background: none; color: var(--on-chrome); font: inherit; cursor: pointer; }
  .side .nav-admin:hover,
  .side .nav-admin.here { background: var(--chrome-2); box-shadow: none; }
  .side .nav-admin.here .icon { color: var(--chrome-accent); }
  .side .nav-pop:not(:popover-open) { display: none; } /* `.side ul`'s flex would outrank the browser's own hiding */
  .side .nav-pop:popover-open {
    position: fixed; inset: auto; top: var(--top-h); left: var(--rail-w); margin: 0; min-width: 13rem; padding: var(--space-2);
    list-style: none; background: var(--chrome); color: var(--on-chrome); border: 1px solid var(--chrome-2); border-radius: var(--radius);
  }
  .side .nav-pop a { justify-content: flex-start; padding: 0.375rem 0.625rem; color: var(--on-chrome); }
}

/* --- The invoice popup (desk v2 Task 9; spec › 4.5, DESIGN 3 and 18): the page's one modal <dialog id="sheet">,
   which js/sheet.js fills with the invoice's body part (no script, no popup: the link opens the page). From 760 CSS
   pixels a panel over the dimmed grid, at most 64rem wide, the × at its top right; below 760 it fills the screen, an
   invoice's lines a list (the phone rules above), its footer padded for the home indicator. Its body scrolls inside
   it and never chains to the grid behind (`overscroll-behavior: contain`). The dialog has no padding of its own, so a
   click on the dialog element itself is a click on the backdrop, which closes it (APG Dialog (Modal) pattern). */
#sheet { width: min(64rem, calc(100vw - 2rem)); max-width: none; max-height: calc(100dvh - 2rem); padding: 0; color: var(--text); background: var(--bg); border: 1px solid var(--rule); border-radius: 10px; overflow: hidden; }
#sheet[open] { display: flex; flex-direction: column; }
#sheet::backdrop { background: rgb(15 27 45 / 0.55); }
#sheet .sheet-close { position: absolute; top: var(--space-2); right: var(--space-2); z-index: 1; padding: 0.125rem 0.625rem; font-size: 1.25rem; line-height: 1.2; color: var(--accent); background: var(--bg); border-color: var(--control); }
#sheet .sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
#sheet .sheet-status { margin: 0; padding: var(--space-4) 1.25rem; padding-right: 3.5rem; color: var(--muted); }
#sheet .invoice.panel { margin: 0; border: 0; border-radius: 0; padding-bottom: 0; }
#sheet .page-head { padding-right: 3rem; } /* clear of the × */
#sheet .page-head h2 { margin: 0; font-size: 1.375rem; }
#sheet .table-box { max-height: none; }
#sheet .sheet-foot { position: sticky; bottom: 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); margin: var(--space-3) -1.25rem 0; padding: var(--space-2) 1.25rem calc(var(--space-2) + env(safe-area-inset-bottom)); background: var(--bg); border-top: 1px solid var(--rule); }
#sheet .sheet-foot p { margin: 0 auto 0 0; }
#sheet a.btn.secondary { color: var(--accent); background: transparent; border-color: var(--control); }
/* Below 760, written as "not 760 and wider" so the coarse-pointer block above stays the last rule at the cards'
   width (tests/test_look.py's 16 px pin); the sheet sets no control's size. */
@media not all and (min-width: 760px) {
  #sheet { width: 100%; height: 100%; max-height: none; margin: 0; border: 0; border-radius: 0; }
  #sheet .invoice.panel { display: flex; flex-direction: column; min-height: 100%; padding-inline: var(--space-3); }
  #sheet .sheet-foot { margin-inline: calc(-1 * var(--space-3)); margin-top: auto; padding-inline: var(--space-3); } /* at the foot, in thumb reach */
}

/* --- The side panel (desk v2 Task 10; spec › 4.4), the second prototype beside the opened row: a non-modal
   <aside id="panel"> holding the invoice's body part, which the server renders from `?open=` and js/panel.js fills
   where it runs. From 1280 CSS pixels a third column of the desk at the right, at the viewport's height under the
   header, the grid narrowed beside it (the table box scrolls inside itself, as at any width); minimised (`data-min`) a
   strip at the right edge, its invoice's number written down it. Below 1280 a sheet from the bottom over the page,
   above the phone's bottom bar, the page given its height at the foot so the last rows scroll clear of it; minimised a
   bar. Marks in --mark; every colour a token. */
#panel[hidden] { display: none; }
body.desk { --panel-w: min(32rem, 36vw); --strip-w: 3.25rem; } /* on the body, whose grid reads them */
#panel { display: flex; flex-direction: column; min-width: 0; color: var(--text); background: var(--bg); }
#panel .panel-bar { position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); padding: var(--space-2) var(--space-3); background: var(--surface); border-bottom: 1px solid var(--rule); }
#panel .panel-bar a.btn.secondary { color: var(--accent); background: transparent; border-color: var(--control); }
#panel .panel-close { font-size: 1.25rem; line-height: 1; padding-inline: 0.625rem; }
#panel .panel-restore { margin-right: auto; }
#panel .panel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
#panel .sheet-status { margin: 0; padding: var(--space-4) 1.25rem; color: var(--muted); }
#panel .invoice.panel { margin: 0; border: 0; border-radius: 0; padding-bottom: 0; }
#panel .page-head h2 { margin: 0; font-size: 1.25rem; }
#panel .table-box { max-height: none; }
#panel .sheet-foot { position: sticky; bottom: 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); margin: var(--space-3) -1.25rem 0; padding: var(--space-2) 1.25rem calc(var(--space-2) + env(safe-area-inset-bottom)); background: var(--bg); border-top: 1px solid var(--rule); }
#panel .sheet-foot p { margin: 0 auto 0 0; }
#panel .sheet-foot a.btn.secondary { color: var(--accent); background: transparent; border-color: var(--control); }
#panel tr.here { background: var(--accent-soft); } /* the line opened from, in the panel and in the grid */
.grid tbody tr.row[aria-current] { background: var(--accent-soft); }
.grid tbody tr.row[aria-current] > :first-child,
#panel tr.here > :first-child { box-shadow: inset 3px 0 0 var(--mark); }
@media not all and (min-width: 760px) { /* the phone's invoice lines are flex rows, its grid rows cards: the bar on the row */
  #panel tr.here { box-shadow: inset 3px 0 0 var(--mark); }
  #panel tr.here > :first-child,
  .grid tbody tr.row[aria-current] > :first-child { box-shadow: none; }
  .wide.grid tbody tr.row[aria-current] { border-color: var(--mark); box-shadow: inset 3px 0 0 var(--mark); }
}
#panel[data-min] .panel-body,
#panel:not([data-min]) .panel-restore { display: none; }
.panel-switch { margin: 0; }
.panel-switch button[aria-pressed=true] { background: var(--accent-soft); border-color: var(--mark); box-shadow: inset 0 0 0 1px var(--mark); } /* on: marked, as a set filter is */
@media (min-width: 1280px) {
  body.desk:has(> #panel:not([hidden])) { grid-template-columns: var(--nav-w) minmax(0, 1fr) var(--panel-w); }
  body.desk.narrow:has(> #panel:not([hidden])) { grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--panel-w); }
  body.desk:has(> #panel[data-min]:not([hidden])) { --panel-w: var(--strip-w); }
  #panel { grid-column: 3; grid-row: 2; position: sticky; top: var(--top-h); align-self: start; height: calc(100vh - var(--top-h)); border-left: 1px solid var(--rule); }
  #panel[data-min] .panel-bar { flex-direction: column; flex: 1 1 auto; justify-content: flex-start; padding-inline: var(--space-1); border-bottom: 0; }
  #panel[data-min] .panel-restore { order: 1; margin: 0; padding: var(--space-3) var(--space-2); writing-mode: vertical-rl; }
  #panel[data-min] .panel-min { display: none; }
}
@media not all and (min-width: 1280px) {
  #panel { position: fixed; left: 0; right: 0; bottom: 0; z-index: 15; max-height: 60dvh; border-top: 1px solid var(--rule); border-radius: 10px 10px 0 0; box-shadow: 0 -8px 24px rgb(0 0 0 / 0.18); }
  #panel .panel-bar { border-radius: 10px 10px 0 0; }
  #panel[data-min] .panel-bar { border-bottom: 0; }
  #panel[data-min] .panel-min { display: none; }
  body.desk:has(> #panel:not([hidden], [data-min])) > main { padding-bottom: calc(60dvh + var(--space-4)); }
}
@media (min-width: 960px) and (max-width: 1279.98px) {
  #panel { left: var(--nav-w); }
  body.desk.narrow > #panel { left: var(--rail-w); }
}
@media (max-width: 959.98px) {
  #panel { bottom: calc(var(--bar-h) + env(safe-area-inset-bottom)); }
  body.desk:has(> #panel:not([hidden], [data-min])) > main { padding-bottom: calc(60dvh + var(--bar-h) + var(--space-4) + env(safe-area-inset-bottom)); }
}
