/* ============================================================================
   SHERPA Procurement Portal — design system
   Brand-aligned with ffesherpa.pro (see docs/DESIGN_SYSTEM.md). Light "reading"
   surface + dark chrome, brass accent, serif display / grotesk body / mono
   labels. Views use these shared classes; brand values live in :root only.
   ========================================================================== */

/* ---- Self-hosted brand fonts (latin subset from Google Fonts, OFL/Apache).
        Regenerate via scratchpad/parse_fonts.php. CSP: font-src falls back to
        'self', so no header change is needed. ---- */
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:400;font-display:swap;src:url('/assets/fonts/cormorant-garamond-italic.woff2') format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:500;font-display:swap;src:url('/assets/fonts/cormorant-garamond-italic.woff2') format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/cormorant-garamond.woff2') format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/cormorant-garamond.woff2') format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:600;font-display:swap;src:url('/assets/fonts/cormorant-garamond.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/ibm-plex-mono-400.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/ibm-plex-mono-500.woff2') format('woff2');}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:300;font-display:swap;src:url('/assets/fonts/space-grotesk.woff2') format('woff2');}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/space-grotesk.woff2') format('woff2');}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/space-grotesk.woff2') format('woff2');}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:600;font-display:swap;src:url('/assets/fonts/space-grotesk.woff2') format('woff2');}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:700;font-display:swap;src:url('/assets/fonts/space-grotesk.woff2') format('woff2');}
@font-face{font-family:'Tenor Sans';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/tenor-sans-400.woff2') format('woff2');}

/* ============================ 1. DESIGN TOKENS ============================ */
:root {
  /* Surfaces */
  --bg:        #efe9de;   /* warm cream page ground */
  --surface:   #fbfaf6;   /* cards / panels */
  --surface-2: #ffffff;   /* elevated / inputs */
  --chrome:    #0c0e10;   /* dark topbar + footer */
  --chrome-2:  #141a1f;   /* dark elevated (drawer/menus) */

  /* Ink */
  --ink:       #1b1e21;   /* primary text on cream */
  --ink-soft:  #3b4046;
  --muted:     #6c7176;   /* secondary text */
  --chrome-ink:#f4f0e7;   /* text on dark chrome */
  --chrome-dim:rgba(244,240,231,.52);

  /* Brass accent (brand) */
  --brass:     #b8916a;
  --brass-hi:  #cca97e;
  --brass-deep:#8c6743;   /* accessible brass for text/links on light */

  /* Lines */
  --line:       rgba(27,30,33,.12);
  --line-soft:  rgba(27,30,33,.07);
  --line-strong:rgba(27,30,33,.2);
  --line-dark:  rgba(244,240,231,.12);

  /* Status (warm brass/terracotta; green only as --live, a live connection, and --confirmed, a vendor's confirmation) */
  --ok:        #7a5a30;                    /* positive → deep brass */
  --ok-bg:     rgba(184,145,106,.16);
  --ok-line:   rgba(184,145,106,.5);
  --danger:    #a3341f;                    /* terracotta */
  --danger-bg: #f6e5e0;
  --danger-line:#e6bdb2;
  /* Live: the one green (Jeff, 3 November 2026, "use green"), for a connection
     that is on right now, never for success or done. Only .badge.live uses it. */
  --live:      #2e6b45;
  --live-bg:   #e3f0e7;
  --live-line: #a9cdb4;
  /* Vendor confirmed: the second green (Jeff, 7 November 2026, "Approved the row can have a green tint"), muted sage
     apart from --live, only for an item its vendor confirmed (modules/vendor_status.php). */
  --confirmed:      #3f6b4a;
  --confirmed-bg:   #e8efe6;
  --confirmed-line: #b9cdb6;
  /* Vendor change: burnt orange (same day, "requiring an update an orange tint"), for an item whose vendor changed
     a figure, or that was edited since they confirmed it. Also .alert.warn. */
  --change:      #9a5414;
  --change-bg:   #fbeadb;
  --change-line: #ecc29b;

  /* Charts (the project report). Validated with the dataviz palette checker
     against --surface: the two series pass lightness, chroma, colour-blind and
     contrast checks; the gates are one brass ramp, light to dark, in order;
     the plan line is a grey at 3.3:1. Used only for chart marks, never text. */
  --viz-1:     #a0702f;   /* the fact: received, paid, billed */
  --viz-2:     #2f6f9f;   /* what it is measured against: invoiced, ordered */
  --viz-plan:  #8f8a81;   /* expected: the plan, quietly */
  --viz-g1:    #cfa56c;
  --viz-g2:    #b0813f;
  --viz-g3:    #8a5e24;
  --viz-g4:    #5a3d16;

  /* Type */
  --font-sans:  'Space Grotesk', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-serif: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --font-mono:  'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-brand: 'Tenor Sans', var(--font-sans);

  /* Radii / shadow / rhythm */
  --r-pill: 999px;
  --r-card: 14px;
  --r-input: 10px;
  --r-sm: 8px;
  --shadow-card: 0 1px 2px rgba(27,30,33,.05), 0 10px 26px rgba(27,30,33,.05);
  --shadow-pop:  0 24px 60px rgba(12,14,16,.24);
  --maxw: 72rem;
  --maxw-wide: 100rem;   /* the Items list: room for every money column */
}

/* ============================ 2. RESET / BASE ============================ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--ink);
  line-height: 1.55;
  font-size: 1rem;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* A long word or link (a pasted URL in a comment) breaks to fit its box
     instead of widening the page on a phone. Only words that would overflow. */
  overflow-wrap: break-word;
}
/* A touch smaller on a phone (Jeff, 18 October): everything sized in rem
   follows. A percentage, so a reader who has set larger text keeps it. */
@media (max-width: 600px) {
  html { font-size: 93.75%; }
}

a { color: var(--brass-deep); text-decoration: none; transition: color .18s ease; }
a:hover { color: var(--brass); }
button, input, select, textarea { font: inherit; color: inherit; }
img, svg { display: block; max-width: 100%; }
::selection { background: var(--brass); color: #0c0e10; }
:focus-visible { outline: 1px solid var(--brass); outline-offset: 3px; border-radius: 3px; }

/* ============================ 3. TYPOGRAPHY ============================== */
h1 {
  font-family: var(--font-serif); font-weight: 500;
  font-size: clamp(1.9rem, 3.4vw, 2.6rem); line-height: 1.05;
  letter-spacing: -0.005em; color: var(--ink); margin: 0 0 .5rem;
}
h2 {
  font-family: var(--font-sans); font-weight: 600;
  font-size: 1.12rem; letter-spacing: -0.01em; margin: 0 0 .85rem;
}
h3 {
  font-family: var(--font-mono); font-weight: 500;
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); margin: 1.1rem 0 .4rem;
}
p { margin: 0 0 .3rem; }

/* Mono eyebrow / kicker — the "— PROOF" motif from the site */
.eyebrow {
  display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--font-mono); font-size: .62rem; font-weight: 500;
  letter-spacing: .22em; text-transform: uppercase; color: var(--brass-deep);
  margin-bottom: .7rem;
}
.eyebrow::before { content: ""; width: 26px; height: 1px; background: var(--brass); opacity: .8; }

/* Page header block: eyebrow + serif h1 + optional lede */
.page-head { margin: .25rem 0 1.4rem; }
.page-head .lede { color: var(--muted); font-size: 1.02rem; max-width: 46rem; }

.muted { color: var(--muted); }
.hint  { font-size: .85rem; color: var(--muted); }
em, i { font-style: italic; }
code { font-family: var(--font-mono); font-size: .88em; }

/* Layout utility: horizontal stack with wrap (no inline styles in views). */
.hstack { display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }

/* ============================ 4. CHROME (dark) ========================== */
.topbar {
  background: var(--chrome); color: var(--chrome-ink);
  display: flex; align-items: center; gap: 1rem;
  padding: .7rem 1.5rem; flex-wrap: wrap;
  border-bottom: 1px solid var(--line-dark);
}
/* The account controls travel together at the right rather than each drifting
   on its own gap, so the bar reads as two groups instead of seven items. */
.topbar-account {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
}
.topbar a { color: var(--chrome-ink); }
.topbar a:hover { color: var(--brass); }

/*
 * THE LOCKUP, STACKED.
 *
 * SHERPA and PROCUREMENT PORTAL used to sit on one line, and the nav that
 * followed shared the tag's font, size, letter-spacing and colour exactly —
 * so PROJECTS ADMIN read as more of the brand rather than as somewhere to go.
 * Stacking the two makes the mark a block with an edge, and the nav then
 * begins somewhere the eye can find.
 */
.brand {
  display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: .12rem; line-height: 1;
}
.brand .brand-mark {
  font-family: var(--font-brand); font-size: 1.28rem; letter-spacing: .28em;
  padding-left: .28em; line-height: 1; color: var(--chrome-ink);
}
.brand .brand-mark sup { font-size: .32em; top: -.9em; position: relative; letter-spacing: 0; }
.brand .brand-tag {
  font-family: var(--font-mono); font-size: .54rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--chrome-dim);
  padding-left: .3em;              /* optically aligned under the S */
}
.brand:hover .brand-mark { color: var(--chrome-ink); }

/*
 * Separated from the brand by a rule, and given more presence than the tag it
 * used to be mistaken for: brighter, and marked when you are on it.
 */
.topnav {
  display: flex; gap: 1.3rem; align-items: center;
  padding-left: 1.3rem;
  border-left: 1px solid var(--line-dark);
  align-self: stretch;
}
.topnav a {
  display: flex; align-items: center;
  font-family: var(--font-mono); font-size: .7rem; letter-spacing: .14em;
  text-transform: uppercase; color: rgba(244,240,231,.72);
}
/* is-current, not is-active: the projectbar already marks the page you are on
   that way and pairs it with aria-current. This rule named .is-active and
   nothing ever emitted it, so the marking the comment above describes has been
   dead since it was written. */
.topnav a:hover, .topnav a.is-current { color: var(--chrome-ink); }
.topnav a.is-current { border-bottom: 2px solid var(--brass); }

.topbar .spacer { flex: 1; }
.topbar .user { font-size: .82rem; color: var(--chrome-dim); }
.topbar form { margin: 0; display: flex; gap: .45rem; align-items: center; }

/* switcher + search on the dark bar */
.topbar select,
.topbar input[type="search"] {
  background: rgba(244,240,231,.06); color: var(--chrome-ink);
  border: 1px solid var(--line-dark); border-radius: var(--r-pill);
  padding: .42rem .8rem; font-size: .82rem;
}
.topbar input[type="search"] { min-width: 15rem; }
.topbar select:focus, .topbar input[type="search"]:focus {
  outline: none; border-color: var(--brass); background: rgba(244,240,231,.1);
}
.topbar select option { color: #0c0e10; }
.topbar .switch-label { font-family: var(--font-mono); font-size: .58rem; letter-spacing: .16em; text-transform: uppercase; color: var(--chrome-dim); }

/* ghost pill button used on the dark bar (log out) */
.topbar button.link {
  background: transparent; border: 1px solid rgba(244,240,231,.3);
  color: var(--chrome-ink); padding: .42rem .9rem; border-radius: var(--r-pill);
  cursor: pointer; font-family: var(--font-mono); font-size: .64rem;
  letter-spacing: .12em; text-transform: uppercase; transition: border-color .2s, color .2s;
}
.topbar button.link:hover { border-color: var(--brass); color: var(--brass-hi); }

/* Footer */
.site-footer {
  background: var(--chrome); color: var(--chrome-dim);
  border-top: 1px solid var(--line-dark);
  padding: 1.6rem 1.5rem; margin-top: 3rem;
}
.site-footer .footer-inner {
  max-width: var(--maxw); margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
}
.site-footer .footer-brand { font-family: var(--font-brand); font-size: .95rem; letter-spacing: .26em; color: var(--chrome-ink); }
/* Who built and runs this, on every page a client sees. Sentence case and not
   letter-spaced, deliberately: the row above is a product mark and reads as
   one, this is a sentence and should read as a sentence. Quieter than the row
   above it so it informs without competing. */
.site-footer .footer-colophon {
  max-width: var(--maxw); margin: .9rem auto 0;
  font-size: .72rem; line-height: 1.5; color: var(--chrome-dim);
}
.site-footer .footer-colophon a { color: var(--brass-hi); }
.site-footer .footer-colophon a:hover { color: var(--chrome-ink); }

/* ============================ 5. LAYOUT ================================= */
main { max-width: var(--maxw); margin: 2rem auto; padding: 0 1.5rem; }

/* ============================ 6. PANELS / CARDS ========================= */
.panel {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 1.5rem 1.6rem; margin-bottom: 1.4rem;
  box-shadow: var(--shadow-card);
}
.panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: .3rem 1rem; flex-wrap: wrap; }
.panel-head h2 { margin-bottom: 0; }
/* A flex item will not shrink below its longest word, so a long name or link
   in a heading row would push past the card; let it shrink and break. */
.panel-head > * { min-width: 0; }
/* A message under a page's heading row keeps clear of its buttons. */
.panel-head + .alert { margin-top: 1rem; }
/* The controls at the right of a panel heading, kept together with room
   between them rather than spread across the heading. */
.panel-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.panel-actions .view-toggle { margin: 0; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1.4rem; margin-bottom: 1.4rem; }
.cards .panel { margin-bottom: 0; }

.card { display: flex; flex-direction: column; }
.card h2 {
  font-family: var(--font-mono); font-size: .64rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--muted); margin-bottom: .5rem;
}
.card .metric {
  font-family: var(--font-sans); font-weight: 700; font-size: 2.1rem;
  line-height: 1.05; letter-spacing: -0.02em; margin: .1rem 0 .2rem;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.card .line { display: flex; justify-content: space-between; gap: 1rem; margin: .3rem 0; font-size: .92rem; }
.card .line strong { font-variant-numeric: tabular-nums; }

/* The money cards on Overview, two to a row. One card alone (a reader with no
   accounting figures) takes the row in two halves: the headline figure and its
   switch on the left, the charges beside it, so neither half stands empty. */
.cards--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cards--one { grid-template-columns: minmax(0, 1fr); }
.cards--one > .card {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  grid-auto-flow: row dense; column-gap: 3rem; align-content: start;
}
.cards--one > .card > * { grid-column: 2; }
.cards--one > .card > h2 { grid-column: 1; grid-row: 1; }
.cards--one > .card > .metric-switch { grid-column: 1; grid-row: 2 / span 12; }
@media (max-width: 860px) {
  .cards--pair { grid-template-columns: minmax(0, 1fr); }
  .cards--one > .card { display: flex; }
}

/* The count band above them (views/dashboard.php): the number large, its label
   under it, in one row; the note on what the totals leave out beside them. */
/* The Scope card: a panel, its numbers in one row with the note beside them. */
.statband-row { display: flex; flex-wrap: wrap; align-items: center; gap: .8rem 2.5rem; }
/* Each number at the top of its row, so a label that wraps to two lines does
   not lift its number above the one beside it. */
.statband-list { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .9rem 2.4rem; margin: 0; }
.statband-list > div { display: flex; flex-direction: column-reverse; }
.statband-list dt {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin-top: .25rem;
}
.statband-list dd {
  margin: 0; font-weight: 700; font-size: 1.7rem; line-height: 1.05;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--ink);
}
.statband-note { flex: 1 1 16rem; margin: 0; }
/* On a phone a row of four numbers broke three and one; two to a row keeps
   them in a grid, a long label wrapping inside its own column. */
@media (max-width: 600px) {
  .statband-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .9rem 1.2rem; }
}

/* Emphasis card (dark "suggested reading" treatment) — opt-in */
.panel--dark {
  background: linear-gradient(162deg, var(--chrome-2) 0%, var(--chrome) 100%);
  color: var(--chrome-ink); border-color: var(--line-dark);
}
.panel--dark h1, .panel--dark h2 { color: var(--chrome-ink); }
.panel--dark .muted, .panel--dark .hint { color: var(--chrome-dim); }
.panel--dark a { color: var(--brass-hi); }

/* ============================ 7. BADGES / CHIPS ========================= */
.badge {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .18rem .6rem; border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: .64rem; letter-spacing: .1em; text-transform: uppercase;
  background: rgba(27,30,33,.05); color: var(--muted); border: 1px solid var(--line);
}
.badge.ok       { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.badge.danger   { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.badge.live     { background: var(--live-bg); color: var(--live); border-color: var(--live-line); }
.alert.warn  { background: var(--change-bg); color: var(--change); border-color: var(--change-line); border-left-color: var(--change); }

/* The release rule (CP15, views/_release.php): reported, never enforced. */
.release-rule { border: 1px solid var(--line); border-left: 3px solid var(--line-strong); border-radius: var(--r-sm); padding: .65rem .85rem; margin: .9rem 0; background: var(--surface-2); }
.release-rule.is-met { border-left-color: var(--ok); }
.release-rule.is-waiting { border-left-color: var(--danger); }
.release-rule p { margin: .15rem 0; }
.release-rule-head { font-size: .9rem; }
.release-rule-state { font-size: .9rem; color: var(--ink-soft); }
/* Installation: one heading per receiving location (CP08, CP11). */
.location-head { font-size: 1rem; margin: 1.1rem 0 .4rem; display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }
.location-head .muted { font-size: .8rem; font-weight: 400; }
.badge.brass    { background: var(--ok-bg); color: var(--brass-deep); border-color: var(--ok-line); }
/* An edit saved from the portal that the next export has not yet carried back.
   Brass like "on order" (it is a fact in flight, not a fault), dashed because it
   is provisional: the export confirms it or contradicts it. */
.badge.syncing  { background: var(--ok-bg); color: var(--brass-deep); border-color: var(--brass); border-style: dashed; }

/* Filter chips (pill toggles — search/insights style) */
.chips { display: flex; flex-wrap: wrap; gap: .5rem; margin: .2rem 0 1rem; }
.chip {
  display: inline-flex; align-items: center; padding: .4rem .9rem; border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: .64rem; letter-spacing: .12em; text-transform: uppercase;
  background: transparent; color: var(--ink-soft); border: 1px solid var(--line-strong);
  cursor: pointer; transition: background .18s, color .18s, border-color .18s;
}
.chip:hover { border-color: var(--brass); color: var(--brass-deep); }
.chip.is-active { background: var(--chrome); color: var(--chrome-ink); border-color: var(--chrome); }
.chip.is-active.chip--brass { background: var(--brass); color: #0c0e10; border-color: var(--brass); }

/* ============================ 8. BUTTONS ================================ */
button.primary, a.button, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  background: var(--brass); color: #0c0e10; border: 1px solid var(--brass);
  padding: .62rem 1.15rem; border-radius: var(--r-pill); cursor: pointer;
  font-family: var(--font-mono); font-size: .7rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; transition: background .2s, border-color .2s, transform .18s;
}
button.primary:hover, a.button:hover, .btn:hover { background: var(--brass-hi); border-color: var(--brass-hi); color: #0c0e10; }
button.primary:active, a.button:active, .btn:active { transform: translateY(1px); }

.btn-ghost {
  background: transparent; color: var(--ink); border: 1px solid var(--line-strong);
}
.btn-ghost:hover { background: transparent; border-color: var(--brass); color: var(--brass-deep); }
/* Destructive, and says so: terracotta, never brass. */
.btn-danger { background: var(--danger); color: var(--danger-bg); border-color: var(--danger); }
.btn-danger:hover { background: transparent; border-color: var(--danger); color: var(--danger); }

/*
 * WORKING… (29 October 2026, app.js): an action sent and its page on the way.
 * The button pressed turns, says what it is doing and counts the seconds; a
 * bar runs across the top of the screen, where it is seen on any device. The
 * button is aria-disabled, never disabled, so the form still sends it.
 */
.is-busy[aria-disabled="true"] { cursor: progress; opacity: .88; }
button.is-busy::before {
  content: ""; display: inline-block; flex: none; width: .85em; height: .85em; margin-right: .1em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  vertical-align: -.12em; animation: busy-spin .8s linear infinite;
}
body.page-busy { cursor: progress; }
.busy-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 1000; height: 4px;
  display: none; overflow: hidden; pointer-events: none; background: var(--ok-bg);
}
body.page-busy .busy-bar, .busy-bar--demo { display: block; }
.busy-bar--demo { position: relative; z-index: auto; border-radius: var(--r-pill); }
.busy-bar-track {
  position: absolute; top: 0; bottom: 0; left: -40%; width: 40%;
  background: var(--brass); animation: busy-slide 1.3s ease-in-out infinite;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }
@keyframes busy-slide { from { left: -40%; } to { left: 100%; } }
@media (prefers-reduced-motion: reduce) {
  /* Still plainly working, without the motion: a full bar and a still ring. */
  button.is-busy::before { animation: none; border-right-color: currentColor; opacity: .55; }
  .busy-bar-track { animation: none; left: 0; width: 100%; }
}

/* ============================ 9. FORMS ================================== */
.field { margin-bottom: 1rem; }
.field label {
  display: block; font-family: var(--font-mono); font-size: .62rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-bottom: .4rem;
}
/* A label with a link on the same line ("Forgot password?" beside Password). */
.field-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .4rem; }
.field-head label { margin-bottom: 0; }
.field-head a { font-size: .8rem; }
.field input, .field select, textarea, .searchform input,
.item-toolbar input, .item-toolbar select, .resp-edit input[type="text"], .resp-edit select, .wording-row input {
  width: 100%; padding: .62rem .75rem; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-radius: var(--r-input); font: inherit; color: var(--ink);
  transition: border-color .18s, box-shadow .18s;
}
.field input:focus, .field select:focus, textarea:focus, .searchform input:focus,
.item-toolbar input:focus, .item-toolbar select:focus, .resp-edit input[type="text"]:focus, .resp-edit select:focus,
.wording-row input:focus {
  outline: none; border-color: var(--brass); box-shadow: 0 0 0 3px rgba(184,145,106,.16);
}
textarea { resize: vertical; }
.import-form .field input[type="file"] {
  padding: 1rem; border: 1px dashed var(--line-strong); background: var(--surface);
  border-radius: var(--r-input); cursor: pointer;
}

/* The line edit form (item page, ?edit=1). Two columns on a desk, one on a
   phone; a read-only field keeps its place so the layout says "this exists and
   is not yours to change" rather than hiding it. */
.edit-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 0 1.4rem; }
.edit-form h3 { grid-column: 1 / -1; margin: 1.2rem 0 .6rem; }
.edit-form .field.is-readonly input { background: var(--surface); color: var(--muted); border-style: dashed; }
.edit-form .field .hint { margin-top: .3rem; }
.edit-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin-top: .6rem; }
.edit-actions .hint { flex-basis: 100%; }
.edit-form .field-wide { grid-column: 1 / -1; }

/* ---- project setup (views/project-setup.php) ------------------------------
 * The contact list is people, not a ledger, so it is a list of cards: who, how
 * to reach them, what they may see, and an Edit that opens in place (a
 * <details>, so it needs no script). An inactive contact stays on the list,
 * dimmed, because "taken off" is a fact about them, not a deletion. */
.setup-contacts { list-style: none; margin: .4rem 0 0; padding: 0; display: grid; gap: .6rem; }
.setup-contact {
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
  padding: .65rem .8rem;
}
.setup-contact.is-inactive { opacity: .7; border-style: dashed; }
.setup-contact-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .7rem; }
.setup-contact-who .muted { font-size: .86rem; overflow-wrap: anywhere; }
.setup-contact-docs { flex-basis: 100%; font-size: .82rem; color: var(--ink-soft); }
.setup-edit { margin-top: .5rem; }
.setup-edit > summary { cursor: pointer; color: var(--brass-deep); font-size: .88rem; font-weight: 600; }
.setup-edit[open] > summary { margin-bottom: .5rem; }
.setup-add { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: .8rem; }
.setup-docs { grid-column: 1 / -1; border: 0; margin: .3rem 0 .4rem; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; }
.setup-docs legend { font-size: .78rem; font-weight: 600; color: var(--muted); padding: 0; margin-bottom: .3rem; }
.setup-docs label { display: inline-flex; align-items: center; gap: .4rem; font-size: .9rem; }
/* A field a submit was refused over: the reason sits under it, the border says which one. */
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--danger); }
.field.has-error .hint { color: var(--danger); }
/* A one-button form that sits in a row of links, so an action that must be a POST reads like its neighbours. */
.inline-form { display: inline; margin: 0; }
/* The item page's own actions, one row under the facts: the filled button
   is the thing most often done here, the eraser a quiet link at the end. */
.item-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .7rem; margin: 1.2rem 0 0; }
.item-actions-remove { margin-left: auto; font-size: .82rem; color: var(--muted); }
.item-actions-remove:hover { color: var(--danger); }
/* A report the workbook wrote, shown verbatim: its columns are how it reads. */
.report {
  font-family: var(--font-mono); font-size: .78rem; line-height: 1.5;
  white-space: pre-wrap; overflow-x: auto; margin: 0 0 1rem;
  padding: .9rem 1rem; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
}

/* ============================ 10. ALERTS =============================== */
.alert {
  padding: .7rem .95rem; border-radius: var(--r-sm); margin-bottom: 1rem; font-size: .92rem;
  border-left: 3px solid transparent;
}
.alert.error { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); border-left-color: var(--danger); }
.alert.ok    { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); border-left-color: var(--brass); }

/* ============================ 11. TABLES =============================== */
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: .62rem .7rem; border-bottom: 1px solid var(--line); }
th {
  font-family: var(--font-mono); font-size: .62rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
tbody tr { transition: background .15s; }
tbody tr:hover { background: rgba(184,145,106,.06); }
td { font-variant-numeric: tabular-nums; }
tfoot th { border-top: 2px solid var(--line-strong); border-bottom: none; font-variant-numeric: tabular-nums; color: var(--ink); }

/* ---- the items table: one row, two depths ---------------------------------
 *
 * The Stage column used to hold the whole lifecycle note, so one wrapped
 * sentence set the height of every row and pushed the money off to the right.
 * Here the row carries only what is scanned and the sentence moves into a
 * panel, which is also how this survives a phone: columns are given up from
 * the right as width runs out, and every column given up is already in the
 * panel. No second layout, and nothing unreachable at any width.
 */
.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;
}

/* A table narrower than its content scrolls itself; the page never does. */
.table-scroll { overflow-x: auto; }
/* A figure in any table's column reads right-aligned, its digits in line (was style="text-align:right" in views). */
table th.num, table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Small helpers that replaced inline styles (views/packet-detail.php): kept text breaks, a long link that wraps,
   space above a block. Listed on /styleguide. */
.pre-wrap { white-space: pre-wrap; }
.break-all { word-break: break-all; }
.space-above { margin-top: 1rem; }
.space-above-sm { margin-top: .75rem; }
/* The Transactions page's checks (views/_txn_checks.php): money columns line
   up on the right; each section's heading a step down from the panel's. */
.checks h3 { margin: 1.1rem 0 .4rem; font-size: 1rem; }
.checks th.num, .checks td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.checks td .hint { display: block; margin-top: .2rem; }
.checks ul { margin: 0; padding-left: 1.2rem; }
.checks li + li { margin-top: .3rem; }
.checks td:first-child { white-space: nowrap; }
/* The count is a way in to the findings (Jeff, 7 November 2026). */
a.badge.checks-count { text-decoration: none; cursor: pointer; }
a.badge.checks-count:hover, a.badge.checks-count:focus-visible { border-color: var(--danger); }
.checks-findings { margin: 1rem 0 .2rem; border: 1px solid var(--danger-line); border-left: 3px solid var(--danger);
  border-radius: var(--r-sm); background: var(--surface-2); padding: .2rem .9rem .6rem; scroll-margin-top: 5rem; }
.checks-findings > summary { cursor: pointer; font-weight: 600; padding: .6rem 0; color: var(--danger); }
.findings-list { margin: 0; padding-left: 1.2rem; }
.findings-list > li + li { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line-soft); }
.finding-title { margin: 0 0 .2rem; font-weight: 600; color: var(--ink); }
.finding-detail { margin: 0 0 .5rem; color: var(--ink-soft); }
.finding-do-head { margin: 0 0 .2rem; font-size: .8rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.checks ul.finding-do { margin: 0 0 .5rem; }
.finding-links { margin: 0; display: flex; flex-wrap: wrap; gap: .3rem .5rem; align-items: baseline; }
/* A document the findings link to is marked where it lands. */
.txn-row { scroll-margin-top: 5rem; }
.txn-row:target > td { background: var(--change-bg); }
.txn-row:target > td:first-child { box-shadow: inset 3px 0 0 var(--change); }
@media (max-width: 560px) {
  .checks .col-opt { display: none; }
  .checks th, .checks td { padding-left: .35rem; padding-right: .35rem; }
}

/* ---------------------------------------------------------------------------
   Charter Vendor Partners — a directory, not a table.

   Cards because a three-column table of names cannot be sliced below ~340px
   without restacking, and three pages already pushed the whole document
   sideways on a phone by trying. auto-fill + minmax means the grid reflows to
   one column on its own at the narrow end, with no breakpoint to maintain.
   --------------------------------------------------------------------------- */
/* The page head and its filter band: title and what the roster is on the
   left, Manage roster as a button beside the title; then one band set off by
   hairlines that says what it filters and how many show, above chips that each
   carry their count. */
.partner-head { align-items: flex-start; margin-bottom: 1.1rem; }
.partner-intro h1 { margin-bottom: .35rem; }
.partner-lede { margin: 0; max-width: 60ch; color: var(--ink-soft); }
.partner-head > .btn { margin-top: .3rem; }
.partner-filter { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line-soft); padding: .9rem 0 1rem; margin-bottom: 1.2rem; }
.partner-filter-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .3rem 1rem; margin-bottom: .7rem; }
.partner-filter-label {
  margin: 0; font-family: var(--font-mono); font-size: .64rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}
.partner-count { margin: 0; font-size: .85rem; color: var(--ink-soft); }
.partner-chips { gap: .45rem; margin: 0; }
.chip-count { margin-left: .5rem; padding-left: .5rem; border-left: 1px solid var(--line-strong); color: var(--muted); letter-spacing: .04em; }
.chip:hover .chip-count { color: var(--brass-deep); }
.chip.is-active .chip-count { color: var(--chrome-dim); border-left-color: var(--line-dark); }
.partner-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: .9rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15.5rem), 1fr));
}
.partner-card {
  display: flex; flex-direction: column; gap: .45rem;
  padding: 1rem 1.1rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  transition: border-color .18s;
}
.partner-card:hover { border-color: var(--brass); }
.partner-name {
  font-family: var(--font-serif); font-size: 1.18rem; line-height: 1.2;
  margin: 0; color: var(--ink);
}
.partner-cats { margin: 0; display: flex; flex-wrap: wrap; gap: .3rem .45rem; align-items: baseline; }
.partner-cat {
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
}
.partner-cat:hover { color: var(--brass-deep); text-decoration: underline; }
.partner-cat-sep { color: var(--line-strong); font-size: .62rem; }
/* Pushed to the bottom so every card in a row lines its link up, however many
   categories the one beside it carries. */
.partner-link {
  margin-top: auto; padding-top: .35rem;
  font-size: .85rem; color: var(--brass-deep);
  overflow-wrap: anywhere;
}
.partner-ext { font-size: .7rem; opacity: .7; }

/* A row of controls that must sit on one line — a form is block-level, so an
   inline button inside a table cell needs telling. */
.inline-actions { display: inline; margin: 0; }
.row-actions {
  display: flex; flex-wrap: wrap; gap: .1rem .8rem; align-items: baseline;
  white-space: nowrap; font-size: .85rem;
}
.danger-link { color: var(--danger); }
.mono-sm { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .04em; }



.items-table { min-width: 34rem; }
.items-table th { position: sticky; top: 0; z-index: 1; background: var(--surface); }
.items-table td { vertical-align: top; }
.items-table .num { text-align: right; }
.items-table .col-toggle { width: 2.2rem; padding-right: 0; }
/* Enrich selected: a tick per line with a product page, and the bar that sends them. */
.items-table .col-pick { width: 2rem; padding-right: 0; }
.items-table .col-pick input { margin: .2rem 0 0; }
.bulk-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; margin: 0 0 .75rem; }
.bulk-bar .hint { margin: 0; }
/* An outcome per line under one message (Enrich selected). */
.alert-lines { margin: .4rem 0 0; padding-left: 1.1rem; }
.alert-lines li { margin: .1rem 0; }
/* One row per item under a confirmation's count (Order differences, 5 November 2026: the bulleted list hung its
   markers outside the alert and ran the words together). The item, what happened as a badge, then why; the rows
   stack on a phone. */
.outcome-list { list-style: none; margin: .55rem 0 0; padding: 0; border-top: 1px solid var(--line); }
.outcome-list li { display: grid; grid-template-columns: 7.5rem 7.5rem 1fr; gap: .25rem .75rem;
  align-items: baseline; padding: .45rem 0; border-bottom: 1px solid var(--line); }
.outcome-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.outcome-list .outcome-item { font-family: var(--font-mono); font-size: .82rem; letter-spacing: .02em; color: var(--ink); }
.outcome-list .badge { justify-self: start; }
.outcome-list .outcome-words { color: var(--ink-soft); }
@media (max-width: 640px) {
  .outcome-list li { grid-template-columns: auto 1fr; }
  .outcome-list .outcome-words { grid-column: 1 / -1; }
}
.items-table .col-qty { width: 3.5rem; }
/* The item: its picture, its name, then its code, category and room. The name
   reads on two lines, not five (Jeff, 22 October 2026). */
