/* STRUCK — the design system.

   One stylesheet, loaded by every page, so the system established on the front page propagates rather than being
   re-typed. Four colours plus states, three typefaces, one spacing scale, and no border radius anywhere.

   ON FONTS. The spec requires self-hosting and forbids a third-party font CDN — that is a security requirement,
   not a preference, and it is honoured here: nothing is fetched from anywhere. The stacks below resolve to faces
   already on the machine, which is the only way to have real typography without either a CDN or font files I
   cannot produce. When the real faces are chosen, drop the .woff2 files beside this stylesheet and replace the
   three stacks — the rest of the system is unaffected, which is the point of naming them once here.

   ON THE ACCENT. Brass is a metal, not a brand colour splash. It appears on eyebrows, links, focus rings and ONE
   highlight per screen. It is never a large fill. Where a page wants emphasis at size, the answer is the ink
   inversion, not more brass. */

:root {
  /* Four values plus states. Do not add a fifth. */
  --ink:     #17150F;   /* near-black, warm. Text, buttons, inverted sections */
  --paper:   #FAF8F4;   /* warm off-white. Page background */
  --paper-2: #F2EEE6;   /* panels, hover fills, the start-point card */
  --brass:   #916B26;   /* the single accent */
  --line:    #DED7C9;   /* hairline rules and borders */

  /* States, derived rather than invented, so they cannot drift from the four. */
  --ink-70:  #5C574C;   /* secondary text — 7.0:1 on paper */
  --ink-55:  #6F6A5D;   /* tertiary text and .help — 5.1:1 on paper, 4.7:1 on panels. Both matter: .help
                           sits on panel fills, which is the harder case and the one the old value failed. */
  --ink-muted: #5A554A;  /* secondary text. Referenced by vessel.html, which had only a hard-coded fallback */
  --surface-alt: #F2EEE6; /* alternate panel fill. Same — defined so the operator’s palette actually reaches it */
  --ink-faint: #6F6A5D;
  /* Aliases onto what the token system actually emits. Redefining them with literal values would give the
     shop two spacing scales that drift; pointing them at --space-N means extending the scale in the token
     screen reaches these rules too. */
  --s1: var(--space-4);
  --s2: var(--space-8);
  --s3: var(--space-12);
  --s4: var(--space-16);
  --s5: var(--space-24);
  --s6: var(--space-32);
  --f: var(--sans);      /* shorthand used by the calendar and queue rules */
  --danger: var(--bad);  /* the role name the newer screens use for the same colour */  /* smallest text: counts, meta, empty states. Nine rules referenced this role while
                            nothing defined it, so their hard-coded fallback rendered instead — failing
                            contrast AND ignoring the palette. Same value as --ink-55 for now; they are
                            separate roles so the operator can pull them apart. */
  --brass-d: #6F5119;   /* brass on paper-2, and visited links */
  --bad:     #8C2F22;
  --warn:    #7A5A10;
  --good:    #3D5C3A;

  /* Type. One serif for statements, one sans for everything else, one mono for numbers. */
  --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --sans:  ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --mono:  ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* One 4px scale, used strictly. Nothing gets an arbitrary value. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  --wrap: 1180px;
  --gutter: 24px;
  --section-y: 96px;
}

@media (max-width: 760px) { :root { --section-y: 64px; } }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
html, body { margin: 0; }

body {
  background: var(--paper);
  color: var(--ink);
  font-weight: 400; font-size: 16px; line-height: 1.6; font-family: var(--sans);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
}

/* ---- Type scale ---- */

.display, h1.display {
  font-family: var(--serif);
  font-size: clamp(38px, 5.6vw, 66px);
  line-height: 1.04;
  letter-spacing: -0.018em;
  font-weight: 400;
  margin: 0;
  text-wrap: balance;
}
.section-h, h2 {
  font-family: var(--serif);
  font-size: clamp(26px, 3.2vw, 38px);
  line-height: 1.12;
  letter-spacing: -0.012em;
  font-weight: 400;
  margin: 0;
  text-wrap: balance;
}
.sub, h3 {
  font-weight: 600; font-size: 19px; line-height: 1.35; font-family: var(--sans);
  letter-spacing: -0.005em;
  margin: 0;
}
.lede {
  font-weight: 400; font-size: 19px; line-height: 1.55; font-family: var(--sans);
  color: var(--ink-70);
  max-width: 56ch;
  margin: 0;
  text-wrap: pretty;
}
.small { font-size: 13.5px; line-height: 1.5; }
.muted { color: var(--ink-70); }
.quiet { color: var(--ink-55); }

.eyebrow {
  font-weight: 600; font-size: 11.5px; line-height: 1; font-family: var(--sans);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--brass);
  display: block;
  margin: 0 0 var(--s3);
}

/* Money, dimensions, phone numbers, quantities and dates. This single choice does more for "looks official"
   than anything else in the system — it makes a price table read as a specification rather than as marketing. */
.num, .money, .dim, .qty, .tel, .date,
td.num, th.num, .price, .figure {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.02em;
}

p { margin: 0 0 var(--s4); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

/* Links in body text: brass, underlined. Not everywhere — a nav full of underlines is noise. */
a { color: var(--brass); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--ink); }
a:visited { color: var(--brass-d); }
a.plain, nav a, .btn { text-decoration: none; }

/* ---- Layout ---- */