.items-table .col-item { min-width: 18rem; }
/* The stage is a word and a mark, never a pill that wraps into a lozenge
   (27 October 2026): it wraps like text, two lines at most. */
.items-table .col-stage { min-width: 10rem; max-width: 15rem; }

/* ---- the Items list, best read (27 October 2026) ---------------------------
 * The list uses the width it is given: on a wide screen every money column
 * sits in view instead of past the card's edge. One row is two lines: the
 * picture beside the name, the code, category and room under it. The stage is
 * a word with a mark whose weight follows the ladder; the spec check has its
 * own column; numbers line up in tabular figures and never wrap. */
main:has(.items-panel) { max-width: var(--maxw-wide); }
.items-table thead th { padding-top: .75rem; padding-bottom: .75rem; border-bottom: 1px solid var(--line-strong); white-space: nowrap; }
.items-table .item-row > td { padding-top: .8rem; padding-bottom: .8rem; border-bottom-color: var(--line-soft); }
.items-table .num { white-space: nowrap; }
.item-cell { display: flex; align-items: flex-start; gap: .85rem; }
.item-text { min-width: 0; padding-top: .1rem; }
.item-text .row-title { line-height: 1.3; }
.item-text .row-sub { margin-top: .22rem; line-height: 1.4; }
.row-id {
  font-family: var(--font-mono); font-size: .74rem; letter-spacing: .02em;
  color: var(--brass-deep); text-decoration: none; white-space: nowrap;
}
.row-id:hover { text-decoration: underline; text-underline-offset: 3px; }
.unset { color: var(--line-strong); }
.qty-unit { display: block; font-size: .68rem; letter-spacing: .04em; color: var(--muted); }
.items-table .col-extra { max-width: 14rem; }
.items-table td.col-extra { font-size: .88rem; }
.items-table .col-wide { max-width: 18rem; }
.items-table .col-code { white-space: nowrap; }
.items-table td.col-code { font-family: var(--font-mono); font-size: .8rem; }
.items-table .col-date { white-space: nowrap; }
.items-table .col-date .stage-when { margin-top: 0; font-size: .82rem; }
.items-table .col-spec { white-space: nowrap; }

/* Where it stands: a word and a mark. The mark's weight follows the ladder
   (hollow while it is worked on, filled once agreed, dark once it is here),
   so progress reads down a column without reading the words. */
.status {
  display: inline-flex; align-items: baseline; gap: .45rem; max-width: 100%;
  font-size: .84rem; line-height: 1.3; color: var(--ink-soft);
}
.status-dot {
  flex: none; width: .55rem; height: .55rem; border-radius: 50%;
  background: var(--brass); transform: translateY(-.04rem);
}
.status-text { min-width: 0; }
.status-idle { color: var(--muted); }
.status-idle .status-dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--line-strong); }
.status-work .status-dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--brass); }
.status-ok { color: var(--ink); }
.status-ok .status-dot { background: var(--brass-hi); }
.status-live { color: var(--brass-deep); font-weight: 600; }
.status-live .status-dot { background: var(--brass); box-shadow: 0 0 0 3px var(--ok-bg); }
.status-ack { color: var(--ink); }
.status-ack .status-dot { background: var(--brass-deep); }
.status-done { color: var(--ink); font-weight: 600; }
.status-done .status-dot { background: var(--chrome); }
.status-out { color: var(--danger); }
.status-out .status-dot { background: var(--danger); }
.items-table .col-stage .stage-when { margin-left: 1rem; }
.items-table .col-stage .badge { margin-top: .3rem; }
/* With the Arrival column shown, the date is not said twice. */
.items-table:not(.hide-arrival) .c-stage .stage-when { display: none; }

/* The spec check, one word: what is still open links to it. */
.spec-state { display: inline-flex; align-items: center; gap: .4rem; font-size: .82rem; }
.spec-state::before { content: ""; flex: none; width: .45rem; height: .45rem; border-radius: 50%; background: currentColor; }
.spec-state.is-done { color: var(--muted); }
.spec-state.is-done::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--line-strong); }
a.spec-state.is-open { color: var(--danger); font-weight: 500; text-decoration: none; }
a.spec-state.is-open:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Each person's columns: one not chosen is hidden by a class on the table, so
   the Columns menu shows and hides it at once (app.js). */
.items-table.hide-vendor .c-vendor, .items-table.hide-manufacturer .c-manufacturer,
.items-table.hide-sku .c-sku, .items-table.hide-room .c-room, .items-table.hide-category .c-category,
.items-table.hide-finish .c-finish, .items-table.hide-dimensions .c-dimensions,
.items-table.hide-lead_time .c-lead_time, .items-table.hide-stage .c-stage,
.items-table.hide-arrival .c-arrival, .items-table.hide-spec .c-spec, .items-table.hide-po .c-po,
.items-table.hide-qty .c-qty, .items-table.hide-unit .c-unit, .items-table.hide-total .c-total,
.items-table.hide-unit_cost .c-unit_cost, .items-table.hide-cost .c-cost,
.items-table.hide-margin .c-margin { display: none; }

/* The Columns menu: a <details>, so it opens without script. */
.col-menu { position: relative; }
.col-menu > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .45rem; }
.col-menu > summary::-webkit-details-marker { display: none; }
.col-menu > summary::after { content: "▾"; font-size: .7em; }
.col-menu-n {
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: 0; line-height: 1.5;
  padding: 0 .42rem; border-radius: var(--r-pill); background: var(--ok-bg); color: var(--brass-deep);
}
.col-menu-pop {
  position: absolute; right: 0; top: calc(100% + .45rem); z-index: 8;
  width: min(36rem, calc(100vw - 2rem)); padding: 1rem 1.1rem;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-card);
  box-shadow: var(--shadow-pop);
}
.col-menu-lead { margin: 0 0 .8rem; font-size: .85rem; color: var(--muted); }
.col-menu-groups { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .8rem 1.4rem; }
.col-menu-groups fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.col-menu-groups legend {
  margin-bottom: .35rem; padding: 0;
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.col-choice { display: flex; align-items: center; gap: .55rem; margin: 0; padding: .3rem 0; font-size: .9rem; cursor: pointer; }
.col-choice input { accent-color: var(--brass-deep); width: 1rem; height: 1rem; margin: 0; }
.col-menu-actions [hidden] { display: none; }
.col-menu-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  margin-top: 1rem; padding-top: .85rem; border-top: 1px solid var(--line-soft);
}
.col-menu-status { font-size: .8rem; color: var(--muted); }
@media (max-width: 600px) {
  .col-menu-pop { position: fixed; left: 1rem; right: 1rem; top: auto; bottom: 1rem; width: auto; max-height: 75vh; overflow-y: auto; }
  .col-menu-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media print { .col-menu { display: none; } }

.row-title { display: block; font-weight: 600; }
.row-sub   { display: block; font-size: .82rem; color: var(--muted); }

/* ---- the readable list (16 October) ----------------------------------------
 * The name opens the item; under it, the category as a tag that filters to
 * it, then the room. Vendor stays on one line. Money says "Not priced" rather
 * than $0.00, and margin carries its share of sell. Groups head their rows
 * with a count and totals; the foot totals what the portal counts. */
a.row-title { color: var(--ink); text-decoration: none; }
a.row-title:hover { color: var(--brass-deep); text-decoration: underline; text-underline-offset: 3px; }
.row-cat {
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--brass-deep); text-decoration: none;
}
.row-cat:hover { text-decoration: underline; }
.row-sep { margin: 0 .4rem; color: var(--line-strong); }
.items-table .col-vendor { min-width: 9rem; max-width: 14rem; }
.items-table td.col-vendor { font-size: .9rem; line-height: 1.35; }
.unpriced { color: var(--muted); font-size: .82rem; }
.pct { display: block; font-size: .74rem; color: var(--muted); }
.items-table tbody th, .items-table tfoot th { position: static; text-align: left; font-weight: 400; }
.group-row > th, .group-row > td {
  padding-top: 1.1rem; padding-bottom: .45rem; background: var(--surface);
  border-bottom: 1px solid var(--line-strong); font-variant-numeric: tabular-nums;
}
.group-row > th, .group-row > td { vertical-align: top; }
.group-row > td { font-weight: 600; color: var(--ink-soft); font-size: .88rem; }
.group-name {
  font-family: var(--font-mono); font-size: .7rem; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--brass-deep); text-decoration: none;
}
a.group-name:hover { text-decoration: underline; }
.group-count { margin-left: .6rem; font-size: .8rem; color: var(--muted); white-space: nowrap; }
/* A phone hides the money columns; the heading then carries its totals. */
.group-money { display: none; }
.total-row > th, .total-row > td {
  vertical-align: top; padding-top: .8rem; border-top: 2px solid var(--line-strong); border-bottom: 0;
  font-weight: 600; font-variant-numeric: tabular-nums;
}

/* The toolbar: search, sort and grouping in sight; categories one click each. */
.item-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .8rem; margin: .4rem 0 .8rem; }
.item-search { flex: 1 1 18rem; display: flex; align-items: center; gap: .45rem; margin: 0; }
.item-search label { flex: 1 1 auto; min-width: 0; margin: 0; }
.item-search input { width: 100%; padding-top: .5rem; padding-bottom: .5rem; }
.item-tool { display: inline-flex; align-items: center; gap: .45rem; margin: 0; }
.item-tool > span:not(.sr-only) {
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.item-tool select { width: auto; padding-top: .5rem; padding-bottom: .5rem; }
.item-cats { position: relative; gap: .45rem; margin: 0 0 .9rem; }
/* On a phone the categories wrapped into a wall of chips above the first item.
   They are one row that scrolls sideways instead, each chip a full-height tap
   target; a fade at either edge says there is more that way. The fade is the
   script's to switch on (.is-ready): only app.js knows where the ends are, and
   without it a fade would cover the last chip for good (Codex on #171). */
@media (max-width: 600px) {
  .item-cats {
    flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; scrollbar-width: none; padding: .1rem 0;
  }
  .item-cats.is-ready {
    --fade-l: 0rem; --fade-r: 2.5rem;
    -webkit-mask-image: linear-gradient(to right, transparent 0, var(--ink) var(--fade-l), var(--ink) calc(100% - var(--fade-r)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, var(--ink) var(--fade-l), var(--ink) calc(100% - var(--fade-r)), transparent 100%);
  }
  .item-cats::-webkit-scrollbar { display: none; }
  .item-cats.is-scrolled { --fade-l: 1.5rem; }
  .item-cats.is-end { --fade-r: 0rem; }
  .item-cats .chip { flex: none; scroll-snap-align: start; min-height: 2.5rem; padding: .45rem .85rem; }
}

/* "Add items": one menu for the two ways in. */
.add-menu { position: relative; }
.add-menu > summary { list-style: none; cursor: pointer; }
.add-menu > summary::-webkit-details-marker { display: none; }
.add-menu > summary::after { content: "▾"; margin-left: .45rem; font-size: .7em; }
.add-menu-pop {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 6; min-width: 16rem;
  display: grid; padding: .35rem; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-sm); box-shadow: var(--shadow-pop);
}
.add-menu-pop a { display: block; padding: .55rem .7rem; border-radius: var(--r-sm); color: var(--ink); text-decoration: none; font-weight: 600; }
.add-menu-pop a:hover { background: var(--ok-bg); }
.add-menu-pop a span { display: block; font-weight: 400; font-size: .8rem; color: var(--muted); }

/* The bulk bar: with script, only once something is ticked, then fixed to
   the foot of the window wherever the tick was (sticky could not lift it
   above its place after the table). Without script it sits under the table. */
.bulk-bar[data-bulk-bar] {
  margin: .9rem 0 .4rem; padding: .55rem .8rem; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-radius: var(--r-card);
}
.bulk-bar.is-idle { display: none; }
.bulk-bar.is-live {
  position: fixed; left: 50%; bottom: .9rem; transform: translateX(-50%); z-index: 20;
  width: min(52rem, calc(100% - 2rem)); margin: 0; box-shadow: var(--shadow-pop);
}
body.has-bulk-bar { padding-bottom: var(--bulk-bar-room, 5.5rem); }
.bulk-count { font-weight: 600; }

/* Shown only once the stage and money columns have been given up. */
.row-meta { display: none; }
.meta-money { font-variant-numeric: tabular-nums; font-weight: 600; }
.meta-cost, .meta-qty { font-weight: 400; color: var(--muted); }

/* Ordered, then pulled off the order. It stays in the record and out of the
   totals, and the row should read that way before anyone reads the panel. */
.item-row.is-withdrawn .row-title { text-decoration: line-through; }
.item-row.is-withdrawn td { color: var(--muted); }

/* ---- the disclosure ---- */
.disclose {
  display: grid; place-items: center;
  width: 1.75rem; height: 1.75rem; padding: 0;
  background: none; border: 1px solid transparent; border-radius: var(--r-sm);
  color: var(--muted); cursor: pointer;
}
.disclose:hover { border-color: var(--line); color: var(--ink); }
.disclose:focus-visible { outline: 2px solid var(--brass-deep); outline-offset: 1px; }
.chev {
  width: .48rem; height: .48rem;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform .18s ease;
}
.disclose[aria-expanded="true"] { color: var(--brass-deep); }
.disclose[aria-expanded="true"] .chev { transform: rotate(45deg) translate(-1px, -1px); }

/* ---- the stage, as a shape rather than a sentence ---- */
.stage {
  display: inline-block; white-space: nowrap;
  padding: .12rem .5rem; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--bg);
  font-family: var(--font-mono); font-size: .66rem; font-weight: 500;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
/* Four steps of the same brass rather than four unrelated colours: the ladder
   is a progression, and the palette should say so. Only the ends leave it. */
.stage-work { color: var(--ink-soft); border-color: var(--line-strong); }
.stage-ok   { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.stage-live { background: var(--ok-bg); border-color: var(--brass); color: var(--brass-deep); font-weight: 600; }
.stage-done { background: var(--brass-deep); border-color: var(--brass-deep); color: var(--chrome-ink); }
.stage-out  { background: var(--danger-bg); border-color: var(--danger-line); color: var(--danger); }
/* Acknowledged: light brass fill. The ladder reads as increasing weight —
   Ordered is an outline, Acknowledged a light fill, Billed a dark one, Paid
   chrome — so progress is legible without reading the words. */
.stage-ack  { background: var(--brass-hi); border-color: var(--brass-hi); color: var(--chrome); }
/* Paid is the end of the money story and reads as closed rather than as a
   brighter Billed: chrome ground, brass text. Billed keeps .stage-done. */
.stage-paid { background: var(--chrome); border-color: var(--chrome); color: var(--brass-hi); }

/* ---- the filter bar --------------------------------------------------
 *
 * A <details>, so it opens and closes with no script, takes focus from the
 * keyboard and is announced without any work from us.
 *
 * ONLY THE SUMMARY IS PINNED. The table headers below are already
 * `position: sticky; top: 0`, and a second sticky element at the same offset
 * collides with them — while the height of an expandable panel cannot be
 * measured without script. So the summary is a bar of KNOWN height, the body
 * scrolls away under it, and the table headers take that height as a constant
 * offset. One number, declared once, used by both.
 */
:root { --filterbar-h: 2.6rem; }

/*
 * THE WHOLE <details> IS THE STICKY ELEMENT, not its summary.
 *
 * Pinning the summary was the obvious move and it does nothing: a sticky
 * element cannot leave its PARENT'S box, and the summary's parent is the
 * <details>, which is only as tall as itself. It pinned perfectly, inside a
 * container two lines high, and then scrolled away with it.
 *
 * The <details> has .panel as its parent, and .panel spans the whole table —
 * so pinning it there keeps the bar under the breadcrumbs for the length of
 * the list, which is the point.
 */
.filterbar {
  position: sticky; top: 0; z-index: 3;
  border: 1px solid var(--line);
  border-radius: var(--r-sm, 4px);
  margin: 1.25rem 0 1.5rem;
  background: var(--surface);
}

.filterbar-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: .4rem .7rem;
  min-height: var(--filterbar-h);
  padding: .4rem .8rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line-soft);
  border-radius: inherit;
  cursor: pointer;
  font-size: .84rem;
}
.filterbar[open] .filterbar-head { border-radius: var(--r-sm, 4px) var(--r-sm, 4px) 0 0; }
.filterbar-head::-webkit-details-marker { display: none; }
.filterbar-head::marker { content: ''; }

/* The disclosure arrow, drawn rather than relied on, so it points the way the
   rest of this portal's disclosures do. */
.filterbar-title { font-weight: 600; }
.filterbar-title::before {
  content: '▸'; display: inline-block; margin-right: .4rem;
  color: var(--muted); transition: transform .18s ease;
}
.filterbar[open] .filterbar-title::before { transform: rotate(90deg); }

.filterbar-count { color: var(--muted); font-variant-numeric: tabular-nums; }
.filterbar-count strong { color: var(--ink); }

/* The chips are the whole reason the count sits OUTSIDE the disclosure: a
   collapsed panel that hid the fact that filters are on is how a filtered list
   gets sent to somebody as the whole project. */
.filterbar-chip {
  padding: .05rem .45rem;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--bg);
  font-size: .7rem; color: var(--ink-soft); white-space: nowrap;
}

/*
 * Capped, because the panel is pinned in both states: left uncapped, an open
 * one would ride down the page covering most of the viewport. It scrolls
 * inside itself instead, and collapsing it is one click away on the bar that
 * is always visible.
 */
.filterbar-body {
  padding: .8rem; display: grid; gap: .9rem;
  max-height: 50vh; overflow-y: auto;
}

.filter-row { display: flex; flex-wrap: wrap; gap: .7rem 1rem; align-items: end; }
.filter-row label { display: grid; gap: .2rem; font-size: .74rem; color: var(--muted); }
.filter-row input, .filter-row select {
  font: inherit; font-size: .85rem; color: var(--ink);
  padding: .3rem .45rem;
  border: 1px solid var(--line); border-radius: var(--r-sm, 4px);
  background: var(--surface);
}
.filter-q { flex: 1 1 16rem; }
.filter-q input { width: 100%; }

.filter-groups {
  display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.filter-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.filter-group legend {
  padding: 0; margin-bottom: .25rem;
  font-size: .68rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
}
/* Capped and scrolled: a project with forty rooms would otherwise push the
   Apply button off the bottom of the screen. */
.filter-options { display: grid; gap: .1rem; max-height: 11rem; overflow-y: auto; }
.filter-opt {
  display: flex; align-items: center; gap: .4rem;
  padding: .1rem .2rem; border-radius: var(--r-sm, 4px);
  font-size: .82rem; cursor: pointer;
}
.filter-opt:hover { background: var(--bg); }
.filter-opt span:nth-of-type(1) { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.filter-n {
  font-family: var(--font-mono); font-size: .68rem; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.filter-actions { display: flex; align-items: center; gap: 1rem; }
.filter-clear { font-size: .8rem; }

/*
 * THE TABLE HEADER IS NOT OFFSET, AND MUST NOT BE.
 *
 * It looked obvious: pin the filter summary at 0, push `.items-table th` down
 * by the same height, two sticky elements sharing one number. On the live page
 * that hid the first row of the table behind its own header, at every scroll
 * position including the top.
 *
 * The reason is `.table-scroll { overflow-x: auto }` one screen up. An overflow
 * value other than visible makes that div the containing scrollport for
 * everything sticky inside it — so `top` is measured from the TABLE's box, not
 * the viewport, and any non-zero value simply shifts the header down over the
 * rows for good.
 *
 * So the header keeps top: 0 (its long-standing value, restated here only to
 * be found by whoever tries this again) and the filter bar is the one pinned
 * thing on this page.
 */
.items-table th { top: 0; }

@media (max-width: 560px) {
  .filterbar-body { padding: .6rem; }
  .filter-options { max-height: 8rem; }
}

@media print {
  /* The table header is already un-pinned for print further down; this only
     drops the panel, which is a control rather than content. */
  .filterbar { display: none; }
}

/* ---- when it gets here, under the stage it is at --------------------
 *
 * "Ordered" and "when does it arrive" are the same question asked twice, and
 * the second half used to need the line opened. It sits UNDER the chip rather
 * than beside it so the column still scans as a row of shapes.
 *
 * Muted by default because most of these are neither good news nor bad — only
 * "late" earns a colour, and it borrows the one the schedule card already uses
 * for overdue rather than introducing a second red.
 */
.stage-when {
  display: block; margin-top: .22rem;
  font-size: .68rem; line-height: 1.25; color: var(--muted);
  white-space: nowrap;
}
.stage-when.is-arrived { color: var(--ok); }
.stage-when.is-late { color: var(--danger); font-weight: 600; }
/* On order with no date given (CP10): said, but quietly. It is a gap to chase,
   not a delay, so it is not terracotta. */
.stage-when.is-unknown { color: var(--muted); font-style: italic; }
/* A tile's text sits on a dark overlay on desktop and on the light surface on
   touch screens, and .tile-info already colours it for each. Only the italic. */
.tile-when.is-unknown { font-style: italic; }

/* On a tile the info block is white-on-image, so the muted grey above would
   vanish. Inherit the tile's own colour and lean on weight instead. */
.tile-when { font-variant-numeric: tabular-nums; }
.tile-when.is-late { font-weight: 700; }

/* ---- the panel ---- */
.row-detail > td { background: rgba(184,145,106,.05); padding: .9rem .7rem 1.1rem 2.9rem; }
.row-detail[hidden] { display: none; }

.detail-body { display: flex; gap: 1.1rem; align-items: flex-start; }
.detail-main { flex: 1 1 auto; min-width: 0; }

/* The picture beside an item's name in the Items list (27 October): seen
   without opening the row. An item with no image keeps the square, quietly. */
.row-mini-thumb {
  display: block; flex: none; width: 3rem; height: 3rem;
  object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--surface-2);
}
.row-mini-thumb.is-empty { background: var(--line-soft); border-color: transparent; }

.detail-body > .row-thumb {
  flex: 0 0 auto; width: 5.5rem; height: 5.5rem;
  object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--surface);
}
/* The placeholder is a <span>, so it needs the box the <img> gets for free. */
.detail-body > span.row-thumb {
  display: grid; place-items: center; padding: .3rem; text-align: center;
  font-size: .62rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}

.detail-grid {
  display: grid; gap: .55rem 1.4rem; margin: 0;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.detail-grid dt {
  font-family: var(--font-mono); font-size: .6rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.detail-grid dd { margin: .1rem 0 0; font-size: .9rem; }
.detail-more { margin: .9rem 0 0; font-size: .85rem; }

/* ---- giving up columns, rightmost and least-scanned first ---- */
/* With the Margin column gone, its figure rides under the last money cell. */
.margin-note { display: none; font-size: .74rem; font-weight: 400; color: var(--muted); white-space: nowrap; }
/* With the Unit price column gone, the price per item stacks under the total. */
.unit-note { display: none; font-size: .78rem; font-weight: 400; color: var(--muted); white-space: nowrap; }
@media (max-width: 1100px) {
  .items-table .col-unit { display: none; }
  .items-table .unit-note { display: block; }
  .items-table .col-margin { display: none; }
  .items-table .col-margin.is-sole { display: table-cell; }
  .items-table .margin-note { display: block; }
  .items-table .col-vendor { max-width: 7rem; }
}
@media (max-width: 900px)  { .items-table .col-qbo    { display: none; } }
@media (max-width: 760px) {
  .items-table .col-vendor, .items-table .col-extra, .items-table .col-spec { display: none; }
  .items-table { min-width: 0; }
}
@media (max-width: 560px) {
  .items-table .col-qty,
  .items-table .col-stage,
  .items-table .col-money,
  .items-table .col-margin.is-sole { display: none; }
  .items-table th, .items-table td { padding: .55rem .4rem; }
  .items-table .col-item { min-width: 0; }
  .row-mini-thumb { width: 2.5rem; height: 2.5rem; }
  /* The table can be no narrower than its widest unbreakable word, and with
     the text a little larger it outgrew the card (Jeff, 18 October): any cell
     may break a word to fit, the id wraps at its hyphens, the date wraps. */
  .items-table td { overflow-wrap: anywhere; }
  .group-money {
    display: flex; flex-wrap: wrap; gap: .1rem .6rem; margin-top: .25rem;
    font-family: var(--font-sans); font-size: .82rem; letter-spacing: 0; text-transform: none;
  }
  .row-meta {
    display: flex; flex-wrap: wrap; gap: .1rem .6rem;
    margin-top: .3rem; font-size: .82rem;
  }
  .row-meta .stage { font-size: .58rem; }
  .row-meta .status { font-size: .8rem; }
  /* In the narrow row the meta line is a flex row, so the date sits BESIDE the
     stage rather than under it. Same values, different shape — which is the
     whole point of rendering both from one arrival_state(). */
  .row-meta .stage-when { display: inline; margin-top: 0; white-space: normal; }
  .row-detail > td { padding-left: 1rem; }
  .detail-body { flex-direction: column; }
  .detail-body > .row-thumb { width: 100%; height: 8.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .chev { transition: none; }
}

@media print {
  .disclose, .col-toggle { display: none; }
  .row-detail[hidden] { display: table-row; }
  .items-table th { position: static; }
}

table.kv th { text-transform: none; font-family: var(--font-sans); color: var(--muted); width: 12rem; font-weight: 600; vertical-align: top; letter-spacing: 0; font-size: .9rem; }
table.kv td { font-variant-numeric: tabular-nums; }

/* ---- the project list on a phone -------------------------------------
 *
 * Six columns need about 486px and a phone panel is 343px, so this table
 * used to push the whole PAGE sideways — the dark topbar and the project
 * nav slid off with it, which is why it read as a broken layout rather
 * than a wide table.
 *
 * Below 620px each row becomes a card. Not a horizontal scroll: a project
 * list is short and card-shaped already, and a row nobody can see all of
 * is not a row. The labels come from data-label attributes in the view.
 */
@media (max-width: 620px) {
  .projects-table,
  .projects-table tbody,
  .projects-table tr,
  .projects-table td { display: block; width: auto; }

  /* The header row still names the columns for a screen reader. */
  .projects-table thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }

  .projects-table tr {
    background: var(--surface-2); border: 1px solid var(--line);
    border-radius: var(--r-sm); padding: .9rem 1rem 1rem; margin-bottom: .8rem;
  }
  .projects-table td { border: 0; padding: .2rem 0; }

  /* Code and name are the card's heading, so they carry no label. */
  .projects-table .pt-code {
    font-family: var(--font-mono); font-size: .6rem;
    letter-spacing: .14em; color: var(--brass-deep);
  }
  .projects-table .pt-name {
    font-size: 1.05rem; font-weight: 600; line-height: 1.25;
    margin-bottom: .55rem; padding-bottom: .55rem;
    border-bottom: 1px solid var(--line-soft);
  }

  /* Everything else is a label/value pair, label left, value right. */
  .projects-table td[data-label] {
    display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  }
  .projects-table td[data-label]::before {
    content: attr(data-label);
    font-family: var(--font-mono); font-size: .58rem; font-weight: 500;
    letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
    flex: 0 0 auto;
  }
  .projects-table td[data-label] { text-align: right; }

  .projects-table .pt-open { margin-top: .7rem; }
  .projects-table .pt-open a {
    display: block; text-align: center; padding: .55rem .9rem;
    border: 1px solid var(--line); border-radius: var(--r-pill); font-size: .88rem;
  }
}

/* ============================ 12. LOGIN / FRONT DOOR =================== */
.login-wrap { width: 100%; max-width: 24rem; margin: 3rem auto; }
.login-wrap .brand-lockup {
  text-align: center; margin-bottom: 1.6rem;
  font-family: var(--font-brand); font-size: 1.5rem; letter-spacing: .3em; padding-left: .3em; color: var(--ink);
}
.login-wrap .brand-lockup sup { font-size: .32em; top: -.9em; position: relative; letter-spacing: 0; }
.login-wrap .brand-lockup small {
  display: block; margin-top: .5rem; font-family: var(--font-mono); font-size: .56rem;
  letter-spacing: .2em; text-transform: uppercase; color: var(--muted); padding-left: 0;
}
.login-wrap button.primary { width: 100%; margin-top: .9rem; padding-top: .8rem; padding-bottom: .8rem; }
.login-wrap .panel { padding: 2rem 2rem 1.75rem; }
.login-wrap h1 { margin-bottom: 1.25rem; }
.login-wrap .hint { margin: 1rem 0 0; }
.login-wrap .field .hint { margin: .35rem 0 0; }
.login-lead { color: var(--muted); margin: -.25rem 0 1.25rem; }
/* A short sign-in page still keeps the footer at the foot of the window. */
body:has(.login-wrap) { min-height: 100vh; display: flex; flex-direction: column; }
body:has(.login-wrap) main {
  flex: 1 0 auto; width: 100%; box-sizing: border-box; margin: 0 auto;
  display: flex; flex-direction: column; justify-content: center;
}
/* The quiet line under the card: the way back, or who to ask. */
.login-foot { margin: 1.25rem 0 0; text-align: center; font-size: .85rem; color: var(--muted); }
@media (max-width: 30rem) {
  .login-wrap { margin: 2rem auto; }
  .login-wrap .panel { padding: 1.5rem 1.25rem 1.25rem; }
}

/* ============================ 13. PROJECT META ========================= */
/* The project header (views/dashboard.php), read top to bottom: the title,
   one strip of facts, then two reference cards side by side (where the job is,
   who is on it); notes and the tool bar run the full width beneath. */
.projhead h1 { margin-bottom: .25rem; }
.projhead-client { color: var(--muted); margin: 0; }
/* The facts strip: one cell per fact, hairlines between them. The 1px gap over
   the line colour draws the dividers, so they stay right however the cells
   wrap. */
.projfacts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 1px; margin: 1.4rem 0 0; background: var(--line-soft);
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
}
.projfacts > div { min-width: 0; padding: .8rem 1.1rem .85rem; background: var(--surface-2); }
.projfacts dt,
.projcard-label {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 .35rem;
}
.projfacts dd { margin: 0; font-size: 1rem; font-weight: 500; overflow-wrap: anywhere; }
/* The two reference cards. The team usually has the longer values, so it gets
   the wider column. */
.projdetails {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  gap: 1rem; margin-top: 1rem; align-items: start;
}
.projdetails--one { grid-template-columns: minmax(0, 1fr); }
.projcard {
  min-width: 0; padding: .9rem 1.1rem .95rem;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
}
/* A rule under the label makes it the card's heading. */
.projcard-label { margin-bottom: .15rem; padding-bottom: .55rem; border-bottom: 1px solid var(--line-soft); }
/* Label and value rows: the label in a fixed column, so every value starts on
   the same line and a long one wraps under itself. */
.projrows { margin: 0; }
.projrows > div {
  display: grid; grid-template-columns: 8.5rem minmax(0, 1fr); gap: .2rem 1rem;
  padding: .6rem 0; border-bottom: 1px solid var(--line-soft);
}
.projrows > div:last-child { border-bottom: 0; padding-bottom: 0; }
.projrows dt { margin: 0; font-size: .8rem; color: var(--muted); padding-top: .08rem; }
.projrows dd { margin: 0; font-size: .92rem; overflow-wrap: anywhere; }
.projrows dd > span { display: block; }
.projrows dd > span + span { margin-top: .3rem; }
.projsame { color: var(--muted); }
/* Search and the exports, one bar under a hairline. */
.projtools {
  display: flex; flex-wrap: wrap; align-items: center; gap: .8rem 1.5rem;
  margin-top: 1.3rem; padding-top: 1.1rem; border-top: 1px solid var(--line-soft);
}
.projtools .searchform { flex: 1 1 22rem; margin: 0; }
.projtools .report-links { margin: 0; }
.report-links span { color: var(--muted); }

/* ---- the delivery address --------------------------------------------
 *
 * A delivery location is a shipping label, so it is set like one rather than
 * run together as a sentence: the receiver carries the weight, the routing
 * prefix and the dock instruction sit back out of the way, and the phone is
 * dialable from a phone. Line breaks come from app/helpers.php, which only
 * splits when it can do so without losing a word.
 */
.addr { font-style: normal; display: flex; flex-direction: column; gap: .15rem; }
.addr span { display: block; }
.addr .addr-company { font-weight: 600; }
.addr .addr-city { font-variant-numeric: tabular-nums; }
.addr .addr-attn,
.addr .addr-note { font-size: .82rem; color: var(--muted); }
/* attn and note are the supporting lines: routing above, an instruction to the
   driver below. Muted type carries that on its own — an earlier version marked
   the note with a rule, which then ran parallel to the column divider a few
   pixels away and read as a rendering fault. */
.addr-tel {
  font-family: var(--font-mono); font-size: .8rem;
  font-variant-numeric: tabular-nums; letter-spacing: .01em;
  margin-top: .5rem; align-self: flex-start;
}

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

@media (max-width: 560px) {
  /* Label over value at phone widths, where a fixed label column leaves the
     value too little room. */
  .projrows > div { grid-template-columns: minmax(0, 1fr); }
  .projfacts > div { padding: .7rem .85rem; }
}
.projnotes {
  margin: 1.3rem 0 0; padding: .7rem .9rem; background: var(--ok-bg);
  border: 1px solid var(--ok-line); border-left: 3px solid var(--brass);
  border-radius: var(--r-sm); font-size: .9rem;
}

/* ============================ 14. SEARCH / RESULTS ===================== */
.searchform { display: flex; gap: .6rem; margin: 1rem 0 0; }
.searchform input { flex: 1; padding: .7rem .9rem; border-radius: var(--r-input); }
.report-links { font-size: .9rem; margin-top: .8rem; display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }
.report-actions { display: flex; gap: .6rem; align-items: center; }

ul.results { list-style: none; margin: .6rem 0 0; }
ul.results li {
  padding: .7rem .2rem; border-bottom: 1px solid var(--line);
  display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap;
}

/* ============================ 15. ITEM DETAIL ========================== */
.product-detail { display: flex; gap: 1.6rem; align-items: flex-start; flex-wrap: wrap; }
.product-image {
  max-width: 300px; border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--surface-2); filter: saturate(.95); box-shadow: var(--shadow-card);
}
.product-desc { flex: 1; min-width: 16rem; margin: 0; white-space: pre-wrap; color: var(--ink-soft); }

/* The image belongs beside the specification it illustrates, not in a panel
   below it: someone checking a finish is checking it against the picture.
   min-width:0 on the table because a flex child defaults to min-content width,
   and one long fabric name would otherwise push the image out of the panel
   rather than wrapping. */
.spec-layout { display: flex; gap: 1.6rem; align-items: flex-start; }
.spec-layout > table { flex: 1; min-width: 0; }
.spec-figure { flex: 0 0 auto; width: 280px; margin: 0; }
/* The frame is held whether or not the image arrives. These URLs point at
   vendor CDNs we do not control; one that 404s or blocks hotlinking collapses
   an unsized <img> to nothing, which looks exactly like no image at all — the
   same silent failure the empty state below exists to prevent. */
.spec-figure .product-image {
  width: 100%; max-width: none; display: block;
  aspect-ratio: 4 / 3; object-fit: contain;
}
.spec-figure figcaption { margin-top: .5rem; }

/* Every image of an item, the vendor's lead image first (views/gallery.php).
   A row that scrolls sideways one image at a time, so it works without
   script; app.js shows the arrows and the count. The frame keeps its shape
   whether or not a vendor's image arrives, like .product-image above. */
.gallery { position: relative; }
.gallery-track {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  scrollbar-width: none; overscroll-behavior-x: contain;
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface-2);
}
.gallery-track::-webkit-scrollbar { display: none; }
.gallery-slide { flex: 0 0 100%; scroll-snap-align: start; display: flex; }
.gallery-slide > a { display: flex; width: 100%; }
.gallery-image { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: contain; }
.gallery--product .gallery-track { box-shadow: var(--shadow-card); }
.gallery--card .gallery-track { border: 0; border-radius: 0; background: var(--surface); }
.gallery--card .gallery-image { aspect-ratio: 1 / 1; }
.gallery-nav {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  margin-top: .45rem; font-size: .8rem; color: var(--muted);
}
.gallery-nav[hidden] { display: none; }
.gallery--card .gallery-nav { margin: .35rem 0 0; }
.gallery-btn {
  width: 2rem; height: 2rem; padding: 0; border-radius: 50%;
  border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink);
  font-size: 1rem; line-height: 1; cursor: pointer;
}
.gallery-btn:hover { border-color: var(--brass); color: var(--brass-deep); }
.gallery-count { min-width: 3.5rem; text-align: center; font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) {
  .gallery-track { scroll-behavior: auto; }
}

/* A missing image says so. Omitting it silently reads as "this portal does not
   show images", which is a different and wrong conclusion. */
.product-image.is-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .3rem;
  aspect-ratio: 4 / 3; padding: 1rem; color: var(--muted); font-size: .85rem; text-align: center;
  border-style: dashed; box-shadow: none;
}
.product-image.is-empty .hint { font-size: .78rem; }
/* On a phone the empty frame is a line, not a screenful of nothing. */
@media (max-width: 860px) {
  .spec-figure .product-image.is-empty { aspect-ratio: auto; min-height: 0; padding: .8rem 1rem; }
}

/* ---- the item header (views/item-detail.php, 29 October 2026) ----------
 * Which item, where it goes, where it stands, how many, when, and what the
 * client pays: read before anything is scrolled. The id is a code, set as
 * one; the name alone is the heading, so it never breaks mid-id. */
.item-hero-id {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .7rem; margin: 0 0 .55rem;
  font-family: var(--font-mono); font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--brass-deep);
}
.item-hero-id::before { content: ""; width: 26px; height: 1px; background: var(--brass); opacity: .8; }
.item-hero-code { letter-spacing: .08em; color: var(--ink-soft); user-select: all; }
.item-hero-cat::before { content: "·"; margin-right: .7rem; color: var(--line-strong); }
.item-hero-title { margin: 0 0 .45rem; overflow-wrap: anywhere; }
.item-hero-sub { margin: 0; color: var(--ink-soft); font-size: .98rem; }
.item-hero-sub span + span::before { content: "·"; margin: 0 .55rem; color: var(--brass); }
/* On a phone the maker and the room are a line each, the room quieter. */
@media (max-width: 560px) {
  .item-hero-sub span { display: block; }
  .item-hero-sub span + span { color: var(--muted); font-size: .92rem; }
  .item-hero-sub span + span::before { content: none; }
}

/* The facts: a row of small tiles, the label in the site's mono, the value
   large. Two to a row on a phone; status takes the full width there because
   it carries the most words. */
.item-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: 1px;
  margin: 1.2rem 0 0; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
}
.item-fact { margin: 0; padding: .75rem .9rem .8rem; background: var(--surface-2); min-width: 0; }
.item-fact dt {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 .3rem;
}
.item-fact dd { margin: 0; display: flex; flex-direction: column; gap: .15rem; }
.item-fact-value { font-size: 1.08rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.25; }
.item-fact-note { font-size: .78rem; color: var(--muted); line-height: 1.35; }
.item-fact .gate-tag { white-space: normal; line-height: 1.5; }
.item-fact .stage-raw { margin-top: 0; }
.item-fact.is-late { box-shadow: inset 3px 0 0 var(--danger); }
.item-fact.is-late .item-fact-note { color: var(--danger); }
@media (min-width: 861px) {
  .item-fact--status { grid-column: span 2; }
}
@media (max-width: 860px) {
  .item-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .item-fact--status { grid-column: 1 / -1; }
  /* Status takes the first row; an even count after it leaves the last
     tile alone on its row, so it takes the row rather than leave a hole. */
  .item-fact:last-child:nth-child(even) { grid-column: 1 / -1; }
}

/* On this page: the item's sections as one row of links, pinned while the
   page scrolls, sideways on a phone. Plain anchors; no script. */
.item-jump {
  position: sticky; top: 0; z-index: 5; display: flex; gap: .35rem; overflow-x: auto; scrollbar-width: none;
  margin: 0 0 1rem; padding: .5rem .2rem; background: var(--bg);
  border-bottom: 1px solid var(--line-soft);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent);
}
.item-jump::-webkit-scrollbar { display: none; }
.item-jump a {
  flex: 0 0 auto; padding: .38rem .8rem; border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  font-size: .82rem; color: var(--ink-soft); text-decoration: none; background: var(--surface); white-space: nowrap;
}
.item-jump a:hover, .item-jump a:focus-visible { border-color: var(--brass); color: var(--brass-deep); }
/* The section being read (app.js marks it; without script nothing is marked). */
.item-jump a[aria-current] { background: var(--chrome); border-color: var(--chrome); color: var(--chrome-ink); }
/* A section reached from the bar lands below it, not under it. */
main .panel[id] { scroll-margin-top: 3.6rem; }
/* "None set: Paint, Size" — what is not set, in one line. */
.item-none { margin: .4rem 0 .2rem; font-size: .92rem; color: var(--ink-soft); }

/* ======================= 15b. ITEM GRID (IMAGES VIEW) ================== */

.view-toggle { display: flex; gap: .1rem; margin: 0; }
.view-toggle a {
  padding: .3rem .8rem; font-size: .85rem; text-decoration: none;
  border: 1px solid var(--line); color: var(--muted); background: var(--surface);
}
.view-toggle a:first-child { border-radius: var(--r-card) 0 0 var(--r-card); }
.view-toggle a:last-child { border-radius: 0 var(--r-card) var(--r-card) 0; border-left: none; }
.view-toggle a.is-current { color: var(--ink); background: var(--surface-2); font-weight: 600; }

.item-grid {
  list-style: none; margin: 1rem 0 0; padding: 0;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}

.tile {
  position: relative; display: block; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--surface-2); text-decoration: none; color: inherit;
}
.tile-image {
  display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: contain;
  background: var(--surface-2);
}
/* An item with no captured image still occupies a tile and still carries its
   name — dropping it would make the grid a picture of the project that omits
   exactly the custom pieces. */
.tile-image.is-empty {
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; text-align: center; font-size: .82rem; color: var(--muted);
}

.tile-flag {
  position: absolute; top: .5rem; left: .5rem; z-index: 1;
  padding: .15rem .45rem; border-radius: 999px;
  font-size: .7rem; letter-spacing: .04em; text-transform: uppercase;
  background: var(--danger-bg); color: var(--danger); border: 1px solid var(--danger-line);
}

/*
 * The overlay carries room, vendor and name.
 *
 * Hover ALONE would hide this from two groups who are not edge cases here: a
 * keyboard user, and anyone on a tablet — which is what a designer walks a site
 * with. So it is revealed by :hover and :focus-within, and on a device with no
 * hover at all it is simply always visible. Information behind an interaction
 * the device cannot perform is information that does not exist.
 */
.tile-info {
  position: absolute; inset: auto 0 0 0;
  display: flex; flex-direction: column; gap: .1rem;
  padding: .6rem .7rem; font-size: .8rem; line-height: 1.35;
  color: #fff; background: linear-gradient(to top, rgba(20,18,16,.92), rgba(20,18,16,.75));
  transform: translateY(100%); opacity: 0;
  transition: transform .18s ease, opacity .18s ease;
}
.tile-info strong { font-size: .86rem; font-weight: 600; }
.tile-info span { color: rgba(255,255,255,.82); }
.tile:hover .tile-info,
.tile:focus-visible .tile-info,
.tile:focus-within .tile-info { transform: translateY(0); opacity: 1; }
.tile:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }

@media (hover: none) {
  .tile-info { position: static; transform: none; opacity: 1;
    color: var(--ink); background: var(--surface); border-top: 1px solid var(--line); }
  .tile-info span { color: var(--muted); }
}
/* A single column on a phone is a list with pictures, which is what the other
   view already is. Two narrower tiles keep it a gallery. */
@media (max-width: 560px) {
  .item-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: .7rem; }
}
@media (prefers-reduced-motion: reduce) {
  .tile-info { transition: none; }
}

/* ============================ 16. COMMENTS ============================= */
.thread { margin: .4rem 0 1rem; }
.comment { padding: .8rem 0; border-bottom: 1px solid var(--line); }
.comment:last-child { border-bottom: none; }
.comment-meta { display: flex; flex-wrap: wrap; gap: .3rem .6rem; align-items: baseline; font-size: .82rem; margin-bottom: .3rem; color: var(--muted); }
/* Where a comment was written, and the link back to it (the feed, Overview). */
.comment-from {
  display: inline-flex; align-items: baseline; gap: .35rem; max-width: 100%;
  padding: .05rem .55rem; border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  color: var(--ink-soft); text-decoration: none; overflow-wrap: anywhere;
}
a.comment-from:hover { border-color: var(--brass); color: var(--brass-deep); }
.comment-sku { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .04em; color: var(--brass-deep); }
/* The feed's narrowing: item and author, beside the page chips. */
.feed-filter { display: flex; flex-wrap: wrap; gap: .4rem .6rem; align-items: center; margin: 0 0 1rem; font-size: .88rem; }
.feed-filter select { width: auto; max-width: 100%; }
.comment-meta .who { color: var(--ink); font-weight: 600; }
.comment-meta > * { min-width: 0; }
.comment-body { white-space: pre-wrap; }
.comment-form textarea { margin-bottom: .5rem; }
.comment-actions { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin-top: .3rem; }
.comment-actions select { width: auto; min-width: 0; max-width: 100%; }
.comment-edited { font-style: italic; color: var(--muted); }
/* An admin's correction, folded away until it is wanted. */
.comment-edit { margin-top: .4rem; }
.comment-edit > summary {
  cursor: pointer; width: max-content; font-size: .78rem;
  color: var(--brass-deep); letter-spacing: .04em;
}
.comment-edit[open] > summary { margin-bottom: .4rem; }
.comment-delete > summary { color: var(--danger); }

/* Option sets (modules/options.php): "Option 2 of 3 · Vanity sconce" on a
   listed item, and the note on an item's page naming the other options. */
.option-tag { display: block; font-size: .75rem; color: var(--brass-deep); }
.option-note {
  border-left: 3px solid var(--brass); background: var(--surface-2);
  padding: .6rem .9rem; margin: .6rem 0 1rem; border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.option-note p { margin: 0 0 .3rem; }
.option-note ul { margin: 0; padding-left: 1.1rem; font-size: .9rem; }
.option-note.is-not-chosen { border-left-color: var(--line-strong); color: var(--muted); }

/* Accounts (views/admin-accounts.php, admin-account.php). */
.accounts-table tr.is-inactive td { color: var(--muted); }
.account-link { margin: .5rem 0 0; }
.account-link code { word-break: break-all; font-size: .82rem; }
.account-offlist { display: block; color: var(--danger); font-size: .85rem; }
.account-grant-add { margin-top: 1rem; align-items: flex-end; }
.account-grant-add .field { margin: 0; min-width: min(100%, 18rem); }

/* Reports and email (views/admin-reports.php, notifications.php). */
.usage-total { font-size: 1.1rem; margin: .4rem 0 1rem; }
.usage-total strong { font-size: 1.8rem; font-variant-numeric: tabular-nums; margin-right: .3rem; }
.usage-grid { display: grid; gap: 1rem 1.5rem; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
.usage-grid h3 { font-size: .8rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin: 0 0 .4rem; }
.usage-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.usage-table th.num { text-align: right; }
.reading-table tfoot th, .reading-table tfoot td { font-weight: 600; border-top: 1px solid var(--line); }
.report-chip { display: inline-flex; align-items: center; gap: .25rem; margin: 0 .4rem .3rem 0; }
.report-chip .btn { padding: .1rem .5rem; font-size: .8rem; }
.report-choices td, .report-choices th { text-align: center; }
.report-choices td:first-child, .report-choices th:first-child { text-align: left; }
.report-weekday { margin-top: 1rem; max-width: 22rem; }
.area-choices { display: flex; flex-wrap: wrap; gap: .3rem 1rem; margin: .3rem 0 .6rem; }

/* One project's permissions and areas on an account (views/admin-account.php):
   three sections, each with its title, its fields and a footer that pairs the
   hint with the button, so nothing lands in a stray grid cell. Areas are chips
   that show their state; removing the project sits apart, at the end. */
.grant-edit[open] { padding-bottom: .2rem; }
.grant-section { padding: .9rem 0 .2rem; border-top: 1px solid var(--line-soft); }
.grant-edit > summary + .grant-section { border-top: 0; padding-top: .3rem; }
.grant-title {
  margin: 0 0 .65rem; padding: 0; font-size: .74rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr)); gap: 0 1rem; }
.perm-grid .field { margin-bottom: .7rem; }
.grant-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .6rem 1rem; margin: .1rem 0 .6rem; }
.grant-foot .hint { margin: 0; flex: 1 1 18rem; }
.area-fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.area-fieldset .area-choices { gap: .5rem; margin: 0 0 .8rem; }
.area-chip {
  display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  padding: .38rem .8rem; border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--ink-soft); font-size: .88rem;
}
.area-chip input { margin: 0; accent-color: var(--brass-deep); }
.area-chip:hover { border-color: var(--brass); }
.area-chip:has(input:checked) { border-color: var(--brass); background: var(--ok-bg); color: var(--ink); }
.area-chip:has(input:focus-visible) { outline: 1px solid var(--brass); outline-offset: 2px; }
.grant-remove { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .6rem 1rem; padding-bottom: .4rem; }
.grant-remove .hint { margin: 0; flex: 1 1 18rem; }
.account-switch { margin-top: 1.4rem; padding-top: 1.1rem; border-top: 1px solid var(--line); }

/* Choices (views/options.php): the options for one place, side by side. */
.choice-cards {
  list-style: none; margin: .6rem 0 0; padding: 0;
  display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.choice-card {
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface-2);
}
.choice-card-image { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: contain; background: var(--surface); }
.choice-card-image.is-empty {
  display: flex; align-items: center; justify-content: center; aspect-ratio: 3 / 1;
  padding: 1rem; text-align: center; font-size: .85rem; color: var(--muted);
}
.choice-card-body { display: flex; flex-direction: column; gap: .35rem; padding: .8rem .9rem 1rem; flex: 1; }
.choice-card-n { margin: 0; font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--brass-deep); }
.choice-card h3 { margin: 0; font-family: var(--font-sans); font-size: 1.05rem; font-weight: 600; letter-spacing: 0; text-transform: none; }
.choice-card-facts { margin: 0; font-size: .85rem; display: grid; grid-template-columns: auto 1fr; gap: .15rem .7rem; }
.choice-card-facts > div { display: contents; }
.choice-card-facts dt { color: var(--muted); }
.choice-card-facts dd { margin: 0; }
.choice-card-link { margin: 0; font-size: .85rem; }
.choice-confirm { margin-top: auto; padding-top: .5rem; }
.choice-confirm > summary { list-style: none; width: max-content; }
.choice-confirm > summary::-webkit-details-marker { display: none; }
.choice-confirm form { margin-top: .6rem; }
.option-choose { margin-top: .5rem; align-items: flex-start; }
.option-choose .choice-confirm { margin: 0; padding: 0; }
.choice-done { margin: 0; padding-left: 1.1rem; }
.choice-done li { margin: .25rem 0; }
/* Choices of finish, paint, fabric or size (views/_choice_group.php):
   one row of swatches per type, the chosen one marked, the rest muted. */
.choice-group + .choice-group { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.choice-group-head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .8rem; }
.choice-group-head h3 { margin: 0; font-family: var(--font-sans); font-size: 1rem; font-weight: 600; letter-spacing: 0; text-transform: none; }
.choice-group-head .inline-form { margin-left: auto; }
.swatches {
  list-style: none; margin: .7rem 0 0; padding: 0;
  display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(min(140px, 100%), 1fr));
}
.swatch {
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface-2);
}
.swatch.is-chosen { border-color: var(--brass); box-shadow: 0 0 0 1px var(--brass); }
.swatch.is-not-chosen { opacity: .62; }
.swatch-img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: var(--surface); }
.swatch-img.is-empty {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-serif); font-size: 2rem; color: var(--muted);
}
.swatch-body { display: flex; flex-direction: column; gap: .2rem; padding: .6rem .7rem .8rem; flex: 1; }
.swatch-body .hint { margin: .2rem 0 0; }
.swatch-name { font-weight: 600; }
.swatch-code, .swatch-state { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.swatch-adj { font-variant-numeric: tabular-nums; color: var(--brass-deep); font-weight: 600; font-size: .9rem; }
.swatch-cost { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .8rem; }
.swatch > .swatch-link { font-size: .85rem; padding: 0 .7rem .7rem; }
/* Staff take a value off a choice (views/_choice_group.php): a quiet link at
   the foot of the tile that opens one confirmation. */
.swatch-remove { padding: 0 .7rem .7rem; font-size: .85rem; }
.swatch-remove > summary { list-style: none; cursor: pointer; color: var(--danger); display: inline-flex; align-items: center; min-height: 32px; }
.swatch-remove > summary::-webkit-details-marker { display: none; }
.swatch-remove > summary:hover { text-decoration: underline; }
.swatch-remove .hint { margin: .2rem 0 .4rem; }
.swatch-remove .btn { width: 100%; }
/* On an open choice the whole tile is the control (.swatch-pick): image, name
   and price open the one confirmation (.swatch-confirm). No script; the tap
   target is the tile, and never under 44px. */
.swatch-pick { display: flex; flex-direction: column; flex: 1; }
.swatch-pick > summary { list-style: none; cursor: pointer; display: flex; flex-direction: column; flex: 1; min-height: 44px; }
.swatch-pick > summary::-webkit-details-marker { display: none; }
.swatch-pick > summary:focus-visible { outline: 2px solid var(--brass-deep); outline-offset: -2px; border-radius: var(--r-card); }
.swatch-cta {
  display: flex; align-items: center; justify-content: center; min-height: 44px; margin: 0 .7rem .8rem; padding: .4rem .6rem;
  border: 1px solid var(--line-strong); border-radius: var(--r-pill); font-weight: 600; font-size: .9rem; text-align: center;
}
.swatch-pick > summary:hover .swatch-cta, .swatch-pick[open] .swatch-cta { border-color: var(--brass); color: var(--brass-deep); }
.swatch:has(.swatch-pick[open]) { border-color: var(--brass); box-shadow: 0 0 0 1px var(--brass); }
.swatch-confirm { padding: 0 .7rem .8rem; }
.swatch-confirm .hint { margin-top: 0; }
.swatch-pick .swatch-confirm .primary { width: 100%; }
/* The Options zone (views/item-detail.php): the product's images beside every
   option of the item, one group per type; on a phone the images first, the
   options directly under them, two tiles to a row. */
.options-layout { display: flex; gap: 1.6rem; align-items: flex-start; }
.options-layout > .spec-figure { position: sticky; top: 1rem; }
.options-zone { flex: 1; min-width: 0; }
.options-zone > .hint:first-child { margin-top: 0; }
/* The options an item is simply set to (views/_option_spec.php, 3 November
   2026): read as a specification, one row per option, the value at full width
   (a spec line can run to a paragraph), a small image only when one was
   captured, where it stands and the one action on the right. An option still
   to confirm carries a brass rule at its edge. */
.choice-group + .option-spec { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.option-spec-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: .6rem 1rem; }
.option-spec-head h3 { margin: 0; font-family: var(--font-sans); font-size: 1rem; font-weight: 600; letter-spacing: 0; text-transform: none; }
.option-spec-count { margin: .15rem 0 0; font-size: .85rem; color: var(--muted); }
.option-spec > .hint { margin: .5rem 0 0; }
.option-spec-list {
  list-style: none; margin: .8rem 0 0; padding: 0;
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface-2); overflow: hidden;
}
.option-spec-row {
  display: grid; grid-template-columns: 7.5rem minmax(0, 1fr) auto; gap: .35rem 1rem; align-items: start;
  padding: .85rem 1rem;
}
.option-spec-row + .option-spec-row { border-top: 1px solid var(--line-soft); }
.option-spec-row:not(.is-confirmed) { box-shadow: inset 3px 0 0 var(--brass); }
.option-spec-type {
  padding-top: .2rem; font-family: var(--font-mono); font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); overflow-wrap: anywhere;
}
.option-spec-main { display: flex; gap: .8rem; align-items: flex-start; min-width: 0; }
.option-spec-img {
  flex: 0 0 auto; width: 56px; height: 56px; object-fit: cover;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
}
.option-spec-text { min-width: 0; }
.option-spec-value { margin: 0; max-width: 68ch; line-height: 1.5; overflow-wrap: anywhere; }
.option-spec-meta { margin: .25rem 0 0; font-size: .8rem; color: var(--muted); }
.option-spec-text .hint { margin: .25rem 0 0; }
.option-spec-state { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: .5rem; }
.option-spec-state .inline-form { margin: 0; }
.option-spec-act { min-width: 6.5rem; }
/* An open spec check names where it is put right (views/item-detail.php). */
.spec-fix { margin-left: .35rem; font-size: .85rem; font-weight: 600; white-space: nowrap; }
/* Staff offer a choice (views/_choice_add.php): a row per value, started
   from what the product page offers where capture found it. */
.choice-add { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.choice-add > summary { list-style: none; width: max-content; }
.choice-add > summary::-webkit-details-marker { display: none; }
.choice-add-seeds { margin-top: .8rem; }
.choice-add-seeds ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.choice-add-seeds li { margin: .2rem 0; }
.choice-add-seeds a[aria-current] { font-weight: 600; }
.choice-add-form { margin-top: .8rem; }
.choice-add-form .field { max-width: 16rem; }
.choice-add-rows { width: 100%; border-collapse: collapse; font-size: .9rem; }
.choice-add-rows th { text-align: left; font-weight: 600; color: var(--muted); padding: 0 .4rem .3rem 0; white-space: nowrap; }
.choice-add-rows td { padding: 0 .4rem .4rem 0; }
.choice-add-rows input { width: 100%; min-width: 7rem; }
/* The Options page: one card per item with a choice still open. */
.item-choices + .item-choices { margin-top: 1.4rem; }
.item-choices-head { display: flex; align-items: center; gap: .8rem; margin-bottom: .6rem; }
.item-choices-head img { width: 56px; height: 56px; object-fit: contain; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
.item-choices-head h3 { margin: 0; font-family: var(--font-sans); font-size: 1.05rem; font-weight: 600; letter-spacing: 0; text-transform: none; }

/* Overview (views/dashboard.php): choices waiting, above everything else. */
.choices-due { border-left: 3px solid var(--brass); }
.choices-due ul { margin: .3rem 0 .8rem; padding-left: 1.1rem; }
/* The next step while options are open (modules/price_hold.php): leads the Overview and the Budget page. */
.next-step { background: var(--ok-bg); border-color: var(--ok-line); border-left-color: var(--brass); }
.next-step h2 { margin-top: .1rem; }

/* Add from links (views/items-paste.php): one row per pasted page. */
.paste-results { margin: .4rem 0 1.4rem; }
.paste-results .paste-url { word-break: break-all; font-size: .8rem; color: var(--muted); }
.paste-more { margin: .2rem 0 1rem; }
.paste-more > summary { cursor: pointer; width: max-content; font-size: .85rem; color: var(--brass-deep); margin-bottom: .6rem; }

/* Email alerts (views/notifications.php): one choice per line. */
.notify-form fieldset { border: 0; padding: 0; margin: 0 0 1rem; }
.notify-form legend { font-weight: 600; margin-bottom: .5rem; }
.notify-option { display: flex; gap: .6rem; align-items: baseline; padding: .35rem 0; cursor: pointer; }
.notify-option input { flex: 0 0 auto; }

/* Email in, the review queue (views/admin-inbound.php). The body is shown as
   it came, line breaks and all, and never as HTML. */
.inbound-body { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .7rem .85rem; margin: .5rem 0 .8rem; max-height: 22rem; overflow-y: auto; }
.inbound-summary { color: var(--ink-soft); }

/* Admin · Workbooks (views/admin-workbooks.php): one card per upkeep step, its tick
   and state on top, why, what it moves (totals, items, then each place with the
   formula now and after), the formulas shown as text and wrapped anywhere. */
.upkeep-targets { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .5rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.upkeep-targets li { border: 1px solid var(--line); border-radius: var(--r-sm); padding: .6rem .8rem; background: var(--surface-2);
  display: grid; gap: .1rem; }
.upkeep-list { display: grid; gap: 1rem; margin-bottom: 1rem; }
.upkeep-step { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card); padding: 1rem 1.15rem;
  box-shadow: var(--shadow-card); min-width: 0; }
.upkeep-step.is-done { opacity: .78; }
.upkeep-step.is-cannot { border-left: 3px solid var(--danger); }
.upkeep-step-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: .4rem 1rem; }
.upkeep-step-head h2, .upkeep-tick { margin: 0; font-size: 1.02rem; font-weight: 600; line-height: 1.35; }
.upkeep-tick { display: inline-flex; align-items: flex-start; gap: .55rem; cursor: pointer; }
.upkeep-tick input { margin-top: .25rem; accent-color: var(--brass-deep); }
.upkeep-step-badges { display: inline-flex; flex-wrap: wrap; gap: .35rem; }
.upkeep-note { margin: .4rem 0 0; font-size: .88rem; color: var(--ink-soft); }
.upkeep-totals { list-style: none; margin: .6rem 0 0; padding: 0; font-size: .92rem; display: grid; gap: .2rem; }
.upkeep-totals strong { font-variant-numeric: tabular-nums; }
.upkeep-more { margin-top: .6rem; border-top: 1px solid var(--line-soft); padding-top: .35rem; }
.upkeep-more > summary { cursor: pointer; color: var(--brass-deep); font-size: .88rem; padding: .2rem 0; }
.upkeep-items { list-style: none; margin: .3rem 0 0; padding: 0; display: grid; gap: .15rem; font-size: .88rem;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); font-variant-numeric: tabular-nums; }
.upkeep-cells { margin: .3rem 0 0; padding-left: 1.2rem; display: grid; gap: .6rem; font-size: .86rem; }
.upkeep-cells p { margin: .1rem 0; }
.upkeep-cells code { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; font-size: .8rem; }
.upkeep-where { font-weight: 600; }
.upkeep-label { display: inline-block; min-width: 3.2rem; font-family: var(--font-mono); font-size: .6rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); }
.upkeep-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.upkeep-actions .hint { margin: 0; }
.upkeep-all-done { margin: 0 0 1.4rem; }
@media (max-width: 600px) {
  .upkeep-step { padding: .85rem .9rem; }
}

/* Admin · Sent emails (views/admin-emails.php), one card per email, the same on
   a phone and a desk: what and when on top, what became of it as badges, each
   address it went to with whether it was opened, and the message and openings
   folded away. The words are shown as sent, never as HTML. */
.sent-figures { margin: 1rem 0 .3rem; }
.sent-figures-note { margin: 0 0 .8rem; }
.sent-kinds { list-style: none; margin: 0 0 .9rem; padding: .7rem 0 0; border-top: 1px solid var(--line-soft);
  display: grid; gap: .35rem; font-size: .9rem; }
.sent-kinds strong { font-weight: 600; margin-right: .4rem; }
.sent-filters > summary { cursor: pointer; font-family: var(--font-mono); font-size: .7rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--brass-deep); padding: .3rem 0; }
.sent-filters .filter-row { margin-top: .6rem; }
.sent-filters-clear { font-size: .85rem; align-self: center; }
.sent-count { margin: 0 0 .6rem; font-size: .85rem; }
.sent-list { display: grid; gap: 1rem; margin-bottom: 1.4rem; }
.sent-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 1rem 1.15rem; box-shadow: var(--shadow-card); min-width: 0; }
.sent-card.is-failed { border-left: 3px solid var(--danger); }
.sent-card-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: .2rem 1rem; }
.sent-card-title { min-width: 0; flex: 1 1 16rem; }
.sent-card-kind { margin: 0; font-size: .78rem; color: var(--muted); overflow-wrap: anywhere; }
.sent-card-head h2 { margin: .1rem 0 0; font-size: 1.05rem; line-height: 1.3; }
.sent-card-when { margin: 0; font-size: .82rem; color: var(--ink-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }
.sent-card-status { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: .65rem 0 .2rem; }
.sent-card-facts { display: grid; grid-template-columns: minmax(0, 2fr) repeat(auto-fit, minmax(9rem, 1fr));
  gap: .7rem 1.4rem; margin: .7rem 0 0; }
.sent-card-facts dt { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted); margin-bottom: .2rem; }
.sent-card-facts dd { margin: 0; font-size: .9rem; overflow-wrap: anywhere; }
.sent-recipients { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.sent-recipients li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 .6rem; }
.sent-addr { overflow-wrap: anywhere; }
.sent-addr-note { font-size: .8rem; color: var(--muted); }
.sent-addr-note.is-opened { color: var(--brass-deep); }
.sent-addr-note.is-bad { color: var(--danger); }
.sent-card-note { margin: .7rem 0 0; }
.sent-card-more { display: flex; flex-wrap: wrap; gap: .2rem 1.4rem; margin-top: .6rem; border-top: 1px solid var(--line-soft);
  padding-top: .4rem; }
.sent-card-more > details { flex: 1 1 100%; }
.sent-card-more > details:not([open]) { flex: 0 1 auto; }
.sent-message > summary, .sent-openings > summary { cursor: pointer; color: var(--brass-deep); font-size: .88rem; padding: .3rem 0; }
.sent-message-text { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .6rem .75rem; margin: .4rem 0 0; max-height: 22rem; overflow-y: auto; font-size: .88rem; }
.sent-openings { font-size: .85rem; }
.sent-openings ol { margin: .3rem 0 .2rem; padding-left: 1.1rem; color: var(--ink-soft); overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .sent-card { padding: .85rem .9rem; }
  .sent-card-facts { grid-template-columns: minmax(0, 1fr); gap: .6rem; }
  .sent-filters .filter-row label { flex: 1 1 100%; }
  .sent-filters .filter-row select { width: 100%; }
}
/* What each email contains (views/admin-report-contents.php): one person's report as it would be
   sent, its subject and counts on top and its words folded below; then a card per group with its
   reports, figures and parts; then term lists for the parts, each point to act on and the other
   emails. One column on a phone. */