.wrap { max-width: var(--wrap); margin: 0 auto; padding-left: var(--gutter); padding-right: var(--gutter); }
.section { padding-top: var(--section-y); padding-bottom: var(--section-y); }
/* Section boundaries are 1px rules, not shadows. */
.section + .section { border-top: 1px solid var(--line); }
.section.inverted { background: var(--ink); color: var(--paper); border-top-color: #2D2A21; }
.section.inverted .lede, .section.inverted .muted { color: #B9B3A5; }
.section.inverted .eyebrow { color: #C9A961; }
.section.inverted a { color: #C9A961; }
.section.panel-bg { background: var(--paper-2); }

.stack > * + * { margin-top: var(--s4); }
.stack-lg > * + * { margin-top: var(--s6); }
.row { display: flex; gap: var(--s4); flex-wrap: wrap; align-items: center; }
.grid { display: grid; gap: var(--s5); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ---- Components. No border radius anywhere: this shop cuts square edges. ---- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  font-weight: 600; font-size: 15px; line-height: 1; font-family: var(--sans);
  padding: 14px var(--s5);
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  border-radius: 0;
  cursor: pointer;
  transition: background .14s ease, border-color .14s ease, color .14s ease;
  min-height: 46px;
}
/* The customer-facing fill, scoped to the site's own sections. Left global it collided with the operator
   button two hundred lines below and contributed nothing to it but a text colour that made the label
   disappear. */
.section .btn:hover, .site .btn:hover, .wrap .btn:hover {
  background: var(--brass); border-color: var(--brass); color: var(--paper); }
.btn.secondary { background: transparent; color: var(--ink); border-color: var(--line); }
.btn.secondary:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.btn.small { font-size: 13.5px; padding: 9px var(--s4); min-height: 38px; }
/* .btn.tiny lives in admin.html's own style block, which loads after this file. It is not repeated here: two
   definitions with different numbers is how a row action ends up one height in a table and another in a list. */
.btn[disabled] { opacity: .4; cursor: not-allowed; }
.section.inverted .btn { background: var(--paper); color: var(--ink); border-color: var(--paper); }
.section.inverted .btn:hover { background: var(--brass); border-color: var(--brass); color: var(--paper); }
.section.inverted .btn.secondary { background: transparent; color: var(--paper); border-color: #46423A; }
.section.inverted .btn.secondary:hover { background: var(--paper); color: var(--ink); }

/* Excludes tick boxes. As a blanket rule this gave every checkbox and radio width:100%, 12px padding
   and a 46px minimum — so a tick box inside a flex row ate the whole row and pushed its own label
   outside the card. Twenty-five screens were affected. */
input:not([type=checkbox]):not([type=radio]), select, textarea {
  font-weight: 400; font-size: 16px; line-height: 1.4; font-family: var(--sans);
  padding: 12px var(--s3);
  border: 1px solid var(--line);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
  width: 100%;
  min-height: 46px;
}
/* Belt and braces: anything else that tries to stretch a tick box is overruled here. */
input[type=checkbox], input[type=radio] {
  width: auto; min-width: 0; min-height: 0; padding: 0;
  display: inline-block; flex: 0 0 auto; box-sizing: border-box;
}
/* 2px brass focus outline, never removed. */
input:focus-visible, select:focus-visible, textarea:focus-visible,
.btn:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--brass);
  outline-offset: 2px;
}
label { display: block; font-weight: 600; font-size: 13.5px; line-height: 1.4; font-family: var(--sans); margin-bottom: var(--s2); }
.field + .field { margin-top: var(--s4); }
.help { font-size: 13.5px; color: var(--ink-55); margin-top: var(--s1); }

.panel { background: var(--paper-2); padding: var(--s6); border-radius: 0; }
.bordered { border: 1px solid var(--line); padding: var(--s5); }
.rule { height: 1px; background: var(--line); border: 0; margin: var(--s6) 0; }

/* Tables read as specifications: uppercase letterspaced headers, hairline rules, right-aligned mono figures. */
table { width: 100%; border-collapse: collapse; }
th {
  font-weight: 600; font-size: 11.5px; line-height: 1; font-family: var(--sans);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-70);
  text-align: left;
  padding: 0 var(--s3) var(--s3);
  border-bottom: 1px solid var(--ink);
}
td { padding: var(--s3); border-bottom: 1px solid var(--line); vertical-align: top; }
tbody tr:hover td { background: var(--paper-2); }
td.right, th.right { text-align: right; }
th.num, td.num { text-align: right; }

.badge {
  display: inline-block; font-weight: 600; font-size: 11px; line-height: 1; font-family: var(--sans); text-transform: uppercase;
  letter-spacing: 0.09em; padding: 5px var(--s2); border: 1px solid var(--line); color: var(--ink-70);
}
.badge.accent { border-color: var(--brass); color: var(--brass); }

/* ---- Photography placeholders ----
   A labelled frame at its true aspect ratio carrying a shot number and what goes in it. It should read as a
   deliberate production placeholder, not an empty div — so the page can be judged before the photography
   exists, and so the shot list writes itself. */
.shot {
  position: relative;
  background: var(--paper-2);
  border: 1px solid var(--line);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: var(--s4);
  overflow: hidden;
}
.shot::before {
  content: "";
  position: absolute; inset: var(--s2);
  border: 1px dashed var(--line);
  pointer-events: none;
}
.shot-n {
  position: absolute; top: var(--s4); left: var(--s4);
  font-weight: 600; font-size: 11px; line-height: 1; font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: 0.08em;
  color: var(--paper); background: var(--ink); padding: 5px 7px;
}
.shot-ratio {
  position: absolute; top: var(--s4); right: var(--s4);
  font-weight: 400; font-size: 11px; line-height: 1; font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--ink-55);
}
.shot-what { font-weight: 400; font-size: 13.5px; line-height: 1.45; font-family: var(--sans); color: var(--ink-70); position: relative; max-width: 42ch; }
.shot.tall  { aspect-ratio: 3 / 4; }
.shot.wide  { aspect-ratio: 16 / 9; }
.shot.square{ aspect-ratio: 1 / 1; }
.shot.four3 { aspect-ratio: 4 / 3; }
.shot.banner{ aspect-ratio: 21 / 9; }

/* ---- Category header. Every part optional: with nothing filled in this is a heading and a line of text,
     which is the header the shop already had. ---- */
.chead { display: grid; gap: var(--s4); margin-bottom: var(--s6); }
.chead-words { display: grid; gap: var(--s3); }
.chead-words h1 { margin: 0; }
.chead-words p { margin: 0; }
.chead-img { display: block; width: 100%; }
.chead-img img { display: block; width: 100%; height: 100%; object-fit: cover; }
.chead.beside { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s6); align-items: center; }
@media (max-width: 760px) { .chead.beside { grid-template-columns: minmax(0, 1fr); } }