.mail-guide-jump { list-style: none; margin: .8rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; font-size: .9rem; }
.mail-guide-pick { margin: .4rem 0 1rem; }
.mail-guide-pick .field { margin-bottom: 0; }
.mail-preview { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card); padding: 1rem 1.15rem;
  box-shadow: var(--shadow-card); min-width: 0; }
.mail-preview-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: .3rem 1rem; }
.mail-preview-head > div { min-width: 0; flex: 1 1 18rem; }
.mail-preview-to { margin: 0; font-size: .8rem; color: var(--muted); overflow-wrap: anywhere; }
.mail-preview-subject { margin: .15rem 0 0; font-size: 1.05rem; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.mail-preview-counts { display: flex; flex-wrap: wrap; gap: .6rem 2rem; margin: .8rem 0 .2rem; }
.mail-preview-counts > div { display: flex; flex-direction: column-reverse; }
.mail-preview-counts dt { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.mail-preview-counts dd { margin: 0; font-weight: 700; font-size: 1.4rem; font-variant-numeric: tabular-nums; }
.mail-preview-more { margin-top: .7rem; border-top: 1px solid var(--line-soft); padding-top: .4rem; }
.mail-preview-more > summary { cursor: pointer; color: var(--brass-deep); font-size: .88rem; padding: .3rem 0; }
.mail-preview-text { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .7rem .8rem; margin: .4rem 0 0; max-height: 34rem; overflow-y: auto; font-size: .85rem; }
/* Email previews (2026-11-04): pick an email and an account by chips, see the email itself framed beside what it is for. */
.mail-picker { display: grid; grid-template-columns: 6rem 1fr; gap: .6rem 1rem; align-items: start; margin: 0 0 .6rem; }
.mail-picker .chips { margin: 0; }
.mail-picker-label { margin: .45rem 0 0; font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); }
.mail-picker-groups { display: grid; gap: .45rem; }
/* One group a row: its name in a column of its own, its chips wrapping beside it, never under it (2026-11-05). */
.mail-picker-row { display: grid; grid-template-columns: 7.5rem minmax(0, 1fr); gap: .45rem 1rem; align-items: start; }
.mail-picker-group { padding-top: .45rem; font-size: .78rem; color: var(--muted); }
/* A pair the portal never sends (Email previews): very light grey; choosing it moves to a pair it does send. */
.chip.chip--off:not(.is-active) { color: var(--muted); background: var(--line-soft); border-color: var(--line-soft); opacity: .5; }
.mail-stage { display: grid; grid-template-columns: minmax(0, 1fr) 19rem; gap: 1.2rem; align-items: start; margin-top: 1rem; }
.mail-stage-main { min-width: 0; background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-card); padding: .8rem; }
.mail-envelope { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: .6rem .8rem; margin-bottom: .8rem; }
.mail-envelope .mail-preview-subject { font-size: .95rem; }
.mail-stage-frame { display: block; width: 100%; height: 72rem; max-height: 85vh; border: 0; border-radius: var(--r-sm); background: var(--bg); }
.mail-stage-side { position: sticky; top: 5rem; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 1rem 1.1rem; min-width: 0; }
.mail-stage-side h2 { margin: 0 0 .7rem; font-size: 1.05rem; }
.mail-stage-checks { list-style: none; margin: .9rem 0 0; padding: .8rem 0 0; border-top: 1px solid var(--line-soft); display: grid;
  gap: .35rem; font-size: .88rem; }
.mail-stage-checks li::before { content: "✓"; color: var(--brass-deep); margin-right: .5rem; }
@media (max-width: 900px) {
  .mail-stage { grid-template-columns: 1fr; }
  .mail-stage-side { position: static; }
  .mail-picker { grid-template-columns: 1fr; }
  .mail-picker-label { margin: .3rem 0 0; }
}
@media (max-width: 560px) {
  .mail-picker-row { grid-template-columns: 1fr; gap: .25rem; }
  .mail-picker-group { padding-top: 0; }
  .mail-picker-group:empty { display: none; }
}

.mail-guide-roles { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); margin: .6rem 0 1rem; }
.mail-guide-role { border: 1px solid var(--line); border-radius: var(--r-sm); padding: .8rem .9rem; background: var(--surface-2); min-width: 0; }
.mail-guide-role h3 { margin: 0 0 .4rem; font-size: 1rem; }
.mail-guide-facts { margin: 0; display: grid; gap: .35rem; font-size: .88rem; }
.mail-guide-facts dt, .mail-guide-label { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted); margin: 0; }
.mail-guide-facts dd { margin: 0; }
.mail-guide-label { margin-top: .6rem; }
.mail-guide-parts { margin: .2rem 0 0; padding-left: 1.1rem; font-size: .88rem; display: grid; gap: .1rem; }
.mail-guide-list { margin: .4rem 0 0; display: grid; gap: .7rem; }
.mail-guide-list > div { border-top: 1px solid var(--line-soft); padding-top: .6rem; scroll-margin-top: 5rem; }
.mail-guide-list dt { font-weight: 600; margin-bottom: .15rem; }
.mail-guide-list dd { margin: 0; color: var(--ink-soft); font-size: .92rem; }
@media (max-width: 600px) {
  .mail-preview { padding: .85rem .9rem; }
  .mail-guide-pick .field, .mail-guide-pick select { width: 100%; }
}

/* Allowances (views/allowances.php): one card per allowance, its name and who it is from
   on top, taxed or not as a badge, the figures in a row (two to a row on a phone); the price
   summary beside the portal, one row each, a row that differs marked in brass. */
.allowance-figures { margin: 1rem 0 .3rem; }
.allowance-list { display: grid; gap: 1rem; margin-bottom: 1.4rem; }
.allowance-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 1rem 1.15rem; box-shadow: var(--shadow-card); min-width: 0; scroll-margin-top: 5rem; }
.allowance-card-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: .3rem 1rem; }
.allowance-card-title { min-width: 0; flex: 1 1 16rem; }
.allowance-card-head h2 { margin: 0; font-size: 1.05rem; line-height: 1.3; overflow-wrap: anywhere; }
.allowance-card-from { margin: .15rem 0 0; font-size: .85rem; color: var(--ink-soft); overflow-wrap: anywhere; }
.allowance-card-figures, .allowance-compare-figures { display: flex; flex-wrap: wrap; gap: .6rem 2rem; margin: .7rem 0 0; }
.allowance-card-figures dt, .allowance-compare-figures dt { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin-bottom: .15rem; }
.allowance-card-figures dd { margin: 0; font-weight: 600; font-size: 1.1rem; font-variant-numeric: tabular-nums; }
.allowance-card-notes { margin: .6rem 0 0; font-size: .88rem; color: var(--ink-soft); white-space: pre-wrap; overflow-wrap: anywhere; }
.allowance-card-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .5rem 1rem; margin-top: .8rem;
  border-top: 1px solid var(--line-soft); padding-top: .6rem; }
.allowance-remove > summary { cursor: pointer; color: var(--danger); font-size: .88rem; padding: .45rem 0; }
.allowance-remove form { margin: .4rem 0 0; }
.allowance-card .allowance-form { margin-top: .8rem; border-top: 1px solid var(--line-soft); padding-top: .9rem; }
/* The form: the name and the notes across, who / price / cost / tax four to a row on a desk, two on a tablet,
   one on a phone; each hint under its own box. */
.allowance-form-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0 1.1rem; align-items: start; }
.allowance-form-name { grid-column: 1 / -1; }
.allowance-form .field .hint { margin: .3rem 0 0; font-size: .8rem; }
.allowance-form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.2rem; margin-top: .2rem; }
.allowance-tick { display: inline-flex; align-items: flex-start; gap: .55rem; cursor: pointer; }
.allowance-tick input { margin-top: .25rem; accent-color: var(--brass-deep); }
@media (max-width: 900px) { .allowance-form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .allowance-form-grid { grid-template-columns: minmax(0, 1fr); } }
.allowance-bring { margin: .6rem 0 1rem; }
.allowance-compare { list-style: none; margin: .6rem 0 1rem; padding: 0; display: grid; gap: .6rem; }
.allowance-compare-row { border: 1px solid var(--line); border-radius: var(--r-sm); padding: .7rem .85rem; background: var(--surface-2); min-width: 0; }
.allowance-compare-row.is-differs { border-left: 3px solid var(--brass); }
.allowance-compare-row.is-only-portal { border-left: 3px solid var(--danger); }
.allowance-compare-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: .3rem 1rem; }
.allowance-compare-head strong { overflow-wrap: anywhere; }
.allowance-compare-figures { margin-top: .45rem; }
.allowance-compare-figures dd { margin: 0; font-size: .9rem; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.allowance-compare-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.allowance-compare-actions .hint { margin: 0; }
.allowance-history { margin: 0; padding-left: 1.1rem; display: grid; gap: .45rem; font-size: .9rem; }
.allowance-history li > * { margin-right: .4rem; }
@media (max-width: 600px) {
  .allowance-card { padding: .85rem .9rem; }
  .allowance-card-figures { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .6rem .8rem; }
  .allowance-card-figures dd { font-size: 1rem; }
  .allowance-compare-figures { display: grid; grid-template-columns: minmax(0, 1fr); gap: .4rem; }
}
.inbound-actions { display: flex; flex-wrap: wrap; gap: .6rem 1rem; align-items: center; }
.inbound-actions form { margin: 0; }
/* A comment that arrived as an email reply. */
.comment-via { font-style: italic; color: var(--muted); }

/* ---- the timeline (views/_timeline.php) -----------------------------
 * One rule down the left, a dot per event. A comment is a communication
 * event and is marked: brass dot, a label, so talk reads apart from the
 * things that merely happened. */
.timeline { list-style: none; margin: .4rem 0 1rem; padding: 0; }
.timeline-day h3 { font-size: .8rem; font-family: var(--font-mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin: 1.1rem 0 .4rem; }
.timeline-day:first-child h3 { margin-top: .2rem; }
.timeline-event {
  position: relative; display: grid; grid-template-columns: 4.6rem minmax(0, 1fr); gap: .2rem .8rem;
  padding: .45rem 0 .45rem 1rem; border-left: 1px solid var(--line-strong); margin-left: .35rem;
}
.timeline-event::before {
  content: ''; position: absolute; left: -.35rem; top: .85rem; width: .6rem; height: .6rem;
  border-radius: 50%; background: var(--surface); border: 2px solid var(--line-strong);
}
.timeline-event.is-communication::before { background: var(--brass); border-color: var(--brass-deep); }
.timeline-time { font-size: .8rem; color: var(--muted); font-variant-numeric: tabular-nums; padding-top: .1rem; }
.timeline-body { min-width: 0; }
.timeline-body p { margin: 0 0 .2rem; }
.timeline-title { font-weight: 600; overflow-wrap: anywhere; }
.timeline-marker {
  display: inline-block; margin-right: .4rem; padding: .02rem .45rem; border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .1em; text-transform: uppercase;
  background: var(--ok-bg); color: var(--brass-deep); border: 1px solid var(--brass); vertical-align: .1em;
}
.timeline-detail { color: var(--ink-soft); white-space: pre-wrap; overflow-wrap: anywhere; }
.timeline-who { font-size: .8rem; color: var(--muted); }
.timeline-pages { display: flex; flex-wrap: wrap; gap: .8rem; align-items: baseline; margin-top: .6rem; }
@media (max-width: 520px) {
  .timeline-event { grid-template-columns: minmax(0, 1fr); }
}

/* ============================ 17. RESPONSIVE =========================== */
@media (max-width: 860px) {
  .topbar { gap: .7rem .9rem; padding: .7rem 1rem; }
  .topbar input[type="search"] { min-width: 10rem; }
  .brand .brand-tag { display: none; }
  main { margin: 1.4rem auto; padding: 0 1rem; }
  .panel { padding: 1.2rem 1.15rem; }
  .product-detail { flex-direction: column; }
  /* Stacked, the image is decoration above a table nobody has read yet, so it
     goes second — and stops being a fixed 280px in a 320px viewport. */
  .spec-layout { flex-direction: column-reverse; }
  .spec-figure { width: 100%; max-width: 320px; }
  /* Beside the options the images are what they are checked against: first. */
  .options-layout { flex-direction: column; }
  .options-layout > .spec-figure { position: static; }
}
/* On a phone each option reads top to bottom: its type, the value, then where
   it stands and its action on one line. */
@media (max-width: 640px) {
  .option-spec-row { grid-template-columns: minmax(0, 1fr); padding: .8rem .9rem; }
  .option-spec-type.is-repeat { display: none; }
  .option-spec-state { justify-content: space-between; }
}
@media (max-width: 520px) {
  .swatches { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
}

/* ============================ 18. MOTION / PRINT ======================= */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}
@media print {
  .topbar, .site-footer, .noprint, form, .report-links { display: none !important; }
  body { background: #fff; color: #000; }
  main { margin: 0; max-width: none; }
  .panel { border: none; box-shadow: none; padding: 0; margin: 0 0 1rem; background: #fff; }
  a { color: inherit; }
  tbody tr:hover { background: none; }
}

/* An order-level charge: real money on the document, belonging to no item. */
.order-level > td { background: rgba(184,145,106,.06); }

/* ---- the project bar --------------------------------------------------
 *
 * A second row under the topbar, present on every page that belongs to a
 * project. Before it, each of those pages was reachable only from a single
 * paragraph on the dashboard and led back only to the dashboard — so moving
 * between two of them meant passing through a third you did not want.
 *
 * Elevated chrome rather than the page ground: it belongs to the frame, not
 * to the content, and reading as part of the header is what makes it feel
 * like a place you are in rather than a widget on the page.
 */
.projectbar {
  background: var(--chrome-2); color: var(--chrome-ink);
  display: flex; align-items: center; gap: 1rem;
  padding: 0 1.5rem;
  border-bottom: 1px solid var(--line-dark);
  overflow-x: auto;                 /* narrow screens scroll the row, not the page */
  scrollbar-width: thin;
}
.projectbar-code {
  flex: 0 0 auto;
  font-family: var(--font-mono); font-size: .68rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--brass-hi);
}
.projectbar-links { display: flex; align-items: stretch; gap: .2rem; }
.projectbar a {
  flex: 0 0 auto;
  padding: .68rem .7rem;
  color: var(--chrome-dim); text-decoration: none; white-space: nowrap;
  font-size: .85rem;
  border-bottom: 2px solid transparent;
}
.projectbar a:hover { color: var(--chrome-ink); }
/* The current page is stated twice — colour and a rule — so it survives being
   read without colour, and aria-current says it a third time for a reader. */
.projectbar a.is-current {
  color: var(--chrome-ink);
  border-bottom-color: var(--brass);
}
.projectbar a:focus-visible {
  outline: 2px solid var(--brass-hi); outline-offset: -3px;
}

/*
 * A tab with sub-pages: its link, then a small caret that opens a menu. Wide
 * screens drop the bar's horizontal scroll so the menu can hang below it; on
 * narrow ones the bar still scrolls, and the menu opens inline in the row
 * instead of being clipped by it.
 */
.projectbar-group { position: relative; display: flex; align-items: stretch; flex: 0 0 auto; }
.projectbar-group > a { padding-right: .25rem; }
.projectbar-menu { display: flex; align-items: stretch; }
.projectbar-menu > summary {
  list-style: none; cursor: pointer;
  padding: .68rem .45rem .68rem .2rem;
  color: var(--chrome-dim); font-size: .7rem;
  border-bottom: 2px solid transparent;
}
.projectbar-menu > summary::-webkit-details-marker { display: none; }
.projectbar-menu > summary:hover, .projectbar-menu[open] > summary { color: var(--chrome-ink); }
.projectbar-menu > summary:focus-visible { outline: 2px solid var(--brass-hi); outline-offset: -3px; }
.projectbar-pop {
  position: absolute; top: 100%; left: 0; z-index: 60;
  min-width: 15rem; padding: .3rem 0;
  display: flex; flex-direction: column;
  background: var(--chrome-2);
  border: 1px solid var(--line-dark); border-top: 0;
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  box-shadow: var(--shadow-pop);
}
.projectbar .projectbar-pop a {
  padding: .55rem .95rem;
  border-bottom: 0; border-left: 2px solid transparent;
}
.projectbar .projectbar-pop a.is-current { border-left-color: var(--brass); }
/* More: the pages opened less often, as one menu at the end of the row. Its
   summary reads like a tab, and like the current tab while one of them is open. */
.projectbar-more { position: relative; flex: 0 0 auto; }
.projectbar-more > summary { padding: .68rem .7rem; font-size: .85rem; white-space: nowrap; }
.projectbar-more > summary.is-current { color: var(--chrome-ink); border-bottom-color: var(--brass); }
@media (min-width: 900px) {
  .projectbar { overflow: visible; }
}
@media (max-width: 899px) {
  .projectbar-pop {
    position: static; flex-direction: row; min-width: 0; padding: 0;
    background: transparent; border: 0; box-shadow: none;
  }
  /* With script (app.js marks the bar .is-js) a menu drops as a panel under
     the tab tapped, fixed to the window so the scrolling row cannot clip it.
     Inline in the row, More - the last tab - opened off the right edge and
     looked like it did nothing (Jeff, 19 October). The inline row above stays
     as the fallback without script. */
  .projectbar.is-js .projectbar-pop {
    position: fixed; left: .75rem; right: .75rem; top: var(--pop-top, 6rem); z-index: 60;
    flex-direction: column; padding: .3rem 0;
    /* A short screen (a phone turned sideways): stop at the bottom of the
       window and scroll inside, since a fixed panel cannot be scrolled to and
       a page scroll closes it (Codex on #174). */
    max-height: calc(100vh - var(--pop-top, 6rem) - .75rem);
    max-height: calc(100dvh - var(--pop-top, 6rem) - .75rem);
    overflow-y: auto; overscroll-behavior: contain;
    background: var(--chrome-2);
    border: 1px solid var(--line-dark); border-top: 0;
    border-radius: 0 0 var(--r-sm) var(--r-sm);
    box-shadow: var(--shadow-pop);
  }
  .projectbar.is-js .projectbar-pop a { padding: .7rem .95rem; }
}

/* ---- breadcrumbs ---- */
.crumbs {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem;
  font-size: .85rem; margin: 0 0 .9rem;
}
.crumbs a { color: var(--brass-deep); }
.crumbs [aria-current="page"] { color: var(--muted); }
.crumb-sep { color: var(--line-strong); }

@media (max-width: 560px) {
  .projectbar { padding: 0 1rem; gap: .6rem; }
  .projectbar a { padding: .6rem .5rem; font-size: .8rem; }
}

@media print {
  .projectbar { display: none; }
}

/* ---- the breadcrumb bar ----------------------------------------------
 *
 * Between the project bar and the page: where you are, and every step back
 * as a link. Drawn by the chrome for every page rather than by each view,
 * because a view that renders its own trail is a view that can be added
 * without one.
 */
.crumbbar {
  padding: .55rem 1.5rem .1rem;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg);
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .3rem 1rem;
}
.crumbbar .crumbs { margin: 0; }
/* The page's own entry in the manual, at the far end of the trail. */
.crumbbar-help { margin-left: auto; font-size: .8rem; color: var(--brass-deep); text-decoration: none; white-space: nowrap; }
.crumbbar-help:hover { text-decoration: underline; }
/* The (i): a lower-case i in a ring, the same on every page. */
.help-i {
  display: inline-grid; place-items: center; width: 1.2rem; height: 1.2rem; margin-right: .25rem;
  border: 1.5px solid var(--brass); border-radius: var(--r-pill);
  font-family: var(--font-serif); font-style: italic; font-size: .78rem; font-weight: 700; line-height: 1;
}

@media (max-width: 560px) {
  .crumbbar { padding: .5rem 1rem .1rem; }
}

/*
 * THE MANUAL BESIDE THE PAGE (29 October 2026, app.js). On a wide screen
 * "Help with this page" opens its section in this panel on the right; the
 * page makes room for it rather than sitting under it. The tab on the right
 * edge, in the middle of the screen wherever the page is scrolled, opens and
 * closes it. Below 75em neither is shown and the link opens the manual.
 */
:root { --help-panel-w: clamp(20rem, 28vw, 27rem); }
body.help-panel-open { padding-right: var(--help-panel-w); }
.help-panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 900; width: var(--help-panel-w);
  display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--line); box-shadow: var(--shadow-pop);
}
.help-panel[hidden] { display: none; }
.help-panel-bar {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .55rem .6rem .55rem 1rem; border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-mono); font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
}
.help-panel-close {
  background: transparent; border: 0; border-radius: var(--r-pill); cursor: pointer;
  font-size: 1.5rem; line-height: 1; padding: .1rem .5rem; color: var(--ink-soft);
}
.help-panel-close:hover { color: var(--brass-deep); background: var(--ok-bg); }
.help-panel-content { position: relative; flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 1rem 1.2rem 2.5rem; }
.help-panel-content:focus { outline: none; }
.help-panel-head h2 { margin: .15rem 0 .35rem; font-size: 1.45rem; }
.help-panel-links { margin: 0 0 1rem; font-size: .8rem; }
.help-panel-body h2 { font-size: 1.15rem; }
.help-panel-list { list-style: none; margin: 0; padding: 0; }
.help-panel-list li { padding: .6rem 0; border-bottom: 1px solid var(--line-soft); }
.help-panel-list .hint { display: block; margin: .15rem 0 0; }
.help-tab {
  position: fixed; top: 50%; right: 0; z-index: 901; transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: center; gap: .45rem;
  padding: .75rem .45rem; border: 0; border-radius: var(--r-sm) 0 0 var(--r-sm); cursor: pointer;
  background: var(--chrome); color: var(--chrome-ink); box-shadow: var(--shadow-card);
}
.help-tab:hover, .help-tab:focus-visible { background: var(--chrome-2); }
.help-tab .help-i { margin: 0; border-color: var(--brass-hi); color: var(--brass-hi); }
.help-tab-word {
  writing-mode: vertical-rl; transform: rotate(180deg);
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
}
.help-tab[hidden] { display: none; }
body.help-panel-open .help-tab { right: var(--help-panel-w); }
/* /styleguide's still copies. */
.help-panel--demo { position: relative; z-index: auto; height: 15rem; width: 100%; max-width: 27rem; box-shadow: var(--shadow-card); }
.help-tab--demo { position: static; transform: none; }

@media print {
  .crumbbar, .help-panel, .help-tab { display: none; }
  body.help-panel-open { padding-right: 0; }
}

/* ---- the daily briefing ----------------------------------------------
 *
 * Directly under the project header: has anything moved, what is it worth,
 * what is waiting on somebody. Read in the first ten seconds or not at all,
 * so it is a scan — counts large, prose small, and the one thing that needs
 * a person marked in a colour nothing else on the page uses.
 */
.summary { border-left: 3px solid var(--brass); }

/* The stale warning reuses the schedule's alert row (.sched-alerts); this
   only sets its place under the heading. */
.summary .summary-stale { margin: .9rem 0 0; }

/* What moved and what it is worth, side by side: the schedule's cards. */
.brief-cards {
  display: grid; gap: .8rem; margin: 1.1rem 0 0; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.brief-card {
  padding: .9rem 1.05rem 1rem; border: 1px solid var(--line);
  border-top: 3px solid var(--brass); border-radius: var(--r-sm); background: var(--surface-2);
}
.brief-card > .gate-tag { margin: 0; }
.brief-card .sched-rows { margin-top: .5rem; }
/* The item ids under a move: which ones, so the count can be checked. */
.brief-ids { display: block; font-size: .76rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.brief-empty { margin: .55rem 0 0; font-size: .88rem; }
.brief-stats { display: flex; flex-wrap: wrap; gap: .9rem 2rem; margin: .6rem 0 0; }
.brief-stat { display: flex; flex-direction: column; gap: .15rem; }
.brief-stat-label {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}
.brief-stat-n { font-size: 1.25rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.2; }

/* Stated rather than left blank: "nothing is waiting" is the answer people
   come here for, and a block that disappears when all is well reads as broken. */
.summary-clear { margin: 1.1rem 0 0; color: var(--ok); font-size: .92rem; font-weight: 600; }

/* One card per gate, the chip answering "anything here?" before a word is
   read: terracotta edge when something is found, brass when all is clear. */
.brief-gates {
  list-style: none; margin: 1.1rem 0 0; padding: 0; display: grid; gap: .8rem; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.summary-clear + .brief-gates { margin-top: .6rem; }
.brief-gate {
  padding: .9rem 1.05rem 1rem; border: 1px solid var(--line);
  border-top: 3px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface-2);
}
.brief-gate.is-found { border-top-color: var(--danger); }
.brief-gate.is-clear { border-top-color: var(--ok); background: transparent; }
.brief-gate .sched-rows { margin-top: .55rem; }
/* A gate's full name is long: it wraps beside the chip rather than pushing
   the chip out of the card. */
.brief-gate .sched-top { align-items: flex-start; }
.brief-gate .gate-tag { white-space: normal; min-width: 0; line-height: 1.6; }
/* Where a finding's row goes, said under its words. */
.brief-go { display: block; margin-top: .1rem; font-size: .76rem; color: var(--brass-deep); }
.brief-checks-head {
  margin: .75rem 0 0; font-size: .76rem; color: var(--muted);
}
.brief-gate.is-found .brief-checks-head { padding-top: .6rem; border-top: 1px solid var(--line-soft); }
.brief-checks { list-style: none; margin: .3rem 0 0; padding: 0; display: grid; gap: .25rem; }
.brief-checks li { display: grid; grid-template-columns: auto 1fr; gap: .55rem; align-items: baseline; font-size: .84rem; color: var(--ink-soft); }
/* A check mark drawn from borders, in the "done" colour. */
.brief-check {
  width: .32rem; height: .6rem; margin: 0 .14rem 0 .1rem;
  border: solid var(--ok); border-width: 0 2px 2px 0; transform: translateY(-.05rem) rotate(45deg);
}

/* ---- the four gates ---------------------------------------------------
 *
 * The public site promises every client four gates — Basecamp, Route, Ascent,
 * Summit — and that nothing advances until its gate clears. This strip is the
 * same four, in the same words, with the project's lines counted into them.
 * One column per gate in order, so progress reads left to right; it folds to
 * two columns and then one rather than scrolling sideways.
 */
.gate-strip { border-left: 3px solid var(--brass); }
.gate-row {
  list-style: none; margin: .4rem 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .9rem;
}
@media (max-width: 56rem) { .gate-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 30rem) { .gate-row { grid-template-columns: 1fr; } }
.gate-cell {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); padding: .75rem .85rem .8rem;
  display: flex; flex-direction: column; gap: .2rem; min-width: 0;
}
.gate-cell.is-current { border-color: var(--brass); box-shadow: inset 0 3px 0 var(--brass); }
/* Each gate's colour along its top (--viz-g1 to g4: the same four as the report, My projects and the emails). */
.gate-cell.gate-g1 { box-shadow: inset 0 3px 0 var(--viz-g1); }
.gate-cell.gate-g2 { box-shadow: inset 0 3px 0 var(--viz-g2); }
.gate-cell.gate-g3 { box-shadow: inset 0 3px 0 var(--viz-g3); }
.gate-cell.gate-g4 { box-shadow: inset 0 3px 0 var(--viz-g4); }
.gate-cell.is-current.gate-g1 { box-shadow: inset 0 5px 0 var(--viz-g1); }
.gate-cell.is-current.gate-g2 { box-shadow: inset 0 5px 0 var(--viz-g2); }
.gate-cell.is-current.gate-g3 { box-shadow: inset 0 5px 0 var(--viz-g3); }
.gate-cell.is-current.gate-g4 { box-shadow: inset 0 5px 0 var(--viz-g4); }
.gate-g1 .gate-meter::-webkit-progress-value { background: var(--viz-g1); }
.gate-g2 .gate-meter::-webkit-progress-value { background: var(--viz-g2); }
.gate-g3 .gate-meter::-webkit-progress-value { background: var(--viz-g3); }
.gate-g4 .gate-meter::-webkit-progress-value { background: var(--viz-g4); }
.gate-g1 .gate-meter::-moz-progress-bar { background: var(--viz-g1); }
.gate-g2 .gate-meter::-moz-progress-bar { background: var(--viz-g2); }
.gate-g3 .gate-meter::-moz-progress-bar { background: var(--viz-g3); }
.gate-g4 .gate-meter::-moz-progress-bar { background: var(--viz-g4); }
.gate-top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: .3rem .5rem; min-height: 1.4rem; }
.gate-top .gate-code { white-space: nowrap; }
.gate-code {
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--brass-deep);
}
.gate-title { font-family: var(--font-serif); font-size: 1.15rem; line-height: 1.2; color: var(--ink); }
.gate-n { margin: .35rem 0 0; font-size: .9rem; color: var(--ink-soft); }
.gate-n strong {
  font-size: 1.6rem; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1.1; margin-right: .2rem;
}
/* How much of the project has cleared this gate: one series, so one colour,
   no legend; the sentence under it says the same thing in words. */
.gate-meter {
  -webkit-appearance: none; appearance: none; display: block;
  width: 100%; height: 6px; margin: .45rem 0 .2rem;
  border: 0; border-radius: var(--r-pill); background: var(--line); overflow: hidden;
}
.gate-meter::-webkit-progress-bar { background: var(--line); border-radius: var(--r-pill); }
.gate-meter::-webkit-progress-value { background: var(--brass-deep); border-radius: var(--r-pill); }
.gate-meter::-moz-progress-bar { background: var(--brass-deep); border-radius: var(--r-pill); }
.gate-through { margin: 0; font-size: .82rem; color: var(--ok); font-variant-numeric: tabular-nums; }
/* Where the items at this gate are, count first. The counts share one column,
   so they line up down the card and read as a list of numbers at a glance. */
.gate-steps {
  list-style: none; margin: .6rem 0 0; padding: .45rem 0 0;
  border-top: 1px solid var(--line-soft); display: grid; gap: .35rem;
}
.gate-steps li {
  display: grid; grid-template-columns: minmax(2.2rem, auto) 1fr;
  align-items: center; gap: .55rem;
}
.gate-step-n {
  justify-self: start; min-width: 2.2rem; padding: .1rem .45rem;
  border-radius: var(--r-pill); background: var(--ok-bg); color: var(--ink);
  font-size: .95rem; font-weight: 600; text-align: center; line-height: 1.35;
  font-variant-numeric: tabular-nums;
}
.gate-step-label { font-size: .86rem; color: var(--ink-soft); line-height: 1.3; }
/* Each count opens the items it counts (views/_gates.php). The whole row is
   the target, and the chevron says so where there is no hover to. */
.gate-step-link {
  grid-column: 1 / -1; display: grid; grid-template-columns: minmax(2.2rem, auto) 1fr auto;
  align-items: center; gap: .55rem; margin: 0 -.4rem; padding: .2rem .4rem;
  border-radius: var(--r-sm); color: inherit;
}
.gate-n-link { color: inherit; border-radius: var(--r-sm); }
.gate-go { color: var(--brass-deep); font-size: 1.1rem; line-height: 1; transition: transform .18s ease; }
.gate-n-link .gate-go { margin-left: .35rem; }
.gate-step-link:hover, .gate-step-link:focus-visible { background: var(--line-soft); color: inherit; }
.gate-step-link:hover .gate-step-label, .gate-n-link:hover { color: var(--ink); }
.gate-step-link:hover .gate-go, .gate-n-link:hover .gate-go { transform: translateX(2px); }
.gate-step-link:focus-visible, .gate-n-link:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
/* A step no item is at yet: kept in the sequence, quiet, so the next step is visible (Jeff, 5 November 2026). */
.gate-steps li.is-empty .gate-step-n { background: transparent; color: var(--muted); font-weight: 400; box-shadow: inset 0 0 0 1px var(--line); }
.gate-steps li.is-empty .gate-step-label { color: var(--muted); }
/* The step's date (GATE_STEP_DATES): under its words, small, figures aligned. */
.gate-step-date { display: block; font-size: .74rem; color: var(--brass-deep); font-variant-numeric: tabular-nums; }
.gate-note { font-size: .78rem; color: var(--muted); margin: .4rem 0 0; }
.gate-outside { margin: .8rem 0 0; font-size: .85rem; color: var(--muted); }
/* A finding or a schedule question is tagged with the gate it belongs to. */
.gate-tag {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--brass-deep); white-space: nowrap;
}
.summary-group { margin: 1rem 0 0; padding-top: .8rem; border-top: 1px solid var(--line); }
.summary-group .summary-attention { margin-top: .35rem; padding-top: 0; border-top: 0; }
/* The raw workbook stage, shown to people with cost rights under the gate step. */
.stage-raw { display: block; font-size: .7rem; color: var(--muted); margin-top: .15rem; }

/* ============================ 20. THE SCHEDULE ========================= */
/*
 * Five questions with five different KINDS of answer, so the type has to
 * separate them. A QuickBooks date and a date projected from a scraped lead
 * time are not the same claim; a question nothing in the system can answer is
 * a third thing again. The chip carries that distinction and the left edge
 * repeats it, so the state survives a glance down the column.
 */
/* Alerts: one action row each, the thing to do at the right. */
.schedule .sched-alerts, .summary .sched-alerts,
/* Admin · Sync: Update now and Refresh from QuickBooks, above what they fix. */
.sync-actions { margin: .9rem 0 .2rem; }
.sync-note .sched-alerts, .sync-detail .sched-alerts {
  list-style: none; margin: .9rem 0 0; padding: 0;
  border: 1px solid var(--danger-line); border-radius: var(--r-sm); background: var(--danger-bg);
}
.sched-alerts li {
  display: grid; grid-template-columns: auto 1fr auto; gap: .75rem; align-items: center;
  padding: .65rem .9rem; font-size: .9rem; color: var(--ink);
}
.sched-alerts li + li { border-top: 1px solid var(--danger-line); }
.sched-alert-mark {
  display: inline-grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%;
  background: var(--danger); color: var(--surface-2); font-weight: 700; font-size: .8rem;
}
.sched-alert-open { white-space: nowrap; font-size: .8rem; padding: .3rem .8rem; }

/* The five questions as cards: two across on a wide page, one on a phone. */
.sched-list {
  list-style: none; margin: 1.1rem 0 0; padding: 0; display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); align-items: start;
}
.sched-item {
  display: flex; flex-direction: column;
  padding: .9rem 1.05rem 1rem; border: 1px solid var(--line);
  border-top: 3px solid var(--line-strong);
  border-radius: var(--r-sm); background: var(--surface-2);
}
.sched-item.is-done     { border-top-color: var(--ok); }
.sched-item.is-partial  { border-top-color: var(--brass); }
.sched-item.is-estimated{ border-top-color: var(--brass-hi); }
.sched-item.is-expected { border-top-color: var(--brass-hi); }
.sched-item.is-planned  { border-top-color: var(--brass-hi); }
.sched-item.is-overdue  { border-top-color: var(--danger); }
/* Untracked is deliberately the quietest: it is a gap in the record, not an
   emergency, and it should not shout down the card beside it that is overdue. */
.sched-item.is-untracked { background: transparent; border-top-color: var(--line); }

.sched-top { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.sched-q { margin: .55rem 0 0; font-size: .8rem; color: var(--muted); }
.sched-chip {
  flex: 0 0 auto; font-family: var(--font-mono); font-size: .56rem;
  letter-spacing: .12em; text-transform: uppercase;
  padding: .2rem .5rem; border-radius: var(--r-pill);
  border: 1px solid var(--line); color: var(--muted);
}
.sched-chip.chip-done     { color: var(--ok); border-color: var(--ok-line); background: var(--ok-bg); }
.sched-chip.chip-overdue  { color: var(--danger); border-color: var(--danger-line); background: var(--danger-bg); }
.sched-chip.chip-due      { color: var(--brass-deep); border-color: var(--brass); background: var(--surface-2); }

/* Project targets (views/_targets.php): one row per agreed date. */
.target-list { list-style: none; margin: .4rem 0 0; padding: 0; display: grid; gap: .1rem; }
.target {
  display: grid; grid-template-columns: 7.5rem 1fr auto; gap: .4rem 1rem; align-items: baseline;
  padding: .55rem 0; border-top: 1px solid var(--line);
}
.target:first-child { border-top: 0; }
.target-when { font-family: var(--font-mono); font-size: .8rem; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.target-what { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.target-what .muted { font-size: .85rem; }
@media (max-width: 560px) {
  .target { grid-template-columns: 1fr auto; }
  .target-when { grid-column: 1 / -1; }
}

.sched-a { margin: .15rem 0 0; font-size: 1.12rem; font-weight: 600; line-height: 1.3; }
.sched-item.is-untracked .sched-a { color: var(--muted); font-weight: 500; }
.sched-window {
  margin: .25rem 0 0; font-family: var(--font-mono); font-size: .78rem;
  font-variant-numeric: tabular-nums; color: var(--ink-soft);
}

/* A breakdown: a bar to scale, then a row per part with its count. Each row
   opens exactly the items it counts (/items?find=). The bar is SVG so its
   widths are attributes, and the tones are the brand tokens. */
.sched-breakdown { margin: .8rem 0 0; }
.sched-bar { display: block; width: 100%; height: .5rem; border-radius: var(--r-pill); overflow: hidden; background: var(--line-soft); }
.sched-bar .seg.tone-ok   { fill: var(--ok); }
.sched-bar .seg.tone-warm { fill: var(--brass); }
.sched-bar .seg.tone-pale { fill: var(--brass-hi); }
.sched-bar .seg.tone-late { fill: var(--danger); }
.sched-bar .seg.tone-none { fill: var(--line-strong); }
.sched-rows { list-style: none; margin: .45rem 0 0; padding: 0; }
.sched-rows li + li { border-top: 1px solid var(--line-soft); }
.sched-rows a, .sched-rows .sched-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: .6rem; align-items: center;
  padding: .38rem .35rem; margin: 0 -.35rem; border-radius: var(--r-sm);
  font-size: .86rem; color: var(--ink-soft); text-decoration: none;
}
.sched-rows a:hover, .sched-rows a:focus-visible { background: var(--line-soft); color: var(--ink); }
.sched-rows a:focus-visible { outline: 2px solid var(--brass); outline-offset: 1px; }
.sched-row-n { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }
.sched-rows a .sched-row-n::after { content: "\2192"; margin-left: .45rem; color: var(--muted); font-weight: 400; }
.sched-dot { width: .6rem; height: .6rem; border-radius: 50%; background: var(--line-strong); }
.sched-dot.tone-ok   { background: var(--ok); }
.sched-dot.tone-warm { background: var(--brass); }
.sched-dot.tone-pale { background: var(--brass-hi); }
.sched-dot.tone-late { background: var(--danger); }
.sched-open { margin-left: .35rem; font-size: .85rem; color: var(--brass-deep); white-space: nowrap; }
/* Of the ordered items, who they are with (CP05): a second, smaller breakdown. */
.sched-split-head {
  margin: .9rem 0 0; font-family: var(--font-mono); font-size: .6rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--brass-deep);
}
.sched-split-head + .sched-breakdown { margin-top: .4rem; }
/* When each item still at the vendor leaves it: every date, its vendor and how it is known, behind a summary. */
.sched-dates { margin: .8rem 0 0; }
.sched-dates summary { cursor: pointer; font-size: .85rem; color: var(--brass-deep); }
.sched-dates table { width: 100%; border-collapse: collapse; margin-top: .4rem; font-size: .85rem; }
.sched-dates th, .sched-dates td { text-align: left; padding: .35rem .4rem; border-top: 1px solid var(--line-soft); vertical-align: top; }
.sched-dates th { font-size: .72rem; font-weight: 600; color: var(--muted); border-top: 0; }
.sched-dates .num { text-align: right; font-variant-numeric: tabular-nums; }
.sched-dates td:first-child { white-space: nowrap; font-variant-numeric: tabular-nums; }
.sched-dates .is-past td:first-child { color: var(--danger); font-weight: 600; }
.sched-dates-basis { display: block; font-size: .75rem; color: var(--muted); }
.sched-note {
  margin: auto 0 0; padding-top: .7rem; font-size: .8rem; line-height: 1.5; color: var(--muted);
}
.sched-breakdown + .sched-note, .sched-window + .sched-note { border-top: 1px solid var(--line-soft); margin-top: .7rem; }
.sched-evidence {
  margin: .9rem 0 0; padding-top: .75rem; border-top: 1px solid var(--line-soft);
  font-size: .82rem; color: var(--muted);
}

@media (max-width: 560px) {
  .sched-alerts li { grid-template-columns: auto 1fr; }
  .sched-alert-open { grid-column: 2; justify-self: start; }
}


/* ========================= 20. REFERENCE LIBRARY =========================
   Operating guides, rendered inside the portal chrome.

   The first guide arrived as a standalone page carrying its own palette —
   #a66f3f accent, #f3eee5 paper, Inter and Georgia. Every one of those is a
   near-miss for a token that already exists here, and a near-miss reads worse
   than a difference: the page looks like the portal until you put it beside
   the portal. So nothing below invents a colour, a font or a radius.
   ====================================================================== */

.ref-index { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr)); gap: .9rem; margin-top: 1rem; }
.ref-index-card {
  display: block; text-decoration: none; color: inherit;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 1.1rem 1.2rem;
}
.ref-index-card:hover { border-color: var(--brass); }
.ref-index-cat { display: block; font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; color: var(--brass-deep); font-weight: 700; }
.ref-index-card strong { display: block; margin: .35rem 0 .4rem; font-family: var(--font-serif); font-size: 1.35rem; font-weight: 500; line-height: 1.2; }
.ref-index-summary { display: block; color: var(--muted); font-size: .9rem; line-height: 1.5; }
.ref-index-meta { display: block; margin-top: .7rem; font-size: .72rem; color: var(--muted); }

/* The two-column reading layout. The aside is sticky on a wide screen and
   simply sits above the document on a narrow one — a sticky sidebar on a phone
   eats the viewport it is meant to help you navigate. */
.ref-layout { display: grid; grid-template-columns: 17rem minmax(0, 1fr); gap: 1.1rem; align-items: start; }
@media (max-width: 62rem) { .ref-layout { grid-template-columns: 1fr; } }
/* A grid item's minimum is its content, so a wide table pushed the page sideways on a phone. */
.ref-layout > * { min-width: 0; }

.ref-aside { position: sticky; top: 1rem; }
@media (max-width: 62rem) { .ref-aside { position: static; } }
.ref-nav { padding: 1.1rem; }
.ref-search-label { display: block; font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; color: var(--brass-deep); font-weight: 700; margin-bottom: .5rem; }
.ref-search { width: 100%; border: 1px solid var(--line-strong); background: var(--surface-2); border-radius: var(--r-input); padding: .6rem .7rem; font: inherit; font-size: .9rem; color: var(--ink); }
.ref-search:focus { outline: none; border-color: var(--brass); box-shadow: 0 0 0 3px rgba(184,145,106,.2); }
.ref-search-note { margin: .5rem 0 0; font-size: .75rem; color: var(--muted); }
.ref-toc { margin-top: .9rem; padding-top: .9rem; border-top: 1px solid var(--line); }
.ref-toc a { display: block; padding: .4rem .5rem; border-radius: var(--r-sm); font-size: .86rem; color: var(--ink-soft); text-decoration: none; }
.ref-toc a:hover { background: var(--bg); color: var(--ink); }
.ref-updated { margin: .9rem 0 0; padding-top: .8rem; border-top: 1px solid var(--line); font-size: .72rem; color: var(--muted); }

/* --- the document itself --- */
.ref-doc { padding: 1.6rem 1.8rem; }
@media (max-width: 40rem) { .ref-doc { padding: 1.1rem; } }
.ref-section { padding-bottom: 1.6rem; margin-bottom: 1.6rem; border-bottom: 1px solid var(--line-soft); scroll-margin-top: 1rem; }
.ref-section:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.ref-doc h2 { font-family: var(--font-serif); font-size: 1.9rem; font-weight: 500; line-height: 1.15; margin: 0 0 .5rem; }
.ref-doc h3 { font-size: 1rem; margin: 1.4rem 0 .5rem; }
.ref-doc p { margin: .6rem 0; line-height: 1.6; }
.ref-lede { color: var(--muted); font-size: 1rem; margin-bottom: 1.1rem !important; }
.ref-doc ul, .ref-doc ol { padding-left: 1.3rem; }
.ref-doc li { margin: .35rem 0; line-height: 1.6; }
.ref-numbered li { margin: .55rem 0; }

.ref-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); gap: .7rem; margin: .9rem 0; }
.ref-card { border: 1px solid var(--line); border-radius: var(--r-card); padding: .9rem 1rem; background: var(--surface-2); }
.ref-card strong { display: block; margin-bottom: .3rem; }
.ref-card p { margin: 0; font-size: .875rem; color: var(--ink-soft); line-height: 1.55; }
/* Four states, borrowing the status tokens rather than restating them. There
   is no green in this system by design; "live" is deep brass. */
.ref-card.ref-ok, .ref-card.ref-live { border-left: 3px solid var(--ok); }
.ref-card.ref-bad { border-left: 3px solid var(--danger); }
.ref-card.ref-proposed { border-left: 3px solid var(--line-strong); background: var(--bg); }

.ref-note { border-left: 3px solid var(--brass); background: var(--bg); padding: .85rem 1rem; border-radius: var(--r-sm); margin: 1rem 0; font-size: .9rem; line-height: 1.6; color: var(--ink-soft); }
.ref-note strong { color: var(--ink); }
.ref-note.ref-warn { border-left-color: var(--ok); }
.ref-note.ref-bad { border-left-color: var(--danger); background: var(--danger-bg); }

.ref-table { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-card); margin: 1rem 0; background: var(--surface-2); }
.ref-table table { border-collapse: collapse; width: 100%; min-width: 34rem; }
.ref-table th, .ref-table td { text-align: left; padding: .6rem .8rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; font-size: .86rem; }
.ref-table th { font-size: .66rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); background: var(--bg); }
.ref-table tr:last-child td { border-bottom: 0; }

.ref-path { font-family: var(--font-mono); font-size: .8rem; background: var(--chrome); color: var(--chrome-ink); border-radius: var(--r-sm); padding: .7rem .85rem; overflow-x: auto; line-height: 1.7; }
.ref-doc code { font-family: var(--font-mono); font-size: .86em; background: var(--bg); border: 1px solid var(--line); border-radius: 5px; padding: .05rem .3rem; }
.ref-path code { background: none; border: 0; padding: 0; color: inherit; }

.ref-two { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: .9rem 0; }
@media (max-width: 46rem) { .ref-two { grid-template-columns: 1fr; } }
.ref-mini { font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--brass-deep); font-weight: 700; margin-bottom: .35rem; }

.ref-flow { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin: 1rem 0; color: var(--brass-deep); font-weight: 700; }
.ref-step { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: .4rem .6rem; font-size: .8rem; color: var(--ink); font-weight: 600; }

.ref-badge { display: inline-block; border: 1px solid var(--line); background: var(--surface-2); border-radius: var(--r-pill); padding: .15rem .55rem; font-family: var(--font-mono); font-size: .7rem; color: var(--muted); margin: 0 .25rem .35rem 0; }

.ref-check { list-style: none; padding: 0; margin: .6rem 0; }
.ref-check li { position: relative; padding: .4rem 0 .4rem 1.4rem; border-bottom: 1px solid var(--line-soft); margin: 0; font-size: .9rem; }
.ref-check li:before { content: "\2713"; position: absolute; left: 0; color: var(--ok); font-weight: 700; }
.ref-check li:last-child { border-bottom: 0; }

/* ---- help: the manual and the FAQ (views/help.php, help-guide.php) ------
 *
 * Built on the reference library's components; these are the parts it lacks:
 * a search row that submits, the question list, results, and the pager.
 */
.help-search { margin-top: 1rem; }
.help-search-row { display: flex; gap: .5rem; flex-wrap: wrap; }
.help-search-row .ref-search { flex: 1 1 14rem; width: auto; }
.help-cat { font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--brass-deep); font-family: var(--font-sans); font-weight: 700; margin: 0; }
.help-topic { margin: 1.3rem 0 .5rem; font-size: .95rem; }
.help-faq { border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface-2); }
.help-q { border-bottom: 1px solid var(--line-soft); }
.help-q:last-child { border-bottom: 0; }
.help-q summary { cursor: pointer; padding: .75rem 1rem; font-weight: 600; line-height: 1.45; }
.help-q summary:hover { color: var(--brass-deep); }
.help-q[open] summary { color: var(--brass-deep); }
.help-a { padding: 0 1rem .9rem; color: var(--ink-soft); line-height: 1.6; font-size: .92rem; }
.help-a p { margin: .4rem 0; }
.help-a ol, .help-a ul { padding-left: 1.3rem; margin: .4rem 0; }
.help-hits { list-style: none; padding: 0; margin: .4rem 0 1rem; }
.help-hits li { padding: .7rem 0; border-bottom: 1px solid var(--line-soft); }
.help-hits li:last-child { border-bottom: 0; }
.help-hits p { margin: .3rem 0 0; color: var(--muted); font-size: .88rem; line-height: 1.5; }
.help-hit-guide { margin-left: .5rem; font-size: .72rem; color: var(--muted); }
.help-pager { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin: 1rem 0; font-size: .9rem; }
.help-pager a { color: var(--brass-deep); text-decoration: none; }
.help-pager a:hover { text-decoration: underline; }
/* The keys a reader presses or clicks, named as they appear on the page. */
.ref-doc kbd, .help-a kbd {
  font-family: var(--font-sans); font-size: .85em; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: 5px;
  background: var(--surface-2); padding: .02rem .35rem; color: var(--ink);
}

.ref-doc details { border: 1px solid var(--line); border-radius: var(--r-card); margin: .55rem 0; background: var(--surface-2); overflow: hidden; }
.ref-doc summary { cursor: pointer; padding: .75rem .95rem; font-weight: 600; font-size: .9rem; list-style: none; position: relative; padding-right: 2.4rem; }
.ref-doc summary::-webkit-details-marker { display: none; }
.ref-doc summary:after { content: "+"; position: absolute; right: .95rem; top: .5rem; font-size: 1.2rem; color: var(--brass-deep); font-weight: 400; }
.ref-doc details[open] summary { border-bottom: 1px solid var(--line); background: var(--bg); }
.ref-doc details[open] summary:after { content: "\2013"; }
.ref-details-body { padding: .8rem .95rem; font-size: .88rem; line-height: 1.6; color: var(--ink-soft); }

/* Print: the guide, nothing else. Somebody will hand section 8 to an
   accountant, and the search box and contents list are not part of that. */
@media print {
  .ref-aside { display: none !important; }
  .ref-layout { display: block; }
  .ref-doc { border: 0; box-shadow: none; padding: 0; }
  .ref-section { page-break-inside: avoid; }
  .ref-doc details { break-inside: avoid; }
  .ref-doc details:not([open]) .ref-details-body { display: block !important; }
  .ref-doc summary:after { display: none; }
}


/* The goods / full-cost switch on the Financial summary card: a ghost button
   under the figure it changes, plainly something to press. */
.metric-switch .metric-toggle { margin: .6rem 0 .4rem; padding: .45rem .9rem; font-size: .64rem; }
/* The full-price and range buttons side by side, wrapping on a narrow card (2026-11-05). */
.metric-switch .metric-actions { display: flex; flex-wrap: wrap; gap: 0 .5rem; }
.metric-switch .metric-range { margin: .2rem 0 0; }
/* The budget beside a TBD figure while options are open (2026-11-05). */
.metric-switch .metric-tbd { margin: .15rem 0 0; color: var(--ink); }
.metric-switch .metric-range strong { color: var(--ink); font-weight: 600; }
.metric-switch [hidden] { display: none; }


/* The client/vendor divide on the Accounting card. A rule rather than a
   sub-heading: the split is worth seeing and not worth two more lines of
   chrome on a card that is already six figures tall. */
.panel .line-rule { border-top: 1px solid var(--line); padding-top: .45rem; margin-top: .45rem; }


/* Transaction filters. Chips are links and the dates are a GET form, so every
   view of this page is a URL somebody can send. */
.filter-bar { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: 1rem; }
.filter-bar .chip {
  display: inline-flex; align-items: center; gap: .4rem;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: .3rem .7rem; font-size: .8rem; text-decoration: none;
  color: var(--ink-soft); background: var(--surface-2);
}
.filter-bar .chip:hover { border-color: var(--brass); color: var(--ink); }
.filter-bar .chip-on { background: var(--chrome); border-color: var(--chrome); color: var(--chrome-ink); }
.filter-bar .chip-n { font-family: var(--font-mono); font-size: .72rem; opacity: .7; }

.filter-dates { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin-top: .6rem; }
.filter-dates label { font-size: .75rem; color: var(--muted); }
.filter-dates input[type="date"] {
  border: 1px solid var(--line-strong); border-radius: var(--r-input);
  padding: .3rem .5rem; font: inherit; font-size: .8rem;
  background: var(--surface-2); color: var(--ink);
}
.filter-dates button {
  border: 1px solid var(--line-strong); border-radius: var(--r-input);
  padding: .3rem .8rem; font: inherit; font-size: .8rem;
  background: var(--surface-2); color: var(--ink); cursor: pointer;
}
.filter-dates button:hover { border-color: var(--brass); }
.filter-dates .filter-clear { font-size: .78rem; color: var(--brass-deep); }
@media (max-width: 34rem) { .filter-dates input[type="date"] { flex: 1 1 8rem; } }

/* The goods-estimate breakout on the Accounting card. A <details> so it works
   with scripting off; the marker is left native rather than restyled, because a
   custom triangle is one more thing to keep working in every browser. */
.est-breakout { margin: 0; }
.est-breakout > summary {
  display: flex; justify-content: space-between; gap: .5rem;
  cursor: pointer; padding: .2rem 0; list-style-position: outside;
}
.est-breakout > summary::-webkit-details-marker { color: var(--ink-soft); }
.est-breakout > summary strong { font-variant-numeric: tabular-nums; }
.est-parts {
  list-style: none; margin: .35rem 0 0; padding: 0 0 0 1.1rem;
  display: grid; gap: .2rem;
}
.est-parts li { display: flex; justify-content: space-between; gap: .5rem; font-size: .85rem; color: var(--ink-soft); }
.est-parts strong { font-variant-numeric: tabular-nums; }
.est-parts .est-parts-rule { border-top: 1px solid var(--line); padding-top: .3rem; margin-top: .2rem; }
.est-note { margin: .4rem 0 .2rem 1.1rem; font-size: .8rem; color: var(--ink-soft); line-height: 1.4; }

/* ======================= DESIGNER MOOD BOARDS ======================== */
/*
 * A list people scan for one board and open it, so it is a table without
 * rules: rows separated by space and a faint alternating ground, the title as
 * the link and the only thing in ink, everything else quiet. On a phone each
 * row becomes a small card, title first.
 */