/* ---- Motion. Every animation respects the visitor's setting regardless of what the operator chose. ---- */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity .5s ease, transform .5s ease; }
.reveal.seen { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

.skip {
  position: absolute; left: -9999px; top: 0; background: var(--ink); color: var(--paper);
  padding: var(--s3) var(--s4); z-index: 100;
}
.skip:focus { left: 0; }

@media print {
  .no-print { display: none !important; }
  body { background: #fff; }
}


/* ---- the token screen ----
   The swatch chip is deliberately square and hairline-bordered: a round swatch on a site with no radius
   anywhere would be the one inconsistency the operator sees every time they open this screen. */
.rolerow { display:flex; gap:12px; align-items:center; padding:6px 0; border-bottom:1px solid var(--line); }
.rolerow:last-child { border-bottom:0; }
.roleswatch { width:26px; height:26px; flex:0 0 auto; border:1px solid var(--line-strong,#958E80); }
.roleswatch.sm { width:13px; height:13px; display:inline-block; vertical-align:-2px; }
.rolename { flex:1; min-width:150px; display:grid; gap:1px; }
.rolename code { font-size:12px; }
.rolevalue { font-size:11px; color:var(--ink-faint,#6F6A5D); width:66px; text-align:right; }
.swatchgrid { display:grid; gap:6px; margin-bottom:10px; }
.swatch { display:flex; gap:8px; align-items:center; }
.swatch input[type=color] { width:34px; height:34px; padding:0; border:1px solid var(--line-strong,#958E80);
  background:none; cursor:pointer; flex:0 0 auto; }
.swatch-t { flex:1; min-width:0; display:grid; gap:1px; }
.in.bare { border:0; padding:2px 0; min-height:auto; font-weight:600; background:none; }


/* ---- the composition screen ----
   Dimmed rather than hidden is the whole point: a block that is off must still be visible and reachable in
   the builder, or the operator cannot find it to turn it back on. */
.compsec { padding-bottom: 8px; }
.compsec.dim { opacity: .55; }
.compsec-h { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;
  padding-bottom:8px; border-bottom:1px solid var(--line); }
.compsec-h .grow { flex:1; min-width:120px; }
.compset { padding:10px 0 14px; border-bottom:1px solid var(--line); margin-bottom:10px; }
.comprow { border-left:2px solid var(--line); padding-left:12px; margin:0 0 10px; }
.comprow-h { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:6px; }
.compblock { display:flex; gap:6px; align-items:center; flex-wrap:wrap; padding:5px 0;
  border-bottom:1px solid var(--line); }
.compblock:last-child { border-bottom:0; }
.compblock.dim { opacity:.5; }
.compblock .grow { flex:1; min-width:110px; }
.bpset { display:flex; gap:2px; }
.bp { width:22px; height:22px; font-weight: 600; font-size: 10px; font-family: var(--f,sans-serif); border:1px solid var(--line);
  background:var(--paper); color:var(--ink-faint,#6F6A5D); cursor:pointer; }
.bp.on { background:var(--ink); color:var(--paper); border-color:var(--ink); }


/* ---- the sheet view ----
   Drawn rather than tabulated: the operator is checking a physical arrangement, and a table of coordinates
   does not answer that question. */
.sheetview { margin: 12px 0; }
.sheetgrid { display:grid; gap:2px; border:1px solid var(--line-strong,#958E80); padding:4px;
  background:var(--surface-alt,#F2EEE6); }
.sheetcell { position:relative; border:1px solid var(--line); background:var(--paper);
  display:flex; align-items:center; justify-content:center; overflow:hidden; min-height:22px; }
.sheetcell.used { background:var(--ink); border-color:var(--ink); }
.sheetcell.bad { background:#F6DCD7; border-color:var(--danger,#A3352A); }
.cellno { position:absolute; top:1px; left:2px; font-size: 9px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color:var(--ink-faint,#6F6A5D); }
.sheetcell.used .cellno { color:rgba(250,248,244,.5); }
.cellname { font-size: 10px; font-family: var(--f,sans-serif); color:var(--paper); padding:0 3px; text-align:center;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
.sheetcell.bad .cellname { color:var(--ink); }


/* ---- the holding page ---- */
.holding { min-height:100vh; display:grid; place-items:center; padding:48px 24px; background:var(--paper); }
.holding-in { max-width:44ch; display:grid; gap:16px; }
.holding h1 { font:var(--type-display,clamp(38px,5.6vw,66px)) var(--serif,Georgia,serif);
  line-height:1.04; letter-spacing:-.018em; margin:0; }
.gateform { display:grid; gap:6px; padding-top:8px; border-top:1px solid var(--line); }
.gateerr { color:var(--danger,#A3352A); font-size:13.5px; margin:0; }


/* A row for something deliberately not built. Dimmed rather than omitted, because the absence is the
   information \u2014 an operator who cannot see that SMS was considered will ask for it. */
.tbl tr.dimrow td { opacity:.6; }


/* ---- true-scale comparison ----
   Bars proportional to the real heights. A dropdown asks the buyer to imagine the difference; this shows it. */
.scalerow { display:flex; gap:24px; align-items:flex-end; flex-wrap:wrap; padding:16px 0 4px; }
.scaleitem { display:grid; gap:4px; justify-items:center; }
.scalebox { background:var(--ink); min-height:8px; }
.scaleitem .num { font-size: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.scaleitem .help { max-width:14ch; text-align:center; }


/* ---- the production calendar ----
   A month is a spatial question \u2014 which week is full, whether the 14th leaves room \u2014 and a list cannot
   answer it. Square cells on purpose: the shop cuts square edges. */
.calhead { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; margin-bottom:2px; }
.calhead span { font-weight: 600; font-size: 10.5px; font-family: var(--f,sans-serif); letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-faint,#6F6A5D); padding:4px 6px; }
/* minmax(0,1fr) rather than 1fr: a grid track defaults to min-width:auto, so the day cells refused to
   shrink below their content and seven of them pushed the month 150px past the work area with no scroller. */
.calgrid { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:2px;
  border:1px solid var(--line-strong,#958E80); padding:2px; background:var(--line); }
.calday { background:var(--paper); min-height:92px; padding:5px 6px; display:flex; flex-direction:column;
  gap:3px; position:relative; min-width:0; }
.calday.empty { background:var(--surface-alt,#F2EEE6); }
.calday.today { outline:2px solid var(--accent); outline-offset:-2px; }
.calday.full { background:#FBF3DF; }
.calday.over { background:#F8E4DF; }
.calnum { font-weight: 600; font-size: 12px; font-family: ui-monospace,Menlo,monospace; font-variant-numeric: tabular-nums; color:var(--ink); }
.calload { position:absolute; top:5px; right:6px; font-size: 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color:var(--ink-faint,#6F6A5D); }
.calitems { display:grid; gap:2px; }
.calitem { font-size: 11px; font-family: var(--f,sans-serif); text-align:left; padding:2px 4px; border:0; cursor:pointer;
  background:var(--ink); color:var(--paper); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.calitem.event { background:var(--surface-alt,#F2EEE6); color:var(--ink); }
.calitem.quote { background:transparent; color:var(--ink); border:1px solid var(--line-strong,#958E80); }
.calitem.rush { background:var(--danger,#A3352A); }
.calmore { font-size: 10px; font-family: var(--f,sans-serif); color:var(--ink-faint,#6F6A5D); }
@media (max-width:820px) {
  .calday { min-height:64px; }
  .calitem { font-size:10px; }
}


/* ---- the production board ----
   Columns, because the question is where work is piling up \u2014 a shape, not a sequence. */
.qboard { display:flex; gap:10px; overflow-x:auto; padding:4px 0 12px; align-items:flex-start; }
.qcol { flex:0 0 220px; border:1px solid var(--line); background:var(--surface-alt,#F2EEE6);
  padding:8px; display:grid; gap:6px; min-height:140px; }
.qcol.gate { border-color:var(--accent); border-width:2px; }
.qhead { display:flex; gap:6px; align-items:baseline; padding-bottom:6px;
  border-bottom:1px solid var(--line-strong,#958E80); }
.qhead strong { flex:1; font-size:13px; }
.qcount { font-size: 11px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color:var(--ink-faint,#6F6A5D); }
.qgate { font-size: 9px; font-family: var(--f,sans-serif); letter-spacing:.08em; text-transform:uppercase;
  background:var(--accent); color:var(--paper); padding:1px 5px; }
.qcards { display:grid; gap:5px; }
.qcard { background:var(--paper); border:1px solid var(--line); padding:6px 7px; cursor:grab;
  display:grid; gap:2px; }
.qcard:hover { border-color:var(--ink); }
.qcard.late { border-left:3px solid var(--danger,#A3352A); }
.qcard.rush { background:#FBF3DF; }
.qref { font-weight: 600; font-size: 12px; font-family: ui-monospace,Menlo,monospace; font-variant-numeric: tabular-nums; }
.qref em { font-style:normal; color:var(--danger,#A3352A); font-size:10px; }
.qcust { font-size:12px; }
.qmeta { font-size:10.5px; color:var(--ink-faint,#6F6A5D); }
.qempty { font-size:11px; color:var(--ink-faint,#6F6A5D); font-style:italic; }


/* The measured drawing. Tabular figures throughout, because that is what makes it read as a specification
   rather than as marketing — and it is the document the customer is actually agreeing to. */
.drawing { border: 1px solid var(--line-strong, #958E80); padding: var(--s4); background: var(--paper); }
.drawhead { font-size: 11.5px; color: var(--ink-55); border-bottom: 1px solid var(--line);
  padding-bottom: var(--s2); margin-bottom: var(--s3); }
.drawview { border-top: 1px solid var(--line); padding-top: var(--s3); margin-top: var(--s3); }
.drawview:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.drawview dl { display: grid; grid-template-columns: auto 1fr; gap: 2px var(--s3); margin: var(--s2) 0; font-size: 12px; }
.drawview dt { color: var(--ink-55); }
.drawview dd { margin: 0; font-variant-numeric: tabular-nums; }


/* ============================================================================
   UI PASS — the difference between a working admin and a finished one.

   Everything here is a refinement of what already exists rather than a new look: the palette, the scale and
   the type are the token system's, and this only applies them consistently. The five things that were
   missing, in the order they matter:

     1  TYPE HIERARCHY — headings that read as headings, not as bold body copy
     2  VERTICAL RHYTHM — space between sections, so a screen has a shape
     3  TABULAR FIGURES — money, dimensions and counts in a monospace with aligned digits. This one does
        more for "looks official" than anything else, because it makes a price table read as a specification
     4  DETAIL — hairlines rather than shadows, considered hover, a focus ring that never disappears
     5  DENSITY WHERE IT EARNS TRUST — tables tight and precise; forms open and readable
   ============================================================================ */

/* ---- 1 · type ---- */
.work h1 { font-size: 27px; line-height: 1.1; letter-spacing: -0.021em; margin: 0 0 2px; font-weight: 600; }
.work h2 { font-size: 15px; line-height: 1.25; letter-spacing: 0.005em; margin: 0 0 12px;
  font-weight: 600; color: var(--ink); }
.work h3 { font-size: 13.5px; font-weight: 600; margin: 0 0 6px; letter-spacing: 0.005em; }
.work .sub { font-size: 14px; line-height: 1.55; max-width: 76ch; color: var(--ink-muted); }
.work .help { font-size: 12px; line-height: 1.5; color: var(--ink-faint); }
.eyebrow, .flaggroup-t, .navarea-t {
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.13em; text-transform: uppercase; }

/* ---- 2 · rhythm ---- */
.work { padding: 26px 30px 96px; }
.head { margin-bottom: 22px; padding-bottom: 18px; border-bottom: 1px solid var(--line); }
.panel { padding: 18px 20px; margin-bottom: 18px; border-radius: 0; }
.panel + .panel { margin-top: 0; }
.grid2 { gap: 16px 20px; }
.bar { padding: 0 0 16px; gap: 10px; }

/* ---- 3 · numbers ----
   Money, dimensions, quantities, dates and counts all align on the digit. A column of prices that does not
   line up reads as marketing; one that does reads as a specification. */
/* Written with enough specificity to survive a shorthand elsewhere in the file — the first version lost to
   `.navitem .count` at (0,2,0) and the digits silently stayed proportional. */
.mono, .tbl td.num, .tbl td.money, .navitem .count, .count, .stagechip, .kpi-n, .flag .count {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.tbl td.num, .tbl td.money { text-align: right; }

/* ---- 4 · detail ---- */
.btn { border-radius: 0; transition: background-color .12s, border-color .12s, color .12s; }
/* All three properties stated, so a rule elsewhere cannot half-win and leave its text colour behind. */
.btn:hover { border-color: var(--ink); background: var(--surface-alt); color: var(--ink); }
.btn.primary:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn:active { transform: translateY(0.5px); }
.btn:focus-visible, .in:focus-visible, .navitem:focus-visible, .chip:focus-visible {
  outline: 2px solid var(--focus, var(--accent)); outline-offset: 1px; }
.in { border-radius: 0; transition: border-color .12s; }
.in:hover { border-color: var(--line-strong); }
/* A chip had exactly one declaration, its corners, so the queue tabs and the Showing switch gave no sign of
   being controls at all. */
.chip { border-radius: 0; transition: background-color .1s, border-color .1s, color .1s; }
.chip:hover { border-color: var(--line-strong); background: var(--surface-alt); color: var(--ink); }
.chip.on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.chip.on:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* Tables: precise, dense, and quiet until you point at a row. */
.tbl { font-size: 13px; }
.tbl th { font-size: 10.5px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-muted); padding: 7px 10px; border-bottom: 1px solid var(--ink); }
.tbl td { padding: 9px 10px; border-bottom: 1px solid var(--line); }
.tbl tbody tr:hover td { background: var(--surface-alt); }
.tbl tbody tr:last-child td { border-bottom: 0; }

/* ---- 5 · the sidebar ---- */
.side { padding-top: 12px; }
.navarea { margin-bottom: 14px; }
.navarea-t { color: var(--ink-faint); padding: 0 14px 5px; }
/* The accent belongs to the BACKGROUND here, not the text — setting both cancels them to a 1:1 brown bar,
   which is what my first pass did. Paper on brass is 6.2:1 and the state stays readable. */
.navarea-t.here { color: var(--paper); }
.navitem { padding: 6px 14px; font-size: 13px; border-left: 2px solid transparent; }
.navitem:hover { background: var(--surface-alt); }
.navitem.on { background: transparent; color: var(--ink); font-weight: 600;
  border-left-color: var(--accent); }
.navitem.later { opacity: 0.5; }
/* Declared and not built. Greyed, and it says which — greying alone reads as "disabled for you", which is a
   permissions message rather than a build one. Not a button, so there is nothing to press. */
.navitem.notbuilt { cursor: default; display: flex; gap: 6px; align-items: baseline; }
.navitem.notbuilt:hover { background: transparent; }
.nbpill { font-size: 8.5px; letter-spacing: 0.08em; text-transform: uppercase; border: 1px solid currentColor;
  padding: 0 4px; border-radius: 2px; opacity: 0.8; white-space: nowrap; }
/* A whole row that opens something says so before it is pressed. */
.tbl tr.clickrow { cursor: pointer; }
.tbl tr.clickrow:hover { background: var(--surface-alt); }
.navitem .count { font-size: 10.5px; opacity: 0.6; }

/* ---- the sidebar's three new parts ----
   A subheading INSIDE an area, one step quieter than the area heading above it. Nine areas at thirty-six items
   apiece was the rule being obeyed at the top and ignored one level down, and two headings of equal weight would
   have simply moved the problem. */
.navgroup-t { font-size: 9.5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-faint); padding: 8px 14px 3px; opacity: 0.75; }
.navitems + .navitems .navgroup-t { padding-top: 10px; }

/* The row holds the destination and its pin. Grid rather than flex so the pin occupies a reserved column and
   nothing shifts sideways when it appears on hover — a list that moves under the cursor is unusable. */
.navhit { display: grid; grid-template-columns: 1fr auto; align-items: center; }
.navhit .navitem { min-width: 0; }
.navhit .navitem { align-items: flex-start; }
.navhit .navitem span:first-child { white-space: normal; overflow-wrap: break-word; line-height: 1.3; }
/* The pin stays on the first line of a label that wrapped, rather than floating to the middle of two. */
.navhit { align-items: start; }
.navhit .navpin { padding-top: 7px; }

.navpin { background: none; border: 0; cursor: pointer; padding: 2px 10px 2px 4px;
  font-size: 12px; line-height: 1; color: var(--ink-faint); opacity: 0;
  transition: opacity .1s linear; }
.navhit:hover .navpin, .navpin.on, .navpin:focus-visible { opacity: 1; }
.navpin.on { color: var(--accent); }

/* An empty queue still shows its tab, greyed, with the count at zero — "no orders are held" is information, and
   a tab that disappears when it empties makes the row jump about under the cursor. Quiet, not hidden. */
.chip.quiet { opacity: 0.55; }

/* ---- marking up a sketch ----
   The overlay is positioned in percentages so it stays correct at any rendered width. Pixels would tie every
   rectangle to the screen it was placed on, which is the one thing a measured drawing cannot afford. */
.sketchwrap { position: relative; display: block; border: 1px solid var(--line);
  background: var(--surface-alt); margin: 12px 0; }
.sketchwrap.live .sketchimg { cursor: crosshair; }
.sketchimg { display: block; width: 100%; height: auto; }

.sketchzone { position: absolute; margin: 0; padding: 0; cursor: pointer;
  background: color-mix(in oklch, var(--accent) 18%, transparent);
  border: 2px solid var(--accent); display: grid; place-items: center;
  font: inherit; font-size: 11px; color: var(--ink); text-align: center; overflow: hidden; }
.sketchzone span { background: var(--paper); padding: 0 4px; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sketchzone.on { border-width: 3px; box-shadow: 0 0 0 2px var(--paper); z-index: 2; }
/* A keep-clear area is the opposite instruction from a zone, so it does not look like one. */
.sketchzone.clear { border-style: dashed; border-color: var(--bad, #982F26);
  background: repeating-linear-gradient(135deg, transparent 0 6px,
    color-mix(in oklch, var(--bad, #982F26) 14%, transparent) 6px 12px); }
.chip.quiet.on { opacity: 1; }

/* The area a filter hit belongs to. Shown because the whole point of the filter is reaching something without
   knowing which of nine areas holds it — and then not saying where it was would teach nothing. */
.navhit-a { display: none; }
.navarea-t.here + .navitems .navhit-a,
.navitems .navhit-a { display: block; font-size: 9.5px; color: var(--ink-faint);
  padding: 0 14px 4px 16px; letter-spacing: 0.04em; }

/* Two search boxes, stacked, and the second is a different question from the first. */
.gsearch + .gsearch { padding-top: 0; }

/* ---- empty states teach rather than apologise ---- */
.empty { border: 1px dashed var(--line); border-radius: 0; padding: 40px 28px;
  display: grid; gap: 10px; justify-items: center; text-align: center; }
.empty p { margin: 0; color: var(--ink-muted); max-width: 52ch; }

/* ---- the status strip and the toast ---- */
.strip { border-radius: 0; padding: 10px 14px; font-size: 12.5px; margin-bottom: 16px; }
.toast { border-radius: 0; font-size: 13px; padding: 11px 16px; }

/* ---- print: the shop prints work orders and job tickets, so this is not decoration ---- */
@media print {
  .side, .headacts, .bar, .bulk, .toast, .strip, .navitem { display: none !important; }
  .work { padding: 0; }
  .panel { break-inside: avoid; border: 1px solid #000; }
  .tbl th { border-bottom: 1px solid #000; }
}


/* ============================================================================
   SCREENS THAT READ AS FINISHED

   The complaint was that some tabs look primitive, and the cause is structural rather than decorative: a
   heading followed by a bare table sitting on the page background has nothing holding it. Six fixes, each
   for a specific reason a screen looks unfinished.
   ============================================================================ */

/* 1 · A TABLE NEEDS AN EDGE. Without a container the content floats and the screen has no shape.

   Done here rather than by wrapping each table in markup: every table already sits inside .tblwrap, so one
   rule reaches all forty screens. The markup route needed the same literal edit at five call sites whose
   closing parens all differed, which is exactly how a file stops parsing. */
.tblwrap {
  border: 1px solid var(--line);
  background: var(--surface);
  margin-bottom: 18px;
}
.tblwrap .tbl { margin: 0; }
.tblwrap .tbl th:first-child, .tblwrap .tbl td:first-child { padding-left: 16px; }
.tblwrap .tbl th:last-child, .tblwrap .tbl td:last-child { padding-right: 16px; }
.tblwrap .tbl thead th {
  background: var(--surface-alt);
  border-bottom: 1px solid var(--line-strong);
  position: sticky; top: 0; z-index: 1;
}
/* A table already inside a panel keeps the panel's edge rather than gaining a second one. */
.panel .tblwrap { border: 0; background: transparent; margin-bottom: 0; }
.panel .tblwrap .tbl th:first-child, .panel .tblwrap .tbl td:first-child { padding-left: 10px; }

/* 2 · THE HEADING NEEDS WEIGHT. A page title the same size as a field label reads as a fragment. */
.head h1 { font-size: 28px; letter-spacing: -0.022em; }
.head .sub { margin-top: 6px; }
.headacts { align-self: flex-start; padding-top: 3px; }

/* 3 · EMPTY IS A STATE, NOT AN ABSENCE. Most of these screens are empty on a new shop, so the empty state IS
   the screen for weeks — it should teach rather than apologise. */
.empty { padding: 52px 32px; background: var(--surface); border: 1px dashed var(--line-strong); }
.empty::before {
  content: ''; width: 34px; height: 34px; display: block;
  border: 2px solid var(--line-strong); border-radius: 50%; opacity: 0.5; margin-bottom: 4px; }
.empty p:first-of-type { font-size: 15px; color: var(--ink); font-weight: 600; }
.empty p { max-width: 46ch; }

/* 4 · COUNTS AND STATUS READ AT A GLANCE. A number in the same weight as its label is a number nobody sees. */
.kpi { display: flex; gap: 28px; flex-wrap: wrap; padding: 2px 0 4px; }
.kpi-item { display: grid; gap: 2px; }
.kpi-n { font-size: 22px; line-height: 1.1; font-weight: 600; letter-spacing: -0.02em; }
.kpi-l { font-size: 10.5px; font-weight: 600; letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--ink-faint); }

/* 5 · THE BAR ABOVE A LIST IS FURNITURE, NOT CONTENT. Setting it apart stops it competing with the rows. */
.bar { border-bottom: 1px solid var(--line); margin-bottom: 16px; padding-bottom: 14px; }
.bar .in[type=search] { min-width: 220px; }

/* 6 · SECTION HEADINGS INSIDE A PANEL. A panel with an h2 the same weight as its body has no internal order. */
.panel > h2:first-child { padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.panel > h2:first-child + .sub { margin-top: -6px; margin-bottom: 12px; }


/* ---- THE PRODUCT BUILDER ----

   One screen for the verb. Three columns and a strip: the ten decisions on the left, the fields for one of them
   in the middle, what is being produced on the right, and what is wrong along the bottom where it cannot be
   dismissed. Everything is on the existing four colours and the existing spacing scale — a screen this dense is
   exactly where a one-off pixel value starts a second design system. */
.bx { display: grid; gap: var(--s4); }
.bx-head { display: grid; grid-template-columns: minmax(0,1fr) minmax(260px,340px); gap: var(--s5);
  align-items: end; padding-bottom: var(--s4); border-bottom: 1px solid var(--ink); }
.bx-id h1 { margin: 4px 0 6px; }
.bx-idrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.bx-idrow .in { max-width: 260px; }
.bx-state { font-size: 10.5px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase;
  padding: 4px 7px; border: 1px solid var(--line-strong); color: var(--ink-muted); }
.bx-state.live { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.bx-meter { display: grid; gap: 6px; }
.bx-bar { height: 6px; background: var(--surface-alt); border: 1px solid var(--line); }
.bx-bar span { display: block; height: 100%; background: var(--accent); }
.bx-meter .bar { border-bottom: 0; margin: 0; padding: 0; display: flex; gap: 6px; }

.bx-body { display: grid; grid-template-columns: 232px minmax(0,1fr) 340px; gap: var(--s4);
  align-items: start; }

/* The rail. A row states which of four states it is in, and a hidden one states WHY — greying without a reason
   is how somebody concludes a feature is missing. */
.bx-rail { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line);
  position: sticky; top: 12px; }
.bx-railhead { background: var(--surface-alt); font-size: 10.5px; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ink-muted); padding: 8px 10px; }
.bx-rrow { display: grid; grid-template-columns: 18px minmax(0,1fr); gap: 7px; align-items: start;
  text-align: left; background: var(--paper); border: 0; padding: 8px 10px; cursor: pointer;
  font-family: inherit; color: var(--ink); }
.bx-rrow:hover { background: var(--surface-alt); }
.bx-rrow.on { background: var(--surface-alt); box-shadow: inset 2px 0 0 var(--accent); }
.bx-rrow.hidden { opacity: 0.55; }
.bx-rlab { display: grid; gap: 1px; min-width: 0; }
.bx-rlab strong { font-size: 12.5px; }
.bx-rlab .help { font-size: 10.5px; }
.bx-dot { font-size: 11px; line-height: 1.5; color: var(--ink-faint); }
.bx-rrow.complete .bx-dot { color: var(--good); }
.bx-rrow.blocked .bx-dot { color: var(--bad); }
.bx-rrow.blocked .bx-rlab .help { color: var(--bad); }
.bx-orphan, .bx-fam { background: var(--paper); padding: 9px 10px; display: grid; gap: 5px; }
.bx-orphan strong, .bx-fam strong { font-size: 12px; }
.bx-orphan .help, .bx-fam .help { font-size: 10.5px; }

/* The centre column scrolls on its own, so the rail and the previews stay where they were put. */
.bx-centre { min-width: 0; max-height: calc(100vh - 260px); overflow: auto; padding-right: 2px; }
.bx-sechead { padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.bx-sechead h2 { margin: 0 0 3px; }
.bx-sub { padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.bx-sub h3 { margin: 0 0 3px; font-size: 13px; }
/* RULE B, made visible: the consequence reads as the primary help and the schema's definition sits under it. */
.bx-says { color: var(--brass-d); }
.bx-lit { outline: 2px solid var(--accent); outline-offset: 1px; }

.bx-aside { display: grid; gap: var(--s3); position: sticky; top: 12px; }
.bx-aside .panel { margin: 0; }
.bx-aside h3 { margin: 0 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--line); font-size: 13px; }
.bx-card { display: grid; gap: 4px; background: var(--paper); border: 1px solid var(--line);
  padding: 10px; margin-bottom: 10px; }
.bx-shot { width: 100%; height: 130px; object-fit: cover; display: block; }
.bx-noshot { height: 130px; display: grid; place-items: center; background: var(--surface-alt);
  border: 1px dashed var(--line-strong); font-size: 11.5px; color: var(--ink-muted); }
.bx-price { font-family: var(--mono, ui-monospace, monospace); font-variant-numeric: tabular-nums; }
.bx-wo { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 10px; margin-bottom: 12px; }
.bx-wo > div { display: grid; gap: 1px; }
.bx-wo .help { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; }
.bx-wo strong { font-size: 12.5px; }
/* The measured drawing, in percentages so it stays right at any rendered width. */
.bx-draw { position: relative; border: 1px solid var(--line-strong); background: var(--paper);
  margin: 0 0 12px; }
.bx-dz { position: absolute; border: 2px solid var(--accent); background: color-mix(in oklch,
  var(--accent) 10%, transparent); display: grid; align-content: center; justify-items: center;
  font-size: 9.5px; padding: 1px; overflow: hidden; }
.bx-dz b { font-weight: 600; }
.bx-dz i { font-style: normal; font-family: var(--mono, ui-monospace, monospace); }
.bx-dz.clear { border-style: dashed; border-color: var(--bad); background: repeating-linear-gradient(135deg,
  transparent 0 6px, color-mix(in oklch, var(--bad) 14%, transparent) 6px 12px); }
.bx-dim { position: absolute; left: 0; right: 0; bottom: -16px; text-align: center; font-size: 10px;
  font-family: var(--mono, ui-monospace, monospace); color: var(--ink-muted); }
.bx-floor { border-top: 2px solid var(--bad); padding-top: 6px; display: grid; gap: 2px; }
.bx-floor strong { color: var(--bad); font-family: var(--mono, ui-monospace, monospace); }
.tbl tr.warnrow td { background: color-mix(in oklch, var(--warn) 10%, transparent); }
.bx-eff { display: grid; gap: 10px; }
.bx-eff > div { display: grid; gap: 2px; padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.bx-eff > div:last-child { border-bottom: 0; padding-bottom: 0; }

/* THE PROBLEM STRIP. Persistent, never a modal, and never on save — a problem reported at save time is a
   problem reported after the decision was made. */
.bx-strip { border: 1px solid var(--ink); background: var(--surface-alt); padding: 10px 12px;
  display: grid; gap: 8px; }
.bx-strip.ok { border-color: var(--line); }
.bx-striphead { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.bx-problems { display: flex; gap: 6px; flex-wrap: wrap; }
.bx-prob { display: grid; grid-template-columns: auto minmax(0,1fr); gap: 8px; align-items: baseline;
  text-align: left; background: var(--paper); border: 1px solid var(--line); padding: 5px 8px;
  font-family: inherit; font-size: 11.5px; color: var(--ink); cursor: pointer; max-width: 100%; }
.bx-prob:hover { border-color: var(--ink); }
.bx-prob.broken { border-left: 3px solid var(--bad); }
.bx-prob.publish { border-left: 3px solid var(--warn); }
.bx-prob.missing { border-left: 3px solid var(--line-strong); }
.bx-probw { font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); }

@media (max-width: 1400px) {
  .bx-body { grid-template-columns: 200px minmax(0,1fr); }
  .bx-aside { grid-column: 1 / -1; position: static; grid-template-columns: 1fr 1fr; }
}
@media (max-width: 1000px) {
  .bx-head { grid-template-columns: 1fr; }
  .bx-body { grid-template-columns: 1fr; }
  .bx-rail { position: static; }
  .bx-centre { max-height: none; }
  .bx-aside { grid-template-columns: 1fr; }
}


/* ---- THE INLINE-CREATION DRAWER ----
   Over the screen, never instead of it: the builder stays mounted and visible behind, which is the whole reason
   this is a drawer and not a route. Two may be open; the one underneath is dimmed rather than hidden so it is
   obvious what you are coming back to. */
.fld-new { display: flex; gap: 6px; align-items: center; }
.fld-new > :first-child { flex: 1; min-width: 0; }
.fld-under { display: grid; gap: 2px; }
/* STACKED, NOT LAID OUT IN A ROW. As flex siblings the second drawer sat beside the first, which put it over
   the sidebar and interleaved two forms into unreadable text. Both are pinned to the right edge and the top one
   covers the other, which is what "two deep" was always supposed to mean. */
.drawers { position: fixed; inset: 0; z-index: 120;
  background: color-mix(in oklch, var(--ink) 34%, transparent); }
.drawer { position: absolute; top: 0; right: 0; bottom: 0; width: min(520px, 92vw);
  background: var(--paper); border-left: 1px solid var(--ink);
  display: grid; grid-template-rows: auto minmax(0,1fr) auto; box-shadow: -2px 0 0 var(--line); }
/* Visible at the edge so it is obvious what you come back to, and inert so it cannot be pressed through. */
.drawer.under { transform: translateX(-18px) scale(0.99); filter: saturate(0.6); pointer-events: none; }
.drawer.under .drawer-b, .drawer.under .drawer-f { opacity: 0.35; }
.drawer-h { display: flex; gap: 12px; align-items: flex-start; padding: 14px 18px;
  border-bottom: 1px solid var(--ink); }
.drawer-h > div { flex: 1; display: grid; gap: 2px; min-width: 0; }
.drawer-h strong { font-size: 15px; }
.drawer-b { padding: 16px 18px; overflow: auto; display: grid; gap: 12px; align-content: start; }
.drawer-f { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 12px 18px;
  border-top: 1px solid var(--line); background: var(--surface-alt); }
.drawer-f .grow { flex: 1; min-width: 20px; }
.drawer-f .help { max-width: 34ch; }