.mb-form {
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) auto;
  gap: .8rem; align-items: end;
}
.mb-form .field { margin: 0; }
.mb-submit { padding-bottom: .05rem; }
.mb-table { width: 100%; border-collapse: separate; border-spacing: 0 .25rem; }
.mb-table th {
  text-align: left; font-weight: 500;
  font-family: var(--font-mono); font-size: .66rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
  padding: .2rem .9rem .35rem; border: 0;
}
.mb-table td { padding: .7rem .9rem; border: 0; vertical-align: baseline; }
.mb-table tbody tr:nth-child(odd) td { background: var(--bg); }
.mb-table tbody td:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.mb-table tbody td:last-child { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.mb-table tbody tr:hover td { background: var(--ok-bg); }
.mb-title a {
  color: var(--ink); font-weight: 600; text-decoration: none;
  overflow-wrap: anywhere;
}
.mb-title a:hover, .mb-title a:focus-visible { color: var(--brass-deep); text-decoration: underline; }
.mb-title a::after { content: " \2197"; color: var(--brass); font-weight: 400; }
.mb-host { font-family: var(--font-mono); font-size: .78rem; color: var(--brass-deep); white-space: nowrap; }
.mb-who, .mb-when { font-size: .85rem; color: var(--muted); white-space: nowrap; }
.mb-when { font-variant-numeric: tabular-nums; text-align: right; }
.mb-table th:last-child { text-align: right; }
@media (max-width: 640px) {
  .mb-form { grid-template-columns: 1fr; }
  .mb-table thead { display: none; }
  .mb-table, .mb-table tbody { display: block; width: 100%; }
  /* The row is the card: its ground, not each cell's, so it reads as one. */
  .mb-table tbody tr { display: block; padding: .65rem .9rem; margin-bottom: .35rem; border-radius: var(--r-sm); }
  .mb-table tbody tr:nth-child(odd) { background: var(--bg); }
  .mb-table tbody tr:hover { background: var(--ok-bg); }
  .mb-table tbody tr td, .mb-table tbody tr:nth-child(odd) td, .mb-table tbody tr:hover td {
    background: transparent; border-radius: 0; padding: 0;
  }
  .mb-table td.mb-title { display: block; margin-bottom: .2rem; }
  .mb-table td.mb-host, .mb-table td.mb-who, .mb-table td.mb-when { display: inline; text-align: left; }
  .mb-table td.mb-host::after, .mb-table td.mb-who::after { content: " \00b7 "; color: var(--muted); }
}

/* ---- installation (Gate 04 on site) -----------------------------------
 *
 * Recorded from a phone in a corridor (modules/install.php), so every form
 * folds to one column and the per-row count form stays a single line: a
 * number, "of 40", Save. An open issue is the one terracotta thing here.
 */
.install-counts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: .6rem; margin: .8rem 0 0;
}
.install-counts div {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); padding: .55rem .75rem;
}
.install-counts dt { font-size: .78rem; color: var(--muted); }
.install-counts dd {
  margin: 0; font-size: 1.4rem; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.install-counts div.is-issue { border-color: var(--danger-line); background: var(--danger-bg); }
.install-counts div.is-issue dd { color: var(--danger); }
.install-status { margin: .2rem 0 .8rem; }
.install-status .hint { display: block; }
.install-issues { list-style: none; margin: 0 0 1rem; padding: 0; }
.install-issues li {
  padding: .65rem .8rem; margin-bottom: .45rem;
  border: 1px solid var(--danger-line); border-left: 3px solid var(--danger);
  border-radius: var(--r-sm); background: var(--surface);
}
.install-note { margin: .3rem 0 0; font-size: .88rem; color: var(--ink-soft); white-space: pre-line; }
.install-forms {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: .8rem 1.4rem; padding-top: .8rem; border-top: 1px solid var(--line);
}
.install-form { margin: .5rem 0 0; }
.install-form .field { margin-bottom: .6rem; }
.install-qty input { max-width: 8rem; }
.install-history { margin-top: 1rem; }
.install-history summary { cursor: pointer; color: var(--brass-deep); font-size: .9rem; }
.install-history ol, .install-log { list-style: none; margin: .5rem 0 0; padding: 0; }
.install-history li, .install-log li {
  display: flex; flex-wrap: wrap; gap: .2rem .7rem; align-items: baseline;
  padding: .45rem 0; border-bottom: 1px solid var(--line); font-size: .9rem;
}
.install-history li .install-note, .install-log li .install-note { flex-basis: 100%; }
.install-when { font-size: .82rem; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.install-table { width: 100%; border-collapse: collapse; }
.install-table th {
  text-align: left; font-size: .75rem; font-weight: 600; color: var(--muted);
  padding: .4rem .6rem; border-bottom: 1px solid var(--line);
}
.install-table td { padding: .55rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.install-table td .muted { display: block; font-size: .85rem; }
.install-inline { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: 0; }
.install-inline input[type="number"] {
  width: 5rem; padding: .45rem .6rem; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-radius: var(--r-input); font: inherit; color: var(--ink);
}
.install-inline input[type="number"]:focus {
  outline: none; border-color: var(--brass); box-shadow: 0 0 0 3px var(--ok-bg);
}
.install-inline .muted { display: inline; }
@media (max-width: 640px) {
  .install-table thead { display: none; }
  .install-table, .install-table tbody, .install-table tr, .install-table td { display: block; width: 100%; }
  .install-table tr { padding: .6rem 0; border-bottom: 1px solid var(--line); }
  .install-table td { padding: .15rem 0; border: 0; }
}

/* ---- view as (admin preview) ------------------------------------------
 *
 * The picker sits with the account controls in the dark bar. While a preview
 * runs, a brass band under the header says which role is showing and keeps
 * the way out in reach on every page. Brass, not terracotta: nothing is wrong,
 * the reader is just somewhere other than usual.
 */
.preview-pick { display: flex; align-items: center; gap: .5rem; margin: 0; }
.preview-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem;
  padding: .6rem 1.5rem; background: var(--ok-bg);
  border-bottom: 2px solid var(--brass); color: var(--ink);
}
.preview-bar p { margin: 0; flex: 1 1 22rem; font-size: .9rem; }
.preview-bar p strong { margin-right: .6rem; }
.preview-bar p span { color: var(--ink-soft); }
.preview-bar form { margin: 0; }
.preview-bar select {
  padding: .4rem .6rem; border: 1px solid var(--line-strong); border-radius: var(--r-input);
  background: var(--surface-2); font: inherit; color: var(--ink);
}
@media (max-width: 640px) { .preview-bar { padding: .6rem 1rem; } }

/* ---- against the approved budget (CP12) -------------------------------
 *
 * Four columns of money that must read down as well as across: figures right
 * aligned in tabular numerals, the label column free to wrap. Over budget is
 * the one terracotta thing; under is brass, on budget is plain. On a phone
 * each row becomes a small card with its column names inline.
 */
.budget-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.budget-table th, .budget-table td { padding: .5rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.budget-table thead th { font-size: .75rem; font-weight: 600; color: var(--muted); text-align: right; }
.budget-table thead th:first-child { text-align: left; }
/* Row labels are words, not column headings: undo the global th lettering. */
.budget-table tbody th, .budget-table tfoot th {
  text-align: left; font-weight: 500; color: var(--ink);
  font-family: var(--font-sans); font-size: .9rem; text-transform: none; letter-spacing: normal;
}
.budget-table td { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.budget-parts { list-style: none; margin: .25rem 0 0; padding: 0; font-size: .8rem; color: var(--muted); font-weight: 400; }
.budget-parts li { display: flex; justify-content: space-between; gap: .8rem; }
.budget-parts span { font-variant-numeric: tabular-nums; }
.budget-why { margin: .15rem 0 0; font-size: .78rem; color: var(--muted); font-weight: 400; }
.budget-diff.is-over { color: var(--danger); font-weight: 600; }
.budget-diff.is-under { color: var(--ok); }
.budget-diff.is-on { color: var(--muted); }
.budget-subtotal th, .budget-subtotal td { border-top: 2px solid var(--line-strong); font-weight: 600; }
.budget-designer th, .budget-designer td { color: var(--ink-soft); }
.budget-table tfoot th, .budget-table tfoot td { font-weight: 700; border-bottom: 0; border-top: 2px solid var(--line-strong); }
@media (max-width: 640px) {
  .budget-table thead { display: none; }
  .budget-table, .budget-table tbody, .budget-table tfoot, .budget-table tr { display: block; width: 100%; }
  .budget-table tr { padding: .5rem 0; border-bottom: 1px solid var(--line); }
  .budget-table th, .budget-table td { display: block; border: 0; padding: .1rem 0; text-align: left; white-space: normal; }
  .budget-table td:nth-of-type(1)::before { content: "Budget "; color: var(--muted); }
  .budget-table td:nth-of-type(2)::before { content: "Current "; color: var(--muted); }
  .budget-table td:empty { display: none; }
  .budget-subtotal th, .budget-subtotal td, .budget-table tfoot th, .budget-table tfoot td { border-top: 0; }
}

/* ---- payments: paid, due, still to come (views/_payments.php, CP14) ----
 * The three figures share the install-count card shape; past-due money takes
 * the terracotta danger tone, the only state here that asks for action. */
.pay-figures {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: .6rem; margin: .8rem 0 .4rem;
}
.pay-figures div {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); padding: .55rem .75rem;
}
.pay-figures dt { font-size: .78rem; color: var(--muted); }
.pay-figures dd {
  margin: 0; font-size: 1.4rem; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.pay-figures div.is-overdue { border-color: var(--danger-line); background: var(--danger-bg); }
.pay-figures div.is-overdue dd, .pay-figures div.is-overdue .pay-note { color: var(--danger); }
.pay-note { display: block; margin: .1rem 0 0; font-size: .78rem; font-weight: 400; color: var(--muted); }
.pay-table { width: 100%; border-collapse: collapse; font-size: .9rem; margin-top: .6rem; }
.pay-table caption { text-align: left; font-size: .78rem; font-weight: 600; color: var(--muted); padding-bottom: .3rem; }
.pay-table th, .pay-table td { padding: .5rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; text-align: left; }
.pay-table thead th { font-size: .75rem; font-weight: 600; color: var(--muted); }
.pay-table tbody th { font-weight: 600; font-family: inherit; font-size: .9rem; letter-spacing: 0; text-transform: none; color: var(--ink); }
.pay-table td:last-child, .pay-table thead th:last-child { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pay-table .badge { margin-left: .35rem; }
@media (max-width: 640px) {
  .pay-table thead { display: none; }
  .pay-table, .pay-table caption, .pay-table tbody, .pay-table tr { display: block; width: 100%; }
  .pay-table tr { display: grid; grid-template-columns: 1fr auto; gap: .15rem .6rem; padding: .5rem 0; border-bottom: 1px solid var(--line); }
  .pay-table th, .pay-table td { border: 0; padding: 0; }
  .pay-table td { grid-column: 1 / -1; text-align: left; }
  .pay-table td:nth-of-type(1)::before { content: "Issued "; color: var(--muted); }
  .pay-table td:nth-of-type(2)::before { content: "Due "; color: var(--muted); }
  .pay-table td:last-child { grid-column: 2; grid-row: 1; text-align: right; }
}

/* ---- the top bar on a phone ------------------------------------------
 *
 * Everything but the brand folds behind one Menu button (views/layout.php).
 * Wide screens never see the toggle, and the wrapper is display: contents
 * there, so its children lay out in the bar exactly as before. The checkbox
 * is display: none there too, not just transparent, so a keyboard or a screen
 * reader on a wide screen never lands on a control that does nothing; on a
 * phone it is visually hidden but still focusable, and the label shows focus.
 */
.topbar-toggle { display: none; }
.topbar-menu-btn { display: none; }
.topbar-collapse { display: contents; }
@media (max-width: 760px) {
  .topbar { padding: .6rem 1rem; gap: .6rem; }
  .topbar-toggle { display: block; position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
  .topbar-menu-btn {
    display: inline-flex; align-items: center; gap: .45rem; margin-left: auto;
    border: 1px solid rgba(244,240,231,.3); border-radius: var(--r-pill);
    padding: .42rem .9rem; color: var(--chrome-ink); cursor: pointer;
    font-family: var(--font-mono); font-size: .66rem; letter-spacing: .12em; text-transform: uppercase;
  }
  .topbar-toggle:focus-visible + .topbar-menu-btn { outline: 2px solid var(--brass); outline-offset: 2px; }
  .topbar-toggle:checked + .topbar-menu-btn { border-color: var(--brass); color: var(--brass-hi); }
  .topbar-collapse {
    display: none; flex-basis: 100%; flex-direction: column; align-items: stretch; gap: .8rem;
    padding: .6rem 0 .3rem; border-top: 1px solid var(--line-dark);
  }
  .topbar-toggle:checked ~ .topbar-collapse { display: flex; }
  .topbar-collapse .spacer { display: none; }
  .topbar-collapse .topnav { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; padding-left: 0; border-left: 0; }
  .topbar-collapse .topbar-account { flex-direction: column; align-items: stretch; gap: .7rem; }
  .topbar-collapse .topbar-account form { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
  .topbar-collapse .topbar-account select,
  .topbar-collapse input[type="search"] { flex: 1 1 auto; min-width: 0; width: 100%; }
}

/* ---- the project report (views/report.php, views/_xr_chart.php) ----------
 * An executive report: a period filter above everything, the summary and its
 * figures, where the items stand, the trends, the period, the money, the items.
 * Chart marks wear the --viz tokens; every word on a chart wears the ink ones.
 */
.xr-filters { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 1.1rem 0 0; }
.xr-filters .chip { margin: 0; }
.xr-filters-label {
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.xr-custom { display: inline-flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.xr-custom label { display: inline-flex; align-items: center; gap: .35rem; margin: 0; font-size: .85rem; color: var(--ink-soft); }
.xr-custom input[type="date"] { width: auto; padding: .35rem .5rem; }
.xr-lede { font-size: 1.05rem; line-height: 1.65; max-width: 62rem; margin: .2rem 0 0; }
.xr-reasons { margin: .6rem 0 0; padding-left: 1.2rem; }
.xr-reasons li::marker { color: var(--danger); }
.xr-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .8rem; margin: 1.2rem 0 0; }
.xr-kpi { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: .8rem .95rem; }
.xr-kpi dt {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.xr-kpi dd { margin: .3rem 0 0; font-size: 1.4rem; font-weight: 600; line-height: 1.15; color: var(--ink); white-space: nowrap; }
.xr-kpi dd.xr-kpi-text { font-size: 1.15rem; }
.xr-kpi-note { margin: .25rem 0 0; font-size: .8rem; color: var(--muted); }
.xr-kpis--quiet .xr-kpi { background: transparent; }
.xr-gatebar { display: block; width: 100%; height: 22px; margin: .8rem 0 .6rem; border-radius: 4px; }
.xr-gate-1 { fill: var(--viz-g1); }
.xr-gate-2 { fill: var(--viz-g2); }
.xr-gate-3 { fill: var(--viz-g3); }
.xr-gate-4 { fill: var(--viz-g4); }
.xr-gate-legend { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; margin: 0; padding: 0; list-style: none; font-size: .88rem; }
.xr-gate-legend li { display: inline-flex; align-items: center; gap: .45rem; }
.xr-key-box { width: 10px; height: 10px; flex: none; }
.xr-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr)); gap: 1.6rem 1.4rem; }
.xr-chart { margin: 0; break-inside: avoid; }
.xr-chart figcaption { display: flex; flex-direction: column; gap: .15rem; margin: 0 0 .5rem; }
.xr-chart-title { font-weight: 600; color: var(--ink); }
.xr-chart-sub { font-size: .85rem; color: var(--muted); }
.xr-legend { display: flex; flex-wrap: wrap; gap: .3rem 1rem; margin-top: .3rem; }
.xr-key { display: inline-flex; align-items: center; gap: .4rem; font-size: .8rem; color: var(--ink-soft); }
.xr-key-line { width: 16px; height: 4px; flex: none; }
.xr-key-line line { stroke-width: 2; stroke-linecap: round; }
.xr-plot { display: block; width: 100%; height: auto; overflow: visible; }
.xr-grid { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
.xr-tick { fill: var(--muted); font-family: var(--font-sans); font-size: 12px; font-variant-numeric: tabular-nums; }
.xr-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.xr-stroke-viz-1 { stroke: var(--viz-1); }
.xr-stroke-viz-2 { stroke: var(--viz-2); }
.xr-stroke-viz-plan { stroke: var(--viz-plan); }
.xr-dot { stroke: var(--surface); stroke-width: 2; }
.xr-fill-viz-1 { fill: var(--viz-1); }
.xr-fill-viz-2 { fill: var(--viz-2); }
.xr-fill-viz-plan { fill: var(--viz-plan); }
.xr-end { fill: var(--ink); font-family: var(--font-sans); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.xr-hit { fill: transparent; }
@media (hover: hover) { .xr-hit:hover { fill: var(--line-soft); } }
.xr-table summary { cursor: pointer; margin-top: .4rem; font-size: .85rem; color: var(--brass-deep); }
.xr-table th.num, .xr-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.xr-fin-total th, .xr-fin-total td { font-weight: 700; }
@media (max-width: 600px) {
  .xr-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
  .xr-kpi { padding: .7rem .75rem; }
  .xr-kpi dd { font-size: 1.15rem; white-space: normal; }
}
@media print {
  .xr-filters, .xr-table { display: none; }
  .xr-kpi, .xr-chart, .xr-gatebar { break-inside: avoid; }
  .xr-charts { grid-template-columns: 1fr 1fr; gap: 1rem; }
  .xr-kpis { grid-template-columns: repeat(3, 1fr); }
}

/* ---- the living report (public/assets/report.js, decision D7) ---------
 * Everything below the report draws without it. The script adds the motion
 * (only under .xr-anim, which it sets for a reader who has not asked for less
 * motion), the crosshair and tooltip, the bars' explanations, the items'
 * sorting and filter, and the period changing in place. On paper all of it
 * is drawn and still.
 */
.xr-root { transition: opacity .25s ease; }
.xr-root.is-loading { opacity: .5; pointer-events: none; }
@media (prefers-reduced-motion: no-preference) { html:has(.xr-root) { scroll-behavior: smooth; } }
.xr-root h2[id] { scroll-margin-top: 5rem; }

/* The bar that follows the reader down the report: one pill per section, the
   one being read filled. */
.xr-nav {
  position: sticky; top: .5rem; z-index: 4;
  display: flex; gap: .25rem; overflow-x: auto; scrollbar-width: none;
  padding: .35rem; margin: 0 0 1.4rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill);
  box-shadow: var(--shadow-card);
}
.xr-nav::-webkit-scrollbar { display: none; }
.xr-nav a {
  flex: none; padding: .4rem .85rem; border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft); text-decoration: none; transition: background .2s, color .2s;
}
.xr-nav a:hover { color: var(--brass-deep); }
.xr-nav a[aria-current] { background: var(--chrome); color: var(--chrome-ink); }

/* A share under a percentage. */
.xr-meter { display: block; width: 100%; height: 6px; margin: .55rem 0 .1rem; }
.xr-meter-track { fill: var(--line-soft); }

/* Bars made of parts: each part answers on hover and focus, the others
   stepping back while one is read. */
.xr-seg { cursor: pointer; transition: opacity .18s ease; }
.xr-gatebar:hover .xr-seg, .xr-moneybar-bar:hover .xr-seg { opacity: .45; }
.xr-gatebar .xr-seg:hover, .xr-moneybar-bar .xr-seg:hover, .xr-seg:focus { opacity: 1; }
.xr-seg:focus { outline: none; }
.xr-seg:focus-visible rect, rect.xr-seg:focus-visible { stroke: var(--ink); stroke-width: 2; }
.xr-moneybar { margin: .4rem 0 1.4rem; break-inside: avoid; }
.xr-moneybar-bar { display: block; width: 100%; height: 26px; margin: .6rem 0; border-radius: 4px; }
.xr-moneybar-legend { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; margin: 0; padding: 0; list-style: none; font-size: .88rem; }
.xr-moneybar-legend li { display: inline-flex; align-items: center; gap: .45rem; }
/* Past due is a state, so it wears the status colour, and always its words. */
.xr-fill-late { fill: var(--danger); }

/* One tooltip: the value leads, the series follows, keyed by a short stroke. */
.xr-tip {
  position: fixed; z-index: 60; pointer-events: none;
  display: flex; flex-direction: column; gap: .25rem; min-width: 9rem; max-width: 20rem;
  padding: .55rem .75rem; border-radius: var(--r-sm);
  background: var(--chrome); color: var(--chrome-ink); box-shadow: var(--shadow-pop);
  font-size: .82rem; line-height: 1.35;
}
.xr-tip[hidden] { display: none; }
.xr-tip--sample { position: static; display: inline-flex; margin: .6rem 0 1rem; }
.xr-tip-head { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .1em; text-transform: uppercase; opacity: .75; }
.xr-tip-row { display: flex; align-items: center; gap: .5rem; }
.xr-tip-key { width: 12px; height: 2px; border-radius: 1px; flex: none; }
.xr-tip-value { font-weight: 700; font-variant-numeric: tabular-nums; }
.xr-tip-label { opacity: .8; }
.xr-bg-viz-1 { background: var(--viz-1); }
.xr-bg-viz-2 { background: var(--viz-2); }
.xr-bg-viz-plan { background: var(--viz-plan); }

/* Reading a trend: a hairline at the date, a ringed dot on every series. */
.xr-area { fill-opacity: .1; stroke: none; }
.xr-plot[data-xr-chart] { cursor: crosshair; touch-action: pan-y; }
.xr-plot:focus { outline: none; }
.xr-plot:focus-visible { outline: 2px solid var(--brass); outline-offset: 4px; border-radius: 4px; }
.xr-cross { stroke: var(--ink-soft); stroke-width: 1; opacity: 0; pointer-events: none; shape-rendering: crispEdges; }
.xr-hover-dot { stroke: var(--surface); stroke-width: 2; opacity: 0; pointer-events: none; }
.xr-plot.is-reading .xr-cross, .xr-plot.is-reading .xr-hover-dot { opacity: 1; }
.xr-plot .is-off { opacity: .08 !important; transition: opacity .25s ease; }
.xr-key-toggle {
  background: none; border: 0; padding: .1rem .15rem; margin: 0; font: inherit; color: var(--ink-soft);
  cursor: pointer; border-radius: var(--r-sm);
}
.xr-key-toggle:hover { color: var(--ink); }
.xr-key-toggle[aria-pressed="false"] { opacity: .45; text-decoration: line-through; }

/* The items: a filter above, and every heading sorts. */
.xr-items-tools { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; margin: 0 0 .8rem; }
.xr-items-filter { display: flex; align-items: center; gap: .6rem; margin: 0; flex: 1 1 18rem; max-width: 28rem; }
.xr-items-filter-label {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.xr-items-filter input { flex: 1; min-width: 0; }
.xr-items-count { font-size: .82rem; color: var(--muted); }
.xr-sort {
  display: inline-flex; align-items: center; gap: .3rem; padding: 0; margin: 0;
  background: none; border: 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
}
.xr-sort::after { content: "\2195"; opacity: .3; font-size: .9em; letter-spacing: 0; }
th[aria-sort="ascending"] .xr-sort::after { content: "\2191"; opacity: 1; }
th[aria-sort="descending"] .xr-sort::after { content: "\2193"; opacity: 1; }

/* Motion, once each, as things come into view. */
@keyframes xr-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes xr-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes xr-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes xr-fade { from { opacity: 0; } to { opacity: 1; } }
.xr-anim .xr-reveal { opacity: 0; }
.xr-anim .xr-reveal.is-in { animation: xr-rise .65s cubic-bezier(.2, .7, .2, 1) both; }
.xr-anim .xr-kpi.is-in:nth-child(2) { animation-delay: .07s; }
.xr-anim .xr-kpi.is-in:nth-child(3) { animation-delay: .14s; }
.xr-anim .xr-kpi.is-in:nth-child(4) { animation-delay: .21s; }
.xr-anim .xr-kpi.is-in:nth-child(5) { animation-delay: .28s; }
.xr-anim .xr-kpi.is-in:nth-child(n+6) { animation-delay: .35s; }
.xr-anim .xr-chart .xr-line { stroke-dasharray: 1 1; stroke-dashoffset: 1; }
.xr-anim .xr-chart.is-in .xr-line { animation: xr-draw 1.4s cubic-bezier(.35, .6, .2, 1) .2s forwards; }
.xr-anim .xr-chart .xr-dot, .xr-anim .xr-chart .xr-end, .xr-anim .xr-chart .xr-area { opacity: 0; }
.xr-anim .xr-chart.is-in .xr-area { animation: xr-fade .9s ease 1.1s forwards; }
.xr-anim .xr-chart.is-in .xr-dot, .xr-anim .xr-chart.is-in .xr-end { animation: xr-fade .45s ease 1.45s forwards; }
.xr-anim .xr-gatebar rect, .xr-anim .xr-moneybar-bar rect, .xr-anim .xr-meter-fill {
  transform-box: fill-box; transform-origin: left center; transform: scaleX(0);
}
.xr-anim .xr-gatebar.is-in rect, .xr-anim .xr-moneybar.is-in rect, .xr-anim .is-in .xr-meter-fill {
  animation: xr-grow .8s cubic-bezier(.2, .7, .2, 1) .15s forwards;
}
.xr-anim .xr-gatebar.is-in .xr-seg:nth-of-type(2) rect, .xr-anim .xr-moneybar.is-in rect:nth-of-type(2) { animation-delay: .3s; }
.xr-anim .xr-gatebar.is-in .xr-seg:nth-of-type(3) rect, .xr-anim .xr-moneybar.is-in rect:nth-of-type(3) { animation-delay: .45s; }
.xr-anim .xr-gatebar.is-in .xr-seg:nth-of-type(4) rect, .xr-anim .xr-moneybar.is-in rect:nth-of-type(4) { animation-delay: .6s; }
.xr-anim .is-in .xr-meter-fill { animation-duration: 1.1s; animation-delay: .3s; }

@media print {
  .xr-nav, .xr-items-tools, .xr-tip, .xr-cross, .xr-hover-dot { display: none !important; }
  .xr-anim .xr-reveal, .xr-anim .xr-line, .xr-anim .xr-dot, .xr-anim .xr-end, .xr-anim .xr-area,
  .xr-anim .xr-gatebar rect, .xr-anim .xr-moneybar-bar rect, .xr-anim .xr-meter-fill {
    animation: none !important; opacity: 1 !important; transform: none !important; stroke-dashoffset: 0 !important;
  }
  .xr-sort::after { content: none; }
  .xr-items tr[hidden] { display: table-row; }
}

/* ============================== ASSETS =============================== */
/*
 * The project's library (/assets, /asset). A list people scan for one
 * document, so each row is: what it is at a glance (a thumbnail for a picture,
 * a type tag for anything else), the title as the link, then a quiet line of
 * facts and one button. "Start here" is the same row on a brass ground. The
 * type tags are told apart by ink and edge only, never by a new colour.
 */
.as-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.as-row {
  display: grid; grid-template-columns: 3.5rem minmax(0, 1fr) auto; gap: .9rem; align-items: center;
  padding: .6rem .8rem; border-radius: var(--r-sm); background: var(--bg);
}
.as-row:hover { background: var(--ok-bg); }
.as-list--pinned .as-row { background: var(--ok-bg); border: 1px solid var(--ok-line); }
.as-thumb, .as-type { width: 3.5rem; height: 3.5rem; border-radius: var(--r-sm); }
.as-thumb { object-fit: cover; display: block; border: 1px solid var(--line); background: var(--surface-2); }
.as-type {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .06em; font-weight: 600;
  background: var(--surface-2); color: var(--ink-soft); border: 1px solid var(--line-strong);
}
.as-type--pdf { color: var(--danger); border-color: var(--danger-line); }
.as-type--csv { color: var(--ok); border-color: var(--ok-line); }
.as-type--link { color: var(--brass-deep); border-color: var(--brass); border-style: dashed; }
.as-main { min-width: 0; }
.as-title { font-weight: 600; color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.as-title:hover, .as-title:focus-visible { color: var(--brass-deep); text-decoration: underline; }
.as-title--out::after { content: " \2197"; color: var(--brass); font-weight: 400; }
.as-desc { margin: .15rem 0 0; font-size: .88rem; color: var(--ink-soft); overflow-wrap: anywhere; }
.as-desc--full { margin: .6rem 0; font-size: .95rem; white-space: pre-line; }
.as-meta { display: flex; flex-wrap: wrap; gap: .25rem .9rem; align-items: center; margin: .2rem 0 0;
  font-size: .8rem; color: var(--muted); }
.as-actions { display: flex; gap: .4rem; }
.as-group { margin-top: 1.4rem; }
.as-group h2 { font-size: 1.05rem; margin: 0 0 .5rem; }
.as-open { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0 0; }
.as-preview { display: block; max-width: 100%; max-height: 32rem; margin-top: 1rem;
  border-radius: var(--r-sm); border: 1px solid var(--line); }
.as-form {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem 1rem; align-items: end;
}
.as-form .field { margin: 0; }
.as-form--one { grid-template-columns: minmax(0, 1fr) auto; }
.as-wide { grid-column: 1 / -1; }
.as-submit { display: flex; flex-wrap: wrap; gap: .8rem 1.2rem; align-items: center; justify-content: space-between; }
.as-versions td:first-child { white-space: nowrap; }
@media (max-width: 600px) {
  .as-row { grid-template-columns: 2.75rem minmax(0, 1fr); gap: .7rem; }
  .as-thumb, .as-type { width: 2.75rem; height: 2.75rem; }
  .as-actions { grid-column: 2; }
  .as-form, .as-form--one { grid-template-columns: minmax(0, 1fr); }
}
@media print {
  .as-actions, .as-form { display: none; }
}

/* ========================== PROJECT BRIEF ============================ */
/*
 * The Project brief (/project/brief): facts in a quiet grid, then the written
 * sections set for reading, not scanning: a measure of about 72 characters and
 * room between sections. A section that changed since the reader last read it
 * carries a brass badge. The card (Overview, Assets) is one line and one
 * button, on a brass edge when staff are owed a review.
 */
.brief-lede { color: var(--ink-soft); max-width: 72ch; margin: .3rem 0 .6rem; }
.brief-due { background: var(--ok-bg); border-color: var(--ok-line); border-left-color: var(--brass); color: var(--ink); }
.brief-facts {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: .7rem 1.4rem; margin: 0;
}
.brief-facts div { min-width: 0; }
.brief-facts dt { font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.brief-facts dd { margin: .15rem 0 0; overflow-wrap: anywhere; }
.brief-body { padding-top: .4rem; }
.brief-section { max-width: 72ch; padding: 1rem 0 1.1rem; border-bottom: 1px solid var(--line-soft); }
.brief-section:last-child { border-bottom: 0; }
.brief-section h2 { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 0 0 .5rem; font-size: 1.1rem; }
.brief-text p { margin: 0 0 .6rem; line-height: 1.6; }
.brief-text ul { margin: 0 0 .6rem; padding-left: 1.2rem; line-height: 1.6; }
.brief-text :last-child { margin-bottom: 0; }
.badge.brief-changed { background: var(--brass); color: var(--chrome); border-color: var(--brass); }
.brief-form .field { max-width: 72ch; }
.brief-form textarea { width: 100%; }
.brief-mine { margin-top: .5rem; padding: .6rem .8rem; border: 1px dashed var(--brass); border-radius: var(--r-sm); background: var(--surface-2); }
.brief-mine summary { cursor: pointer; font-size: .85rem; color: var(--brass-deep); }
.brief-card { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .8rem 1.2rem; }
.brief-card-main { min-width: 0; flex: 1 1 22rem; }
.brief-card .eyebrow { margin: 0; }
.brief-card-line { margin: .2rem 0 0; color: var(--ink-soft); }
.brief-card-due { margin: .4rem 0 0; font-size: .88rem; color: var(--ink-soft); }
.brief-card--due { border-left: 3px solid var(--brass); }
@media print {
  .brief-card .btn, .as-open, .brief-form { display: none; }
  .brief-section { break-inside: avoid; }
}
/* An asset linked to an item (views/_item_assets.php): its unlink button sits
   under the row, quiet, for staff only. */
.as-unlink { display: flex; justify-content: flex-end; margin-top: -.2rem; }
.as-unlink .btn { font-size: .72rem; padding: .3rem .7rem; }
.as-link-form { margin-top: 1rem; }
/* The close-out pack (views/assets.php): one download at the top of Warranty
   and care, with how many files and links it covers beside it. */
.as-closeout { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .75rem; margin: 0 0 .7rem; }
@media print { .as-closeout { display: none; } }

/* ---- the Budget Studio (views/budget-studio.php, modules/studio.php) ----
 * What-ifs against the approved budget, worked through with a client. The
 * summary is the one thing read from across a table, so it is large; a
 * switched-out row stays in place, struck through, so nothing jumps while
 * someone is talking. Presentation mode (.studio.is-present) hides the
 * portal's own chrome and the working hints, and keeps every switch. */
.studio-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.studio-summary { display: flex; flex-wrap: wrap; gap: 1rem 2.4rem; align-items: flex-end; }
.studio-figure { display: flex; flex-direction: column; gap: .2rem; }
.studio-figure-label { font-size: .75rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.studio-figure-value { font-family: var(--font-serif); font-size: 2.4rem; font-weight: 600; line-height: 1.05;
  color: var(--ink); font-variant-numeric: tabular-nums; }
.studio-figure-value.is-quiet { font-size: 1.5rem; color: var(--ink-soft); }
.studio-verdict { font-weight: 600; color: var(--muted); }
.studio-verdict.is-over { color: var(--danger); }
.studio-verdict.is-under { color: var(--ok); }
.studio-change { color: var(--ink-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.studio-meta { flex-basis: 100%; margin: 0; font-size: .9rem; color: var(--muted); }
.studio-unpriced { color: var(--danger); }

.studio-bill { width: 100%; border-collapse: collapse; font-size: .9rem; }
.studio-bill th, .studio-bill td { padding: .5rem .6rem; border-bottom: 1px solid var(--line); text-align: right; }
.studio-bill thead th { font-size: .75rem; font-weight: 600; color: var(--muted); }
.studio-bill thead th:first-child { text-align: left; }
.studio-bill tbody th, .studio-bill tfoot th {
  text-align: left; font-weight: 500; color: var(--ink);
  font-family: var(--font-sans); font-size: .9rem; text-transform: none; letter-spacing: normal;
}
.studio-bill td { font-variant-numeric: tabular-nums; white-space: nowrap; }
.studio-bill tfoot th, .studio-bill tfoot td { font-weight: 700; border-bottom: 0; border-top: 2px solid var(--line-strong); }
.studio-bill tr.is-out td, .studio-bill tr.is-out th span { color: var(--muted); text-decoration: line-through; }

.studio-switch { display: inline-flex; align-items: center; gap: .45rem; cursor: pointer; }
.studio-switch input, .studio-chip input, .studio-table input[type="checkbox"] { accent-color: var(--brass-deep); width: 1.05rem; height: 1.05rem; }

.studio-groups { border: 0; margin: 0 0 1rem; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.studio-groups legend { font-size: .78rem; font-weight: 600; color: var(--muted); padding: 0; margin-bottom: .4rem; }
.studio-chip { display: inline-flex; align-items: center; gap: .5rem; padding: .35rem .75rem; cursor: pointer;
  border: 1px solid var(--ok-line); border-radius: var(--r-pill); background: var(--ok-bg); font-size: .88rem; }
.studio-chip-total { color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.studio-chip.is-out { background: var(--surface); border-color: var(--line); color: var(--muted); }
.studio-chip.is-out .studio-chip-name { text-decoration: line-through; }

.studio-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.studio-table th, .studio-table td { padding: .45rem .5rem; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: middle; }
.studio-table thead th { font-size: .75rem; font-weight: 600; color: var(--muted); }
.studio-table .studio-total { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }
/* Laid out by room or category (STUDIO_VIEWS), and the Needs options filter:
   the controls above the table, a heading row per section with its total that
   follows the switches, and the open choices named on an item. */
.studio-views { display: flex; flex-wrap: wrap; gap: 0 1.4rem; }
.studio-views .chips { margin-bottom: .6rem; }
.studio-table .studio-section th, .studio-table .studio-section td { background: var(--surface-2); border-bottom: 1px solid var(--line);
  padding-top: .6rem; padding-bottom: .6rem; }
.studio-table .studio-section th { font-size: .95rem; font-weight: 600; color: var(--ink); }
.studio-table .studio-section th .muted { font-weight: 400; font-size: .8rem; margin-left: .4rem; }
.studio-table .studio-section.is-out th, .studio-table .studio-section.is-out td { color: var(--muted); text-decoration: line-through; }
.studio-needs { display: inline-block; margin-left: .3rem; padding: .05rem .5rem; border: 1px solid var(--ok-line); border-radius: var(--r-pill);
  background: var(--ok-bg); color: var(--brass-deep); font-size: .72rem; font-weight: 600; text-decoration: none; white-space: nowrap; }
.studio-needs:hover { text-decoration: underline; }
.studio-table thead th:last-child { text-align: right; }
.studio-th-note { display: block; font-weight: 400; font-size: .68rem; letter-spacing: .02em; text-transform: none; }
.studio-item-id { font-size: .75rem; }
.studio-qty { width: 4.5rem; }
.studio-price { width: 8rem; }
.studio-table input[aria-invalid="true"] { border-color: var(--danger); background: var(--danger-bg); }
.studio-table tr.is-out td { color: var(--muted); }
.studio-table tr.is-out .studio-item-name, .studio-table tr.is-out .studio-total { text-decoration: line-through; }
.studio-table tr.is-changed .studio-total { color: var(--brass-deep); }

/* Presentation: the portal steps back and the figures come forward. */
body:has(.studio.is-present) .topbar,
body:has(.studio.is-present) .projectbar,
body:has(.studio.is-present) .crumbbar,
body:has(.studio.is-present) .preview-bar,
body:has(.studio.is-present) .site-footer { display: none; }
.studio.is-present .studio-hint, .studio.is-present .studio-item-id, .studio.is-present .studio-form .hint { display: none; }
.studio.is-present .studio-figure-value { font-size: 3.4rem; }
.studio.is-present .studio-figure-value.is-quiet { font-size: 2rem; }

@media print {
  .studio-actions, .studio-hint { display: none; }
  .studio-summary { break-inside: avoid; }
  .studio-table input { border: 0; background: none; padding: 0; }
  .studio-table tr.is-out { display: none; }
}
@media (max-width: 640px) {
  .studio-figure-value { font-size: 1.9rem; }
  .studio-bill th, .studio-bill td { padding: .4rem .3rem; }
}

/* ---- responsibilities (views/responsibilities.php, modules/responsibilities.php, D6) ----
 * One chip per RACI letter, read at a glance down a column: Accountable the
 * deepest brass, Responsible brass, Consulted the pale brass tint, Informed
 * an outline. Each scope's items by gate sit under the gate's chart tone as a
 * rule, never as the text's colour (the --viz tones are for marks only). */
.resp-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.resp-title { margin: 0; color: var(--ink-soft); font-size: 1rem; letter-spacing: .02em; }
.resp-chip { display: inline-flex; align-items: center; justify-content: center; width: 1.6rem; height: 1.6rem;
  border-radius: var(--r-pill); font-size: .8rem; font-weight: 700; border: 1px solid transparent; }
.resp-chip.resp-a { background: var(--brass-deep); color: var(--surface); }
.resp-chip.resp-r { background: var(--brass); color: var(--ink); }
.resp-chip.resp-c { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.resp-chip.resp-i { background: var(--surface); color: var(--muted); border-color: var(--line-strong); }
.resp-legend { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem 1.6rem; font-size: .88rem; }
.resp-legend li { display: inline-flex; align-items: center; gap: .45rem; }
/* Under the legend, for admins: where its words are changed (/project/wording). */
.resp-legend-edit { margin: -.4rem 0 1rem; }
.resp-parties { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: .8rem; }
.resp-party { display: flex; flex-direction: column; gap: .2rem; padding: .8rem 1rem; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--surface-2); }
.resp-party-type { font-size: .72rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--brass-deep); }
.resp-party-counts { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .3rem; font-size: .85rem; }
.resp-party-counts > span { display: inline-flex; align-items: center; gap: .3rem; }

.resp-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.resp-table th, .resp-table td { padding: .55rem .6rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; text-align: left; }
.resp-table thead th { font-size: .75rem; font-weight: 600; color: var(--muted); }
.resp-table tbody th { font-family: var(--font-sans); font-size: .88rem; font-weight: 400; text-transform: none; letter-spacing: normal; color: var(--ink); min-width: 14rem; }
.resp-scope { display: block; font-size: .95rem; font-weight: 600; }
.resp-class { display: block; font-size: .78rem; color: var(--brass-deep); }
.resp-notes { display: block; font-size: .8rem; color: var(--muted); }
.resp-word { display: block; font-size: .75rem; color: var(--muted); margin-top: .15rem; }
.resp-gates { display: inline-flex; gap: .2rem; }
.resp-gate { min-width: 1.8rem; text-align: center; padding: .1rem .3rem; font-size: .8rem; font-variant-numeric: tabular-nums;
  border-top: 3px solid var(--line-strong); background: var(--surface-2); color: var(--ink); }
.resp-gate.resp-gate-g1 { border-top-color: var(--viz-g1); }
.resp-gate.resp-gate-g2 { border-top-color: var(--viz-g2); }
.resp-gate.resp-gate-g3 { border-top-color: var(--viz-g3); }
.resp-gate.resp-gate-g4 { border-top-color: var(--viz-g4); }
.resp-gate.is-empty { color: var(--muted); }
.resp-installed { display: block; margin-top: .25rem; font-size: .78rem; color: var(--muted); }
.resp-checks { margin: 0 0 .6rem; padding-left: 1.2rem; }
.resp-checks li { margin-bottom: .3rem; }

.resp-edit { width: 100%; border-collapse: collapse; font-size: .86rem; }
.resp-edit th, .resp-edit td { padding: .4rem .4rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; text-align: left; }
.resp-edit thead th { font-size: .75rem; font-weight: 600; color: var(--muted); }
.resp-edit-scope { display: flex; flex-direction: column; gap: .3rem; min-width: 16rem; }
.resp-edit input[type="text"], .resp-edit select, .resp-edit textarea { padding: .4rem .55rem; min-width: 9rem; }
.resp-edit-scope input[type="text"] { min-width: 16rem; }

/* A party's people (views/responsibilities-edit.php): the project's accounts
 * and contacts to tick, folded under the names already ticked, and a box for
 * anyone the project does not know yet. */
.resp-pick-cell { min-width: 16rem; }
.resp-pick summary { cursor: pointer; color: var(--ink); }
.resp-pick summary:hover { color: var(--brass-deep); }
.resp-pick-list { list-style: none; margin: .4rem 0 .5rem; padding: 0; display: grid; gap: .25rem; max-height: 16rem; overflow-y: auto; }
.resp-pick-list label { display: flex; flex-wrap: wrap; align-items: baseline; gap: .1rem .4rem; cursor: pointer; }
.resp-pick-name { color: var(--ink); }
.resp-pick-other { display: flex; flex-direction: column; gap: .2rem; margin-top: .35rem; font-size: .75rem; color: var(--muted); }

.resp-scope { color: var(--ink); text-decoration: none; }
a.resp-scope:hover { color: var(--brass-deep); text-decoration: underline; }

/* Steps (phase 2): one dot per step, in order, in its gate's chart tone, a
 * mark and never text. Filled when done; a thick ring when it is next; a
 * dashed terracotta ring while it waits on another scope; a faint ring later. */
.resp-track { display: inline-flex; gap: .25rem; align-items: center; margin-bottom: .3rem; }
.resp-dot { display: inline-block; width: .8rem; height: .8rem; border-radius: var(--r-pill);
  border: 2px solid var(--line-strong); background: var(--surface-2); flex: none; }
.resp-dot.resp-dot-g1 { border-color: var(--viz-g1); }
.resp-dot.resp-dot-g2 { border-color: var(--viz-g2); }
.resp-dot.resp-dot-g3 { border-color: var(--viz-g3); }
.resp-dot.resp-dot-g4 { border-color: var(--viz-g4); }
.resp-dot.is-done.resp-dot-g1 { background: var(--viz-g1); }
.resp-dot.is-done.resp-dot-g2 { background: var(--viz-g2); }
.resp-dot.is-done.resp-dot-g3 { background: var(--viz-g3); }
.resp-dot.is-done.resp-dot-g4 { background: var(--viz-g4); }
.resp-dot.is-next { border-width: 4px; }
.resp-dot.is-waiting { border-style: dashed; border-color: var(--danger); }
.resp-dot.is-later { opacity: .45; }
.resp-now { display: block; font-size: .82rem; font-weight: 600; color: var(--ink); }
.resp-now.is-done { color: var(--ok); }
.resp-now.is-waiting { color: var(--danger); }
.resp-wait { display: block; font-size: .78rem; color: var(--danger); }
.resp-wait.is-met { color: var(--muted); }

/* A card per party on a matrix (views/_project_cards.php, D6): who they are
 * and what they hold, who sees their steps, how far the matrix has come, and
 * their next steps as links to the work. Equal heights in a grid, actions
 * pinned to the foot, one column on a phone. */
.rc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(17.5rem, 1fr)); gap: 1.1rem; margin-bottom: 1.4rem; }
.rc {
  display: flex; flex-direction: column; gap: .9rem; min-width: 0;
  background: var(--surface); border: 1px solid var(--line); border-top: 3px solid var(--brass);
  border-radius: var(--r-card); padding: 1.15rem 1.2rem 1.1rem; box-shadow: var(--shadow-card);
}
.rc.is-unseen { border-top-color: var(--line-strong); }
.rc.is-late { border-top-color: var(--danger); }
.rc-head { display: flex; flex-direction: column; gap: .3rem; }
.rc-kind { margin: 0; font-family: var(--font-mono); font-size: .62rem; font-weight: 500; letter-spacing: .2em;
  text-transform: uppercase; color: var(--brass-deep); }
.rc-title { margin: 0; font-size: 1.12rem; line-height: 1.25; overflow-wrap: anywhere; }
.rc-title a { color: var(--ink); text-decoration: none; }
.rc-title a:hover { color: var(--brass-deep); text-decoration: underline; }
.rc-people { margin: 0; font-size: .86rem; color: var(--ink-soft); }
.rc-letters { list-style: none; margin: .2rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .35rem .45rem; }
.rc-letters > li { display: inline-flex; align-items: center; gap: .35rem; padding: .15rem .55rem .15rem .2rem;
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface-2); font-size: .78rem; color: var(--ink-soft); }
.rc-letters > li.rc-as { padding-left: .55rem; }
.rc-letters .resp-chip { width: 1.35rem; height: 1.35rem; font-size: .7rem; }
.rc-n { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

.rc-reach { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .6rem; font-size: .84rem;
  color: var(--ink-soft); padding: .5rem .65rem; border-radius: var(--r-sm); background: var(--surface-2); }
.rc-reach.is-warn { color: var(--danger); background: var(--danger-bg); border: 1px solid var(--danger-line); }
.rc-reach a { font-weight: 600; overflow-wrap: anywhere; }
.rc-reach-more { color: var(--muted); }

.rc-progress { display: flex; flex-direction: column; gap: .3rem; }
.rc-meter { appearance: none; -webkit-appearance: none; width: 100%; height: .45rem; border: 0; border-radius: var(--r-pill);
  background: var(--line-soft); overflow: hidden; }
.rc-meter::-webkit-progress-bar { background: var(--line-soft); border-radius: var(--r-pill); }
.rc-meter::-webkit-progress-value { background: var(--brass); border-radius: var(--r-pill); }
.rc-meter::-moz-progress-bar { background: var(--brass); border-radius: var(--r-pill); }
.rc-progress-words { margin: 0; font-size: .8rem; color: var(--muted); }
.rc-progress-words strong { color: var(--ink); font-variant-numeric: tabular-nums; }

.rc-counts { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(4.6rem, 1fr));
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.rc-counts > li { display: flex; flex-direction: column; gap: .1rem; padding: .5rem .65rem; font-size: .72rem; line-height: 1.25;
  color: var(--muted); border-left: 1px solid var(--line-soft); }
.rc-counts > li:first-child { border-left: 0; }
.rc-counts strong { font-size: 1.35rem; font-weight: 600; line-height: 1.1; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.rc-counts > li.is-late a { display: flex; flex-direction: column; gap: .1rem; }
.rc-counts > li.is-on { color: var(--ink-soft); background: var(--surface-2); }
.rc-counts > li.is-on strong { color: var(--brass-deep); }
.rc-counts > li.is-late { background: var(--danger-bg); }
.rc-counts > li.is-late a, .rc-counts > li.is-late strong { color: var(--danger); text-decoration: none; }
.rc-counts > li.is-late a:hover { text-decoration: underline; }

.rc-next { display: flex; flex-direction: column; gap: .45rem; padding-top: .8rem; border-top: 1px solid var(--line-soft); }
.rc-next-head { margin: 0; font-family: var(--font-mono); font-size: .62rem; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
.rc-next-none { margin: 0; font-size: .88rem; color: var(--ink-soft); }
.rc-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .4rem; }
.rc-step { display: flex; flex-direction: column; gap: .1rem; padding-left: .7rem; border-left: 2px solid var(--brass); }
.rc-step.is-waiting { border-left-color: var(--line-strong); }
.rc-step.is-late { border-left-color: var(--danger); }
.rc-step a { display: flex; flex-direction: column; text-decoration: none; color: var(--ink); }
.rc-step a:hover .rc-step-label { text-decoration: underline; }
.rc-step-scope { font-size: .76rem; color: var(--muted); overflow-wrap: anywhere; }
.rc-step-label { font-size: .9rem; font-weight: 600; color: var(--brass-deep); }
.rc-step-meta { font-size: .76rem; color: var(--muted); }
.rc-step.is-late .rc-step-meta { color: var(--danger); font-weight: 600; }
.rc-more { font-size: .82rem; }

.rc-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .9rem; margin-top: auto; padding-top: .3rem; }
.rc-edit { font-size: .82rem; }

/* The matrix's history and its page to print and sign (D6 phase 4). */
.resp-changes { list-style: none; margin: 0; padding: 0; }
.resp-changes > li { padding: .55rem 0; border-bottom: 1px solid var(--line-soft); font-size: .88rem; }
.resp-changes ul { margin: .25rem 0 0; padding-left: 1.1rem; }
.resp-print-table { margin: 1rem 0 1.6rem; }
.resp-signatures { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1.4rem; }
.resp-signature { display: flex; flex-direction: column; gap: 1.4rem; break-inside: avoid; }
.resp-sign-line { padding-top: .25rem; border-top: 1px solid var(--ink); font-size: .72rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
/* The lines start at the same height in every party's block, however long its people. */
.resp-signature-who { display: flex; flex-direction: column; gap: .3rem; }
.resp-signature > .resp-sign-first { margin-top: auto; }
/* The project's name over the title is for the paper: on screen the
   eyebrow above the buttons already says it. */
@media screen { .resp-print-only { display: none; } }
.resp-print > .panel-head { margin-bottom: 1.2rem; }

/* On a phone the table would push the page sideways: each scope becomes a
   card, its parties' letters as label and chip, the parties without one left
   out. Screens only: on paper it stays the table that gets signed. */
.resp-print-table .resp-word { display: none; }
@media screen and (max-width: 720px) {
  .resp-print-table, .resp-print-table tbody, .resp-print-table tr,
  .resp-print-table th, .resp-print-table td { display: block; width: auto; }
  .resp-print-table thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
  .resp-print-table tr {
    background: var(--surface-2); border: 1px solid var(--line);
    border-radius: var(--r-sm); padding: .8rem 1rem .9rem; margin-bottom: .8rem;
  }
  .resp-print-table th, .resp-print-table td { border: 0; padding: .2rem 0; }
  .resp-print-table th[scope="row"] { margin-bottom: .45rem; padding-bottom: .5rem; border-bottom: 1px solid var(--line-soft); }
  .resp-print-table td[data-label].is-empty { display: none; }
  .resp-print-table td[data-label] { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
  .resp-print-table td[data-label]::before {
    content: attr(data-label);
    font-family: var(--font-mono); font-size: .6rem; font-weight: 500;
    letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  }
  .resp-print-table td[data-label] > span:first-child { margin-left: auto; }
  .resp-print-table .resp-word { display: inline; font-size: .82rem; color: var(--ink-soft); }
  .resp-print-table .resp-print-now { margin-top: .45rem; padding-top: .5rem; border-top: 1px solid var(--line-soft); }
}
@media print {
  .resp-print-table tr { break-inside: avoid; }
  .resp-print .resp-chip { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}

/* Due dates and lateness (D6 phase 4): the date quietly, a late step in the
   warm warning tone, wherever a step shows. */
.resp-due { display: block; font-size: .78rem; color: var(--muted); }
.resp-late { display: block; font-size: .78rem; font-weight: 600; color: var(--danger); }
.resp-overdue { margin: 0; padding-left: 1.1rem; }
.resp-overdue li { margin-bottom: .45rem; }
.resp-due-form { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem; font-size: .82rem; }
.resp-due-form input[type="date"] { width: auto; }

.resp-next { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .8rem; }
.resp-next-party { padding: .8rem 1rem; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); font-size: .88rem; }
.resp-next-party ul { margin: .4rem 0 0; padding-left: 1.1rem; }
.resp-next-party li { margin-bottom: .35rem; }
.resp-next-party li.resp-next-waiting { color: var(--ink-soft); }

.resp-steps { list-style: none; margin: 0; padding: 0; }
.resp-step { display: flex; gap: .8rem; padding: .8rem 0; border-bottom: 1px solid var(--line-soft); }
.resp-step .resp-dot { margin-top: .3rem; }
.resp-step-body { display: flex; flex-direction: column; gap: .25rem; flex: 1; }
.resp-step-head { display: flex; flex-wrap: wrap; gap: .3rem .7rem; align-items: baseline; }
.resp-step.is-later .resp-step-head strong { color: var(--ink-soft); }
.resp-state { font-size: .72rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: .05rem .5rem;
  border-radius: var(--r-pill); border: 1px solid var(--line); color: var(--muted); }
.resp-state.is-done { color: var(--ok); border-color: var(--ok-line); background: var(--ok-bg); }
.resp-state.is-next { color: var(--surface); background: var(--brass-deep); border-color: var(--brass-deep); }
.resp-state.is-waiting { color: var(--danger); border-color: var(--danger-line); background: var(--danger-bg); }
.resp-step-who { font-size: .85rem; color: var(--ink-soft); }
.resp-step-note { font-size: .82rem; color: var(--muted); }
.resp-inline { display: inline; }
.resp-tick { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-top: .3rem; }
.resp-tick input[type="text"] { flex: 1; min-width: 12rem; padding: .45rem .6rem; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-radius: var(--r-input); font: inherit; color: var(--ink); }

/* My Tasks (views/_tasks.php, phase 3): one row per step owed, its dot as on
 * the matrix, what and where, and Mark done at the end when it is ticked by hand. */
.task-list { list-style: none; margin: 0; padding: 0; }
.task { display: flex; gap: .8rem; align-items: flex-start; padding: .7rem 0; border-bottom: 1px solid var(--line-soft); }
.task:last-child { border-bottom: 0; }
.task .resp-dot { margin-top: .3rem; }
.task-body { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-width: 0; }
.task-what { font-weight: 600; color: var(--ink); text-decoration: none; }
a.task-what:hover { color: var(--brass-deep); text-decoration: underline; }
.task-meta { font-size: .8rem; color: var(--muted); }
.task-tick { flex: none; }
.tasks-due { border-left: 3px solid var(--brass); }

@media print {
  .resp-actions { display: none; }
  .resp-table { font-size: .8rem; }
}

/* Wording (views/wording.php): a project's own names, one row per name. The
   built-in words show under what the row is, and faintly in an empty box. */
.wording-jump { list-style: none; margin: 1rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; font-size: .88rem; }
.wording-jump .badge { margin-left: .25rem; }
.wording-list { list-style: none; margin: 0 0 1rem; padding: 0; }
.wording-row { display: grid; grid-template-columns: minmax(0, 16rem) minmax(0, 1fr) auto; align-items: center; gap: .4rem 1rem;
  padding: .55rem 0; border-bottom: 1px solid var(--line-soft); }
.wording-row:last-child { border-bottom: 0; }
.wording-row label { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.wording-what { font-family: var(--font-mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.wording-default { font-size: .9rem; color: var(--ink-soft); overflow-wrap: anywhere; }
.wording-row.is-changed .wording-default { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--line-strong); }
.wording-row input::placeholder { color: var(--muted); opacity: .7; }
.wording-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; }
.wording-scopes { margin: 0; padding-left: 1.1rem; display: grid; gap: .3rem; font-size: .9rem; }
.wording-scopes .muted { display: block; font-size: .82rem; }
@media (max-width: 600px) {
  .wording-row { grid-template-columns: minmax(0, 1fr) auto; }
  .wording-row label { grid-column: 1 / -1; }
}

/* ---- Estimate preview (views/_estimate_doc.php): the client estimate as a document ---- */
.estimate-doc { max-width: 56rem; }
.estimate-head { display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; align-items: flex-start;
  border-bottom: 1px solid var(--line); padding-bottom: 1rem; margin-bottom: 1rem; }
.estimate-meta { display: grid; gap: .4rem; margin: 0; min-width: 14rem; }
.estimate-meta div { display: grid; grid-template-columns: 5rem 1fr; gap: .5rem; }
.estimate-meta dt { color: var(--muted); font-size: .8rem; text-transform: uppercase; letter-spacing: .06em; }
.estimate-meta dd { margin: 0; }
.estimate-table th.num, .estimate-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.estimate-room th { text-align: left; padding-top: 1rem; font-family: var(--font-serif); font-size: 1.05rem; color: var(--ink); }
.estimate-product { display: block; font-weight: 500; }
.estimate-ref, .estimate-spec { display: block; font-size: .82rem; }
.estimate-spec { color: var(--ink-soft); }
.estimate-table tfoot th { text-align: right; font-weight: 400; color: var(--ink-soft); }
/* The totals read as one block: a rule above it and above each total, none between the charges. */
.estimate-table tfoot th, .estimate-table tfoot td { border-top: none; border-bottom: none; padding-top: .35rem; padding-bottom: .35rem; }
.estimate-table tfoot tr:first-child th, .estimate-table tfoot tr:first-child td { border-top: 2px solid var(--line-strong); padding-top: .7rem; }
.estimate-table tfoot .estimate-subtotal th, .estimate-table tfoot .estimate-subtotal td,
.estimate-table tfoot .estimate-total th, .estimate-table tfoot .estimate-total td { font-weight: 600; color: var(--ink);
  border-top: 1px solid var(--line-strong); padding-top: .6rem; }
.estimate-foot { margin-top: 1rem; }
.estimate-shares td.num, .estimate-shares th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Estimate › Create and update: each write under its own dry run, ruled apart. */
.estimate-write { border-top: 1px solid var(--line-soft); margin-top: 1.1rem; padding-top: 1rem; }
.estimate-write h3 { margin-bottom: .3rem; }
@media print { .estimate-doc { max-width: none; } }

/* ---- Vendor reply (views/vendor-packet.php) and its review (views/packet-detail.php) ---- */
.vendor-table th.num, .vendor-table td.num, .reply-table th.num, .reply-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vendor-table .mono, .reply-table .mono, .vendor-item .mono { font-family: var(--font-mono); font-size: .8rem; }
.vendor-spec { white-space: pre-wrap; }
.vendor-items { display: grid; gap: 1rem; }
.vendor-item { border: 1px solid var(--line); border-radius: var(--r-card); padding: 1rem 1.1rem .4rem; margin: 0; background: var(--surface); }
.vendor-item legend { padding: 0 .4rem; display: flex; flex-wrap: wrap; gap: .3rem .6rem; align-items: baseline; }
.vendor-item-product { font-weight: 500; color: var(--ink); }
.vendor-item-response { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; margin: .4rem 0 .8rem; }
.vendor-item-response label { display: inline-flex; gap: .45rem; align-items: center; cursor: pointer; }
.vendor-item-fields, .vendor-alt-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0 1rem; }
.vendor-item-fields .field-wide, .vendor-alt-fields .field-wide { grid-column: 1 / -1; }
.vendor-alt { margin: .2rem 0 .8rem; }
.vendor-alt summary { cursor: pointer; color: var(--brass-deep); font-size: .9rem; margin-bottom: .4rem; }
.vendor-alt-fields { border-top: 1px dashed var(--line); padding-top: .8rem; margin-top: .4rem; }
.reply-cannot { color: var(--danger); font-weight: 500; }

/* ---- A vendor's own page (views/vendor-workspace.php) and Vendor pages for staff (views/vendor-portal.php), D15 ----
   Phone first: one card per item, what is owed at its top in terracotta, one form per item behind a disclosure. */
.vw-hero h1 { margin-bottom: .1rem; }
.vw-project { margin: 0 0 .6rem; font-family: var(--font-serif); font-size: 1.25rem; color: var(--ink-soft); }
.vw-stats { margin-top: .9rem; }
.vw-stat--due .brief-stat-n { color: var(--danger); }
.vw-section { margin: 1.4rem 0; }
.vw-section > h2 { margin-bottom: .2rem; }
.vw-cards, .vw-vendors { display: grid; gap: .9rem; margin-top: .8rem; }
.vw-vendors { grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr)); }
.vw-card, .vw-vendor { border: 1px solid var(--line); border-left: 3px solid var(--line-strong); border-radius: var(--r-card);
  padding: .9rem 1rem .6rem; background: var(--surface); min-width: 0; }
.vw-card--due { border-left-color: var(--danger); }
.vw-vendor.is-selected { border-left-color: var(--brass); background: var(--surface-2); }
/* The card's own actions: small pills that wrap on a phone, never one wide box each. */
.vw-vendor-head .btn, .vw-vendor .inline-actions .btn { padding: .4rem .85rem; font-size: .62rem; }
.vw-vendor .inline-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin: .5rem 0 .2rem; }
.vw-card:target { box-shadow: 0 0 0 2px var(--brass); }
.vw-card-head, .vw-vendor-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .7rem; }
.vw-card-head .mono-sm { color: var(--muted); flex-basis: 100%; }
.vw-card-title { margin: 0; font-family: var(--font-sans); text-transform: none; letter-spacing: 0; font-size: 1.05rem; font-weight: 600; flex: 1 1 14rem; min-width: 0; overflow-wrap: anywhere; }
.vw-card-price { font-variant-numeric: tabular-nums; color: var(--ink-soft); white-space: nowrap; }
.vw-due { margin: .6rem 0 .4rem; padding: .45rem .7rem; border-radius: var(--r-sm); background: var(--danger-bg);
  border: 1px solid var(--danger-line); color: var(--danger); font-weight: 500; }
.vw-due-inline { color: var(--danger); font-size: .88rem; }
.vw-answered { margin: .6rem 0 .4rem; color: var(--ink-soft); }
.vw-note { margin: .3rem 0 .6rem; color: var(--ink-soft); font-style: italic; }
.vw-facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr)); gap: .35rem 1rem; margin: .6rem 0; }
.vw-facts div { min-width: 0; }
.vw-vendor .vw-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.vw-facts dt { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.vw-facts dd { margin: 0; overflow-wrap: anywhere; }
.vw-form { margin: .4rem 0 .3rem; }
.vw-form > summary { cursor: pointer; color: var(--brass-deep); font-weight: 500; padding: .35rem 0; }
.vw-form[open] > summary { margin-bottom: .5rem; }
.vw-form .check-row { margin-top: .5rem; font-weight: 500; }
.vw-ladder { display: flex; list-style: none; padding: 0; margin: .7rem 0 .3rem; gap: 3px; counter-reset: vw; }
.vw-ladder li { flex: 1 1 0; min-width: 0; padding-top: .45rem; border-top: 4px solid var(--line); font-size: .7rem;
  color: var(--muted); line-height: 1.25; overflow-wrap: anywhere; }
.vw-ladder li.is-done { border-top-color: var(--viz-g2); color: var(--ink-soft); }
.vw-ladder li.is-now { border-top-color: var(--viz-g3); color: var(--ink); font-weight: 600; }
.vw-done { margin: .5rem 0 0; padding-left: 1.1rem; }
.vw-done li { margin: .25rem 0; }
.vw-inline-h { display: inline; font-size: 1.05rem; margin: 0; }
details.panel > summary { cursor: pointer; }
.vw-ack { margin: .9rem 0; }
/* Staff's preview of a vendor's page: one disabled fieldset around it, with no fieldset look of its own. */
.vw-preview { border: 0; padding: 0; margin: 0; min-width: 0; }
.vw-preview-note a { margin-left: .4rem; }
.vw-preview :disabled { opacity: .55; cursor: not-allowed; }
.vw-ack .vendor-item-fields { margin-top: .4rem; }
.vw-talk > summary .badge { margin-left: .4rem; vertical-align: middle; }
/* One conversation (views/_vendor_thread.php): the reader's own messages to the right, SHERPA's on cream. */
.vw-thread { list-style: none; margin: .4rem 0 .8rem; padding: 0; display: grid; gap: .5rem; }
.vw-msg { max-width: min(36rem, 92%); padding: .5rem .75rem; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); }
.vw-msg--staff { background: var(--ok-bg); border-color: var(--ok-line); }
.vw-msg.is-mine { justify-self: end; }
.vw-msg-meta { margin: 0 0 .15rem; font-family: var(--font-mono); font-size: .6rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); }
.vw-msg-body { margin: 0; overflow-wrap: anywhere; }
.vw-docs { margin: .4rem 0 .6rem; padding-left: 1.1rem; }
.vw-docs li { margin: .2rem 0; overflow-wrap: anywhere; }
.vw-upload { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 0 1rem; align-items: end;
  border-top: 1px dashed var(--line); padding-top: .6rem; margin-top: .4rem; }
.vw-upload .edit-actions { grid-column: 1 / -1; }
@media (max-width: 520px) {
  .vw-ladder li { font-size: .62rem; }
  .vw-card, .vw-vendor { padding: .8rem .8rem .5rem; }
}
.reply-alt { margin: .3rem 0 0; padding-left: 1rem; }
.reply-alt li { margin-bottom: .35rem; }
.reply-apply td:first-child { width: 2rem; }
.reply-outcomes { margin: .4rem 0 0; padding-left: 1.1rem; }
/* A checkbox with its words on one row, inside a form (the vendor link's "Email it now", accepting an estimate). */
.check-row { display: flex; gap: .5rem; align-items: center; margin: 0 0 1rem; cursor: pointer; }
/* A tick box inside a .field reads as a tick box with its words beside it, never as a stretched box under a
   field label (the estimate page's client accounts and the allowance form showed it so, 30 October 2026). */
.field label.check-row { display: flex; font-family: inherit; font-size: .95rem; letter-spacing: normal; text-transform: none;
  color: var(--ink); margin: 0 0 .5rem; }
.field input[type="checkbox"], .field input[type="radio"] { width: auto; padding: 0; flex: none; accent-color: var(--brass-deep); }

/* ---- Enrichment review (views/_enrich_review.php): the page's reading beside the item, ticked to take ---- */
.enrich-table td { vertical-align: top; }
.enrich-table .enrich-value { max-width: 26rem; overflow-wrap: break-word; }
.enrich-text { white-space: pre-wrap; max-height: 9rem; overflow: auto; }
.enrich-thumb { display: block; max-width: 8rem; max-height: 8rem; border: 1px solid var(--line); border-radius: var(--r-input); margin-bottom: .3rem; }
.enrich-url { display: block; font-family: var(--font-mono); font-size: .75rem; color: var(--muted); overflow-wrap: anywhere; }
.enrich-list { margin: .3rem 0 0; padding-left: 1rem; font-family: var(--font-mono); font-size: .75rem; max-height: 10rem; overflow: auto; overflow-wrap: anywhere; }
.enrich-state { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .25rem; }
.enrich-same td { color: var(--muted); }
.enrich-flagged td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
/* The page's value in a box, to correct before taking it. */
.enrich-input { width: 100%; min-width: 12rem; font: inherit; }
/* A secret shown once to copy (views/extension.php): wraps rather than overflowing. */
.key-once { display: block; overflow-wrap: anywhere; user-select: all; padding: .5rem .6rem; border: 1px dashed var(--line); border-radius: var(--r-input); }
/* A phone: each detail stacks, its two values under it, each named. */
@media (max-width: 640px) {
  .enrich-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .enrich-table tr { display: grid; grid-template-columns: 2rem 1fr; border-bottom: 1px solid var(--line); padding: .5rem 0; }
  .enrich-table td { display: block; border: 0; padding: .15rem .3rem; }
  .enrich-table td.enrich-value { grid-column: 2; max-width: none; }
  .enrich-table td.enrich-value::before { content: attr(data-label); display: block; font-size: .7rem; color: var(--muted);
    text-transform: uppercase; letter-spacing: .06em; }
}

/* ---- phones: the item page and the trail (29 October 2026) -----------
 * A label column of 12rem in a 343px panel left the values a third of the
 * width; the trail wrapped a long project name over three lines; the
 * offer-a-choice rows pushed the whole page sideways. */
@media (max-width: 560px) {
  table.kv th { width: 38%; padding-left: 0; font-size: .82rem; }
  table.kv td { overflow-wrap: anywhere; }
  .crumbs { flex-wrap: nowrap; min-width: 0; overflow: hidden; white-space: nowrap; }
  .crumbs > a, .crumbs > span[aria-current] { min-width: 0; max-width: 11rem; overflow: hidden; text-overflow: ellipsis; }
  .crumbs > .crumb-sep { flex: 0 0 auto; }
  .crumbbar { flex-wrap: nowrap; align-items: center; }
  .crumbbar-help { font-size: 0; }
  .crumbbar-help .help-i { font-size: .78rem; margin-right: 0; }
  .item-actions .btn { flex: 1 1 auto; }
  .item-actions-remove { flex-basis: 100%; margin-left: 0; text-align: right; }
}
@media (max-width: 760px) {
  .choice-add-rows, .choice-add-rows thead, .choice-add-rows tbody, .choice-add-rows tr, .choice-add-rows td { display: block; width: 100%; }
  .choice-add-rows thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .choice-add-rows tr { padding: .6rem 0; border-bottom: 1px solid var(--line-soft); }
  .choice-add-rows td { padding: 0 0 .35rem; }
  .choice-add-rows td::before { content: attr(data-label); display: block; font-size: .75rem; font-weight: 600; color: var(--muted); }
  .choice-add-rows input { min-width: 0; }
}

/* ---- Overview › People (modules/people.php, views/_people.php): each person, their status in words, the next action. ---- */
.people-counts { display: flex; flex-wrap: wrap; gap: .4rem; margin: .6rem 0 1rem; }
.people-list { list-style: none; margin: 0; padding: 0; }
.people-row { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.2fr) auto; gap: .4rem 1rem; align-items: center;
  padding: .7rem 0; border-top: 1px solid var(--line-soft); }
.people-who, .people-status { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.people-who .muted { font-size: .85rem; overflow-wrap: anywhere; }
.people-status { flex-direction: row; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; }
.people-status .hint { margin: 0; flex-basis: 100%; }
.people-action { justify-self: end; }
.people-action .inline-form { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0; }
.people-action select { width: auto; }
@media (max-width: 760px) {
  .people-row { grid-template-columns: minmax(0, 1fr); }
  .people-action { justify-self: start; }
}

/* ---- New project: "Before you begin" (modules/new_project_before.php) and Check before creating ---- */
.np-before { list-style: none; margin: .4rem 0 1.2rem; padding: 0; display: grid; gap: .7rem; counter-reset: npb; }
.np-before > li { border: 1px solid var(--line); border-radius: var(--r-sm); padding: .65rem .8rem; background: var(--surface-2); min-width: 0; break-inside: avoid; }
.np-before-item { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .6rem; margin: 0 0 .35rem; }
.np-before-box { width: .95rem; height: .95rem; border: 1.5px solid var(--brass-deep); border-radius: 3px; flex: none; }
.np-before-dl { display: grid; gap: .3rem; margin: 0; font-size: .88rem; }
.np-before-dl dt { font-size: .72rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.np-before-dl dd { margin: 0; overflow-wrap: anywhere; }
.np-before-more { margin: .35rem 0 0; font-size: .82rem; }
.np-before-tools { display: flex; flex-wrap: wrap; gap: .5rem; margin: .2rem 0 .8rem; }
.np-check-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.np-check-actions .hint { margin: 0; }
.np-check-actions form { margin: 0; }
/* The hub connection on New project: a badge in the status palette, its dot the badge's own colour. */
.np-hub-badge { display: inline-flex; align-items: center; gap: .4rem; font-weight: 600; }
.np-hub-dot { width: .55rem; height: .55rem; border-radius: 50%; background: currentColor; flex: none; }
/*
 * NEW PROJECT, the form (3 November 2026, Jeff: "easier to use and best-in-class"): the form on the left, what helps
 * beside it on the right (the hub, Before you begin, what Create does). Where the person stands runs across the top;
 * the form is two numbered sections on a six-column grid; Check and Create stay in reach at the bottom of the view.
 */
.np-layout { display: grid; gap: 1.4rem; align-items: start; }
.np-layout > .panel, .np-aside .panel { margin-bottom: 0; }
.np-main { min-width: 0; }
.np-aside { display: grid; gap: 1rem; min-width: 0; }
@media (min-width: 64em) {
  .np-layout { grid-template-columns: minmax(0, 1fr) 19rem; }
  .np-aside { position: sticky; top: 1rem; }
}
.np-lede { margin: .4rem 0 0; max-width: 46rem; color: var(--ink-soft); }
.np-steps { list-style: none; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .5rem;
  margin: 1.3rem 0 1.2rem; padding: 0; counter-reset: none; }
.np-steps li { display: flex; align-items: center; gap: .55rem; padding: .55rem .7rem; min-width: 0;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); color: var(--muted); font-size: .85rem; }
.np-steps li > span:last-child { min-width: 0; overflow-wrap: anywhere; }
.np-step-n { flex: none; display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem; border-radius: 50%;
  border: 1px solid var(--line-strong); font-family: var(--font-mono); font-size: .72rem; color: var(--muted); }
.np-steps li.is-done { color: var(--ink-soft); }
.np-steps li.is-done .np-step-n { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.np-steps li.is-current { border-color: var(--brass); color: var(--ink); font-weight: 600; box-shadow: inset 0 -2px 0 var(--brass); }
.np-steps li.is-current .np-step-n { background: var(--brass); border-color: var(--brass); color: var(--chrome); }
.np-form { margin: 0; }
.np-section { border: 1px solid var(--line); border-radius: var(--r-sm); margin: 0 0 1rem; padding: 1rem 1.1rem .2rem; min-width: 0; }
.np-section > legend { display: flex; align-items: center; gap: .55rem; padding: 0 .4rem; font-weight: 600; color: var(--ink); }
.np-section-n { display: inline-grid; place-items: center; width: 1.45rem; height: 1.45rem; border-radius: 50%;
  background: var(--chrome); color: var(--chrome-ink); font-family: var(--font-mono); font-size: .68rem; }
.np-results { padding-top: 1rem; padding-bottom: .4rem; scroll-margin-top: 1rem; }
.np-results .alert { margin-top: 0; }
.np-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 0 1rem; }
.np-grid > .field { min-width: 0; }
.np-span-1 { grid-column: span 1; } .np-span-2 { grid-column: span 2; } .np-span-3 { grid-column: span 3; }
.np-span-4 { grid-column: span 4; } .np-span-6 { grid-column: 1 / -1; }
@media (max-width: 52em) {
  .np-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .np-span-1 { grid-column: span 1; } .np-span-2, .np-span-3, .np-span-4 { grid-column: 1 / -1; }
  .np-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.np-req { margin-left: .35rem; color: var(--brass-deep); letter-spacing: .08em; }
.np-code { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: .12em; }
.np-code::placeholder { letter-spacing: .12em; }
.np-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem;
  margin: 0 -1.6rem -1.5rem; padding: .9rem 1.6rem; background: var(--surface); border-top: 1px solid var(--line);
  border-radius: 0 0 var(--r-card) var(--r-card); }
.np-actions .hint { margin: 0; flex: 1 1 14rem; }
@media (max-width: 860px) { .np-actions { margin: 0 -1.15rem -1.2rem; padding: .8rem 1.15rem; } }
/* Kept in reach while the form scrolls, where the screen has room for it. */
@media (min-width: 52em) { .np-actions { position: sticky; bottom: 0; z-index: 2; } }
.np-actions button[disabled] { opacity: .45; cursor: not-allowed; }
.np-actions button[disabled]:hover { background: var(--brass); border-color: var(--brass); }
.np-card { padding: 1.1rem 1.2rem; }
.np-card > :last-child { margin-bottom: 0; }
.np-card form { margin: 0 0 .4rem; }
.np-card-title { font-family: var(--font-mono); font-size: .66rem; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 .7rem; }
.np-card-list { margin: 0 0 .6rem; padding-left: 1.2rem; display: grid; gap: .35rem; font-size: .9rem; }
.np-card-links { font-size: .85rem; }
@media print {
  .np-before-tools, .np-print-hide, .np-aside, .np-actions { display: none; }
  .np-before > li { background: none; }
}

/* ============================ 21. WALKTHROUGH ========================== */
/* The 3D stage (modules/walkthroughs.php, D10; public/assets/walkthrough.js).
   The stage is a fixed-height panel the model fills; its HUD floats over it in
   the portal's chrome. Everything is tokens: no colour is named here. The
   model's own clay palette lives in walkthrough.js and is not chrome
   (docs/DESIGN_SYSTEM.md). */
.wt-head .eyebrow a { color: var(--brass-deep); text-decoration: none; }
.wt-facts { display: flex; gap: 1rem; flex-wrap: wrap; font-variant-numeric: tabular-nums; }
.wt-stage { position: relative; height: min(72vh, 760px); min-height: 380px; margin: 1rem 0; border-radius: var(--r-card); overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-card); -webkit-user-select: none; user-select: none; }
.wt-stage--sample { height: 220px; min-height: 0; }
.wt-stage:fullscreen { height: 100%; margin: 0; border-radius: 0; border: 0; }
.wt-stage canvas#wt-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; outline: none; cursor: grab; }
.wt-stage canvas#wt-canvas:focus-visible { outline: 2px solid var(--brass); outline-offset: -2px; }
.wt-stage canvas.wt-walkto { cursor: crosshair; }
.wt-note[hidden] { display: none; }
.wt-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; padding: 1rem 2rem; color: var(--muted); font-size: .95rem; }
.wt-hud { position: absolute; inset: 0; pointer-events: none; font-family: var(--font-sans); font-size: .85rem; }
.wt-hud > * { pointer-events: auto; }
.wt-top { position: absolute; left: 0; right: 0; top: 0; display: flex; align-items: center; justify-content: space-between; gap: .5rem; padding: .6rem .75rem; pointer-events: none; }
.wt-top > * { pointer-events: auto; }
.wt-seg, .wt-mapwrap, .wt-drawer, .wt-insp, .wt-tour, .wt-where, .wt-toast, .wt-stick {
  background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--line); border-radius: var(--r-input); box-shadow: var(--shadow-pop); }
.wt-seg { display: flex; padding: 3px; gap: 2px; }
.wt-seg button, .wt-ib, .wt-chip, .wt-tab, .wt-sw button, .wt-it, .wt-x { font: inherit; color: inherit; cursor: pointer; }
.wt-seg button { border: 0; background: transparent; padding: .4rem .8rem; border-radius: calc(var(--r-input) - 3px); font-weight: 600; color: var(--muted); }
.wt-seg button[aria-pressed="true"] { background: var(--chrome); color: var(--chrome-ink); }
.wt-seg button:disabled { opacity: .45; cursor: not-allowed; }
.wt-tools { display: flex; gap: .35rem; flex-wrap: wrap; justify-content: flex-end; }
.wt-ib { display: inline-flex; align-items: center; gap: .35rem; border: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 92%, transparent); border-radius: var(--r-input); padding: .4rem .7rem; font-weight: 600; white-space: nowrap; }
.wt-ib[aria-pressed="true"] { background: var(--chrome); color: var(--chrome-ink); border-color: var(--chrome); }
.wt-ib-small { padding: .25rem .55rem; font-size: .8rem; font-weight: 500; }
.wt-seg button:focus-visible, .wt-ib:focus-visible, .wt-chip:focus-visible, .wt-tab:focus-visible, .wt-it:focus-visible, .wt-sw button:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
.wt-mapwrap { position: absolute; right: .75rem; top: 3.4rem; padding: 5px; }
.wt-mapwrap canvas { display: block; cursor: crosshair; }
.wt-toast { position: absolute; left: 50%; top: 3.5rem; transform: translateX(-50%); padding: .4rem .9rem; border-radius: var(--r-pill); font-weight: 600; opacity: 0; transition: opacity .25s; pointer-events: none; white-space: nowrap; max-width: 92%; overflow: hidden; text-overflow: ellipsis; }
.wt-toast.on { opacity: 1; }
.wt-drawer { position: absolute; top: 3.4rem; right: .75rem; bottom: .75rem; width: min(340px, calc(100% - 1.5rem)); display: none; flex-direction: column; overflow: hidden; }
.wt-drawer.on { display: flex; }
.wt-tabs { display: flex; border-bottom: 1px solid var(--line); }
.wt-tab { flex: 1; border: 0; background: transparent; padding: .7rem; font-weight: 700; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.wt-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--brass); }
.wt-pane { display: none; flex-direction: column; gap: .55rem; overflow: auto; padding: .75rem .85rem 1rem; min-height: 0; }
.wt-pane.on { display: flex; }
.wt-pane h4 { margin: .5rem 0 .1rem; font-family: var(--font-mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); font-weight: 500; }
.wt-pane .hint { margin: 0; }
.wt-pane kbd { font-family: var(--font-mono); font-size: .72rem; border: 1px solid var(--line); border-radius: 4px; padding: 0 .3em; margin-right: .1em; }
.wt-row { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.wt-row label { font-weight: 600; }
.wt-row .mono { font-family: var(--font-mono); font-weight: 400; color: var(--muted); font-size: .8rem; margin-left: .3rem; }
.wt-row input[type=range] { width: 140px; accent-color: var(--brass-deep); }
.wt-sw { display: flex; padding: 2px; gap: 2px; border: 1px solid var(--line); border-radius: var(--r-input); }
.wt-sw button { border: 0; background: transparent; padding: .3rem .6rem; border-radius: calc(var(--r-input) - 2px); font-size: .78rem; font-weight: 600; color: var(--muted); }
.wt-sw button[aria-pressed="true"] { background: var(--chrome); color: var(--chrome-ink); }
.wt-it { display: grid; grid-template-columns: 1fr auto; gap: .1rem .6rem; padding: .5rem .6rem; border-radius: var(--r-input); border: 1px solid transparent; background: transparent; text-align: left; width: 100%; }
.wt-it:hover { border-color: var(--line); background: var(--ok-bg); }
.wt-it b { font-weight: 600; }
.wt-it .wt-q { font-family: var(--font-mono); font-size: .78rem; color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.wt-it .wt-d { grid-column: 1 / -1; font-family: var(--font-mono); font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.wt-tot { padding: .5rem .6rem 0; border-top: 1px solid var(--line); margin-top: .3rem; font-size: .78rem; color: var(--muted); }
.wt-bottom { position: absolute; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; gap: .4rem; padding: .6rem .75rem; pointer-events: none; }
.wt-bottom > * { pointer-events: auto; }
.wt-insp { display: none; align-self: center; align-items: flex-start; gap: .8rem; padding: .7rem .85rem; max-width: min(560px, 100%); box-sizing: border-box; }
.wt-insp.on { display: flex; }
.wt-insp-body { display: flex; gap: .8rem; align-items: flex-start; min-width: 0; }
.wt-insp img { width: 72px; height: 72px; object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line); flex: none; }
.wt-insp h3 { margin: 0 0 .1rem; font-family: var(--font-sans); font-size: .95rem; font-weight: 700; }
.wt-insp .mono { font-family: var(--font-mono); font-size: .75rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.wt-insp-k { display: grid; grid-template-columns: auto auto; gap: .1rem .8rem; margin-top: .35rem; font-size: .78rem; }
.wt-insp-k span:nth-child(odd) { color: var(--muted); }
.wt-insp a { display: inline-block; margin-top: .35rem; font-size: .8rem; color: var(--brass-deep); }
.wt-x { margin-left: auto; border: 0; background: transparent; font-size: 1.1rem; line-height: 1; color: var(--muted); padding: .1rem .4rem; }
.wt-tour { display: none; align-items: center; gap: .6rem; align-self: center; padding: .4rem .5rem .4rem .8rem; max-width: 100%; }
.wt-tour.on { display: flex; }
.wt-tour b { white-space: nowrap; }
.wt-bar { position: relative; width: min(220px, 30vw); height: 4px; border-radius: 2px; background: var(--line); overflow: hidden; }
.wt-bar i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--brass); }
.wt-where { display: inline-flex; align-items: center; gap: .6rem; align-self: flex-start; padding: .35rem .6rem; font-size: .8rem; }
.wt-where b { font-weight: 700; }
.wt-where .mono { font-family: var(--font-mono); color: var(--muted); font-variant-numeric: tabular-nums; }
.wt-chips { display: flex; gap: .35rem; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; max-width: 100%; }
.wt-chips::-webkit-scrollbar { display: none; }
.wt-chip { flex: none; font-size: .8rem; font-weight: 600; padding: .45rem .75rem; border-radius: var(--r-pill); border: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 92%, transparent); backdrop-filter: blur(8px); }
.wt-chip:hover { border-color: var(--brass); }
.wt-chip.on { background: var(--chrome); color: var(--chrome-ink); border-color: var(--chrome); }
.wt-stick { position: absolute; left: 1.2rem; bottom: 7.2rem; width: 120px; height: 120px; border-radius: 50%; display: none; pointer-events: none; opacity: .9; }
.wt-knob { position: absolute; left: 40px; top: 40px; width: 40px; height: 40px; border-radius: 50%; background: var(--brass); }
.wt-touch .wt-stick.show { display: block; }
.wt-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.wt-rooms, .wt-views, .wt-unplaced { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .35rem; }
.wt-rooms li, .wt-views li, .wt-unplaced li { display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap; }
.wt-rooms .mono, .wt-views .mono { font-family: var(--font-mono); font-size: .8rem; font-variant-numeric: tabular-nums; }
.wt-view-form { margin-top: 1rem; }
.wt-form-actions { display: flex; gap: .75rem; align-items: center; margin-top: .5rem; }
.wt-sched td.num, .wt-sched th.num, .wt-list td.num { text-align: right; font-variant-numeric: tabular-nums; }
.wt-sched .mono, .wt-list .mono { font-family: var(--font-mono); }
.wt-palette { display: flex; flex-direction: column; gap: .4rem; }
.wt-pal-row { display: grid; grid-template-columns: 1fr auto auto; gap: .6rem; align-items: center; }
.wt-pal-row input[type=color] { width: 44px; height: 28px; padding: 0; border: 1px solid var(--line); border-radius: var(--r-sm); background: transparent; }
.wt-pal-clay { font-size: .8rem; color: var(--muted); display: inline-flex; gap: .3rem; align-items: center; }
.wt-inline-form { margin-bottom: 1rem; }
@media (max-width: 720px) {
  .wt-stage { height: min(78vh, 640px); }
  .wt-grid { grid-template-columns: 1fr; }
  .wt-seg button { padding: .4rem .55rem; font-size: .78rem; }
  .wt-ib { padding: .4rem .5rem; font-size: .78rem; }
  .wt-top { flex-wrap: wrap; justify-content: flex-start; }
  .wt-tools { justify-content: flex-start; }
  .wt-mapwrap { top: auto; bottom: 7.4rem; right: .6rem; }
  .wt-mapwrap canvas { width: 104px; height: 69px; }
  .wt-drawer { top: 6.6rem; left: .75rem; width: auto; bottom: .75rem; }
  .wt-stick { bottom: 7.4rem; }
  .wt-row input[type=range] { width: 110px; }
}
.wt-stage canvas.wt-pinning { cursor: cell; }
/* Pins (D12): a pin's thread and the form that places one float over the model in the HUD's own chrome; the list
   under the model is the comment thread's look with a numbered marker. */
.wt-pincard, .wt-pinform { position: absolute; left: .75rem; top: 3.4rem; width: min(340px, calc(100% - 1.5rem)); max-height: calc(100% - 4.2rem);
  display: none; flex-direction: column; gap: .45rem; overflow: auto; padding: .7rem .85rem; box-sizing: border-box;
  background: color-mix(in srgb, var(--surface) 94%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--line); border-radius: var(--r-input); box-shadow: var(--shadow-pop); -webkit-user-select: text; user-select: text; }
.wt-pincard.on, .wt-pinform.on { display: flex; }
.wt-pincard-head { display: flex; align-items: center; gap: .5rem; }
.wt-pincard-head b { font-weight: 700; }
.wt-pincard textarea, .wt-pinform textarea { width: 100%; box-sizing: border-box; font: inherit; }
.wt-pincard-thread { display: flex; flex-direction: column; gap: .5rem; }
.wt-pc-c .comment-meta { display: flex; flex-wrap: wrap; gap: .4rem; align-items: baseline; font-size: .78rem; }
.wt-pc-body { margin: .15rem 0 0; white-space: pre-line; overflow-wrap: anywhere; }
.wt-pin-reply { display: flex; flex-direction: column; gap: .3rem; border-top: 1px solid var(--line); padding-top: .5rem; }
.wt-pinform-actions { justify-content: flex-end; margin-top: 0; }
.wt-pinform .field { margin: 0; }
.wt-pinform select { width: 100%; }
.wt-pin-n { display: inline-flex; align-items: center; justify-content: center; min-width: 1.5rem; height: 1.5rem; padding: 0 .3rem;
  border-radius: var(--r-pill); background: var(--brass); color: var(--chrome-ink); font-family: var(--font-mono); font-size: .75rem;
  font-weight: 700; box-sizing: border-box; }
.wt-pins { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .75rem; }
.wt-pin .comment-meta { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.wt-pin-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: .4rem 0 0; }
.wt-pin-replies { list-style: none; margin: .5rem 0 0; padding: 0 0 0 1rem; border-left: 2px solid var(--line); display: flex; flex-direction: column; gap: .5rem; }
/* Renders (D13): an uploaded rendering of a saved view, a thumbnail linking to its asset. */
.wt-views li { align-items: baseline; }
.wt-renders { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: .5rem; margin: .3rem 0 .2rem; }
.wt-render { display: flex; flex-direction: column; gap: .2rem; width: 132px; max-width: calc(50% - .25rem); font-size: .75rem; color: var(--ink-soft); text-decoration: none; }
.wt-render img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface); }
.wt-render span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wt-render:hover img { border-color: var(--brass); }
.wt-render-add { flex-basis: 100%; }
.wt-render-add summary { cursor: pointer; color: var(--brass-deep); font-size: .85rem; }
.wt-render-add input[type=file] { max-width: 100%; }
.as-render-of { margin: .5rem 0 0; font-size: .9rem; }
@media (max-width: 720px) {
  .wt-pincard, .wt-pinform { top: auto; bottom: .75rem; left: .5rem; width: calc(100% - 1rem); max-height: 60%; }
}
@media (prefers-reduced-motion: reduce) { .wt-toast { transition: none; } }

/* Production & shipping (views/production.php, views/_production.php; Jeff, 5 November 2026): Gate 03 step by step.
   One form records for the ticked items; each choice is a radio with its own fields under it. */
.production-record { border: 0; padding: 0; margin: 1.4rem 0 0; min-width: 0; }
.production-record legend { font-weight: 600; color: var(--ink); margin: 0 0 .6rem; padding: 0; }
.production-record .check-row { margin: .9rem 0 .4rem; }
.production-table td .muted { display: block; font-size: .8rem; }
/* On a phone each item is a card (Jeff, 7 November 2026: "Fix the page layout on mobile"): the tick and the item as its
   heading, then each fact as a label and its value; a fact with nothing in it is left out. Labels from data-label. */
@media (max-width: 760px) {
  .pr-cards, .pr-cards tbody, .pr-cards tr, .pr-cards td { display: block; width: auto; }
  .pr-cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .pr-cards tr {
    position: relative; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
    padding: .85rem 1rem .9rem 3rem; margin-bottom: .8rem;
  }
  .pr-cards td { border: 0; padding: .25rem 0; text-align: left; }

  .pr-cards td.col-pick { position: absolute; left: .9rem; top: .95rem; padding: 0; width: auto; }
  .pr-cards td.pr-item { padding-bottom: .55rem; margin-bottom: .35rem; border-bottom: 1px solid var(--line-soft); }
  .pr-cards td.pr-item strong { word-break: normal; overflow-wrap: anywhere; }
  .pr-cards td[data-label] { display: grid; grid-template-columns: 7.5rem 1fr; gap: .75rem; align-items: baseline; }
  .pr-cards td[data-label]::before {
    content: attr(data-label); font-family: var(--font-mono); font-size: .58rem; font-weight: 500;
    letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  }
  .pr-cards td[data-label].num { text-align: left; }
  .pr-cards td[data-label].is-blank { display: none; }
  .pr-cards .pr-val { min-width: 0; }
  .pr-cards .badge { white-space: normal; justify-self: start; }
  .pr-cards td[data-label] .production-check { min-width: 0; } /* outranks .production-table .production-check (Codex on #303) */
}
.production-empty { margin: .6rem 0 0; padding-left: 1.1rem; display: grid; gap: .3rem; }
/* An entry beside the accounting system's paperwork (views/_production_check.php): status badge, evidence beneath. */
.production-check { margin: .6rem 0 0; }
.production-table .production-check { margin: 0; min-width: 12rem; }
.production-evidence { margin: .35rem 0 0; padding-left: 1rem; display: grid; gap: .2rem; font-size: .8rem; color: var(--ink-soft); }
.production-history { list-style: none; margin: .8rem 0 0; padding: .6rem 0 0; border-top: 1px solid var(--line-soft);
  display: grid; gap: .3rem; font-size: .85rem; color: var(--ink-soft); }

/* ---- My projects (views/my-projects.php, modules/my_projects.php; Jeff, 6 November 2026) -------------------------
   A timeline of every project, then a card per project: a paragraph and its money. The timeline's tracks are SVGs
   with no viewBox, so x and width are percentages of the real width and the date marks keep their shapes. Colours
   are tokens; motion only when the reader has not asked for less (below). */
.mp-head .panel-head { align-items: flex-start; }
.mp-head-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.2rem; }
.mp-open select { min-width: 14rem; max-width: 22rem; padding: .5rem .8rem; border: 1px solid var(--line-strong);
  border-radius: var(--r-input); background: var(--surface); color: var(--ink); }
.gantt-window { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.2rem; margin: 1rem 0 0; }
.gantt-window > label:first-child { font-weight: 600; }
.gantt-window input[type="range"] { flex: 1 1 12rem; max-width: 20rem; accent-color: var(--brass-deep); }
.gantt-window-words { font-size: .9rem; color: var(--ink-soft); }
.gantt-details-toggle { margin: 0; font-size: .9rem; }
#mp-main > .panel + .panel { margin-top: 2rem; }
.gantt { display: grid; }
.gantt-row { display: grid; grid-template-columns: minmax(10rem, 16rem) 1fr; column-gap: 1rem; align-items: center;
  padding: .35rem 0; border-top: 1px solid var(--line-soft); }
.gantt-row:nth-child(odd):not(.gantt-axis-row) { background: var(--line-soft); }
.gantt-axis-row { border-top: 0; padding: 0; }
.gantt-row.is-outside { opacity: .62; }
.gantt-name { display: flex; align-items: baseline; gap: .4rem; min-width: 0; font-size: .9rem; color: var(--ink); text-decoration: none;
  padding-left: .4rem; }
.gantt-name:hover .gantt-name-text, .gantt-name:focus-visible .gantt-name-text { text-decoration: underline; }
.gantt-name-text { color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gantt-dot { flex: none; width: .6rem; height: .6rem; border-radius: 50%; background: var(--line-strong); align-self: center; }
.gantt-dot.is-on-track { background: var(--brass-deep); }
.gantt-dot.is-watch { background: var(--brass-hi); box-shadow: inset 0 0 0 2px var(--brass-deep); }
.gantt-dot.is-at-risk { background: var(--danger); }
.gantt-axis, .gantt-track { display: block; overflow: visible; }
.gantt-grid { stroke: var(--line); stroke-width: 1; }
.gantt-tick { font-family: var(--font-mono); font-size: 11px; fill: var(--muted); }
.gantt-today-label { font-family: var(--font-mono); font-size: 10px; fill: var(--danger); letter-spacing: .04em; }
/* The bar is the four gates (--viz-g1 to g4, the gates' colours wherever they are drawn), outlined; what is still
   ahead of today under a pale veil. A gate placed evenly, for want of an agreed date, is lighter. */
.gantt-gate { stroke: var(--surface); stroke-width: 1; cursor: default; }
.gantt-gate.g1 { fill: var(--viz-g1); }
.gantt-gate.g2 { fill: var(--viz-g2); }
.gantt-gate.g3 { fill: var(--viz-g3); }
.gantt-gate.g4 { fill: var(--viz-g4); }
.gantt-gate.is-even { fill-opacity: .55; stroke: var(--ink-soft); stroke-width: 1.2; stroke-dasharray: 3 2; }
.gantt-gate:hover { fill-opacity: .82; }
.gantt-gate-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: .06em; pointer-events: none; }
.gantt-gate-label.g1, .gantt-gate-label.g2 { fill: var(--ink); }
.gantt-gate-label.g3, .gantt-gate-label.g4 { fill: var(--chrome-ink); }
.gantt-ahead { fill: var(--surface); fill-opacity: .4; pointer-events: none; }
.gantt-ahead.is-key { fill-opacity: .55; }
.gantt-bar { fill: none; stroke: var(--brass-deep); stroke-width: 1; pointer-events: none; }
.gantt-bar.cut-start, .gantt-bar.cut-end { stroke-dasharray: 4 3; }
.gantt-mark { cursor: default; }
/* The note a section or date shows on hover (projects.js; its words are the server's, from data-tip). */
.mp-tip { position: fixed; z-index: 60; max-width: 22rem; padding: .45rem .65rem; border-radius: var(--r-sm);
  background: var(--chrome); color: var(--chrome-ink); font-size: .8rem; line-height: 1.45; box-shadow: var(--shadow-pop);
  pointer-events: none; }
.mp-tip[hidden] { display: none; }
.gantt-today { stroke: var(--danger); stroke-width: 1.5; stroke-dasharray: 3 3; pointer-events: none; }
.gm-met { fill: var(--brass-deep); stroke: var(--surface); stroke-width: 1.5; }
.gm-ahead { fill: var(--surface); stroke: var(--brass-deep); stroke-width: 1.5; }
.gm-due { fill: var(--brass-hi); stroke: var(--brass-deep); stroke-width: 1.5; }
.gm-due-dot { fill: var(--ink); }
.gm-missed { stroke: var(--danger); stroke-width: 3; stroke-linecap: round; fill: none; }
.gm-install { fill: var(--ink); stroke: var(--surface); stroke-width: 1.5; }
.gm-completion { fill: var(--ink-soft); stroke: var(--surface); stroke-width: 1.5; }
/* The ordering dates on a project's bar (staff who see cost): a ring for the first order-by still open, a dot for the
   last expected on site; terracotta when passed or after the install, burnt orange when soon or later than needed. */
.gm-ob { fill: var(--surface); stroke: var(--ink-soft); stroke-width: 2; }
.gm-ob.is-soon { stroke: var(--change); fill: var(--change-bg); }
.gm-ob.is-passed { stroke: var(--danger); fill: var(--danger-bg); }
.gm-os { fill: var(--ink-soft); stroke: var(--surface); stroke-width: 1.5; }
.gm-os.is-tight { fill: var(--change); }
.gm-os.is-late { fill: var(--danger); }
.gantt-chip.state-passed, .gantt-chip.state-late { color: var(--danger); border-color: var(--danger-line); background: var(--danger-bg); }
.gantt-chip.state-soon, .gantt-chip.state-tight { color: var(--change); border-color: var(--change-line); background: var(--change-bg); }
/* The Report's Ordering on time (views/_ordering_chart.php): the same ring and dot on one axis, the install a solid rule. */
.ord-chart { margin: 1rem 0 .4rem; overflow: visible; }
.ord-install { stroke: var(--ink); stroke-width: 1.5; pointer-events: none; }
.gantt-none { margin: 0; font-size: .82rem; color: var(--muted); }
.gantt-dates { grid-column: 2; list-style: none; margin: .25rem 0 .2rem; padding: 0; display: flex; flex-wrap: wrap; gap: .3rem; }
.gantt.hide-details .gantt-dates { display: none; }
.gantt-dates li { font-size: .76rem; color: var(--ink-soft); padding: .1rem .5rem; border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface); }
.gantt-chip.state-missed { color: var(--danger); border-color: var(--danger-line); background: var(--danger-bg); }
.gantt-chip.state-met { color: var(--brass-deep); border-color: var(--ok-line); }
.gantt-key { list-style: none; margin: .9rem 0 0; padding: .6rem 0 0; border-top: 1px solid var(--line-soft);
  display: flex; flex-wrap: wrap; gap: .4rem 1.1rem; font-size: .8rem; color: var(--ink-soft); }
.gantt-key li { display: inline-flex; align-items: center; gap: .35rem; }
.gantt-key svg { overflow: visible; }
@media (max-width: 620px) {
  .gantt-row { grid-template-columns: 1fr; row-gap: .15rem; }
  .gantt-axis-row .gantt-name { display: none; }
  .gantt-dates { grid-column: 1; }
  .mp-open select { min-width: 0; max-width: none; width: 100%; }
  .gantt-tick.is-minor { display: none; }
  .gantt-today-label { display: none; }
  .gantt-gate-label { display: none; }
  .pc-money { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
  .pc-num { font-size: 1.1rem; }
}

/* One project's card: header band, the paragraph, the money row. */
.pc { border-left: 4px solid var(--line-strong); }
.pc.is-on-track { border-left-color: var(--brass-deep); }
.pc.is-watch { border-left-color: var(--brass-hi); }
.pc.is-at-risk { border-left-color: var(--danger); }
.pc-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: .6rem 1.5rem;
  padding-bottom: .8rem; border-bottom: 1px solid var(--line-soft); }
.pc-head h2 { margin: 0; }
.pc-where { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .8rem; }
.pc-gates { display: inline-flex; gap: 3px; }
.pc-gate { width: 1.4rem; height: .45rem; border-radius: var(--r-pill); background: var(--line); }
.pc-gate.is-done.g1, .pc-gate.is-here.g1 { background: var(--viz-g1); }
.pc-gate.is-done.g2, .pc-gate.is-here.g2 { background: var(--viz-g2); }
.pc-gate.is-done.g3, .pc-gate.is-here.g3 { background: var(--viz-g3); }
.pc-gate.is-done.g4, .pc-gate.is-here.g4 { background: var(--viz-g4); }
.pc-gate.is-here { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--ink); }
.pc-gate-words { font-size: .85rem; font-weight: 600; color: var(--ink); }
.pc-text { margin: 1rem 0 0; max-width: 68ch; font-size: 1.02rem; line-height: 1.65; color: var(--ink); }
.pc-money { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .8rem; margin: 1.2rem 0 0; }
.pc-tile { margin: 0; padding: .8rem .9rem; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg);
  display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.pc-tile dt { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.pc-tile dd { margin: 0; }
.pc-num { font-size: 1.35rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.2; }
.pc-bar svg { display: block; }
.pc-bar-track { fill: var(--line); }
.pc-bar-fill { fill: var(--brass-deep); }
.pc-bar-fill.is-danger { fill: var(--danger); }
.pc-bar-mark { stroke: var(--ink); stroke-width: 2; }
.pc-sub { font-size: .8rem; color: var(--ink-soft); }
.pc-sub.is-danger { color: var(--danger); font-weight: 600; }
.pc-more { margin-top: 1rem; }
.project-more { margin-top: .6rem; border-top: 1px solid var(--line-soft); padding-top: .6rem; }
.project-more > summary { cursor: pointer; font-weight: 600; color: var(--brass-deep); }
.project-more[open] > summary { margin-bottom: .6rem; }
.link-list { list-style: none; margin: .4rem 0 0; padding: 0; display: grid; gap: .3rem; }

/* Motion (D14): the bars grow from their start, the date marks follow, the money bars fill. None of it for a
   reader who has asked for reduced motion; the numbers count up in projects.js under the same rule. */
@media (prefers-reduced-motion: no-preference) {
  .gantt-bar-g, .pc-bar-fill { transform-box: fill-box; transform-origin: left center;
    animation: mp-grow .9s cubic-bezier(.2, .7, .2, 1) both; }
  .gantt-mark { animation: mp-fade .4s ease-out both; animation-delay: .85s; }
  .gantt-row:nth-child(3) .gantt-bar-g { animation-delay: .08s; }
  .gantt-row:nth-child(4) .gantt-bar-g { animation-delay: .16s; }
  .gantt-row:nth-child(5) .gantt-bar-g { animation-delay: .24s; }
  .gantt-row:nth-child(6) .gantt-bar-g { animation-delay: .32s; }
  .gantt-row:nth-child(n+7) .gantt-bar-g { animation-delay: .4s; }
  .pc-bar-fill { animation-delay: .25s; }
  .pc { animation: mp-rise .5s ease-out both; }
}
@keyframes mp-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes mp-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes mp-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* Admin · Sync: a save the records may not hold, its value and the records' side by side (views/admin-sync.php). */
.sync-field { display: block; overflow-wrap: anywhere; }
.sync-actions { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }

/* ---- Vendor status (views/_vendor_status.php, modules/vendor_status.php + purchasing.php, 7 November 2026) ----
   A row tinted by what its vendor said: green confirmed, orange a change to update (or edited since confirmed),
   terracotta cannot supply. The marker beside it says which in words, so the tint is never the only signal. */
.row-vendor-confirmed > td { background: var(--confirmed-bg); }
.row-vendor-change > td    { background: var(--change-bg); }
.row-vendor-cannot > td    { background: var(--danger-bg); }
.row-vendor-confirmed > td:first-child { box-shadow: inset 3px 0 0 var(--confirmed); }
.row-vendor-change > td:first-child    { box-shadow: inset 3px 0 0 var(--change); }
.row-vendor-cannot > td:first-child    { box-shadow: inset 3px 0 0 var(--danger); }
.row-vendor-confirmed:not(tr) { background: var(--confirmed-bg); border-left: 3px solid var(--confirmed); }
.row-vendor-change:not(tr)    { background: var(--change-bg); border-left: 3px solid var(--change); }
.row-vendor-cannot:not(tr)    { background: var(--danger-bg); border-left: 3px solid var(--danger); }
.badge.vendor-confirmed { background: var(--confirmed-bg); color: var(--confirmed); border-color: var(--confirmed-line); }
.badge.vendor-change    { background: var(--change-bg); color: var(--change); border-color: var(--change-line); }
.badge.vendor-cannot    { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.badge.vendor-open      { background: transparent; color: var(--ink-soft); border-color: var(--line-strong); }
.badge.order-by         { background: transparent; color: var(--ink-soft); border-color: var(--line-strong); }
.badge.order-by.is-soon   { background: var(--change-bg); color: var(--change); border-color: var(--change-line); }
.badge.order-by.is-passed { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.badge.on-site          { background: transparent; color: var(--ink-soft); border-color: var(--line-strong); }
.badge.on-site.is-tight { background: var(--change-bg); color: var(--change); border-color: var(--change-line); }
.badge.on-site.is-late  { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.vendor-mark { display: inline-flex; flex-wrap: wrap; gap: .35rem .5rem; align-items: center; }
.vendor-mark.is-full { display: flex; flex-direction: column; align-items: flex-start; gap: .5rem; }
.vendor-changes { display: inline-flex; flex-wrap: wrap; gap: .25rem .9rem; font-size: .85rem; }
.vendor-change { white-space: nowrap; }
.vendor-change-label { color: var(--muted); margin-right: .3rem; }
.vendor-was { text-decoration: line-through; color: var(--muted); }
.vendor-new { font-weight: 600; color: var(--change); }
.row-vendor-confirmed .vendor-new { color: var(--confirmed); }
.vendor-note { font-size: .85rem; color: var(--ink-soft); }
.vendor-anyway { margin: .4rem 0 0; font-size: .85rem; }
.vw-status { margin: 0 0 .6rem; }
.vendor-panel .vendor-mark { margin-bottom: .6rem; }
@media (max-width: 560px) {
  .vendor-change { white-space: normal; }
}

/* Admin · Usage (views/admin-usage.php, modules/usage.php): the filters beside the period chips, two tables side by
   side, and the weekday-by-hour grid, one hue light to dark (busier is darker; none is the faint line colour). */
.use-filter { display: inline-flex; align-items: center; gap: .35rem; margin: 0; font-size: .85rem; color: var(--ink-soft);
  max-width: 100%; min-width: 0; }
/* Admin · Dates (views/admin-dates.php): each date to move, ticked to accept; the now crossed through beside the new. */
.date-plan h2 { display: flex; flex-wrap: wrap; gap: .25rem .6rem; align-items: baseline; }
.date-plan-table .col-pick { width: 2rem; padding-right: 0; }
.date-plan-now { color: var(--muted); text-decoration: line-through; white-space: nowrap; }
.date-plan-new { white-space: nowrap; }
.date-plan-new .muted { display: block; font-size: .8rem; }
.date-plan-why { font-size: .88rem; color: var(--ink-soft); min-width: 14rem; }
.date-plan-actions { display: flex; flex-wrap: wrap; gap: .6rem 1rem; align-items: center; margin-top: .9rem; }
/* A long option (a project's code and name) never widens the select past the panel; it shrinks and clips instead. */
.use-filter select { flex: 0 1 auto; min-width: 0; max-width: min(100%, 22rem); text-overflow: ellipsis; }
.use-two { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 0 1rem; }
.use-heat { max-width: 60rem; }
.use-heat-0 { fill: var(--line-soft); }
.use-heat-1 { fill: var(--viz-1); fill-opacity: .28; }
.use-heat-2 { fill: var(--viz-1); fill-opacity: .5; }
.use-heat-3 { fill: var(--viz-1); fill-opacity: .74; }
.use-heat-4 { fill: var(--viz-1); fill-opacity: 1; }
