/* ==========================================================================
   NASHVILLE LASER CUTTING — Design System
   A division of ELS Nashville

   Instrument Serif headings, Bitter body,
   IBM Plex Mono for ALL technical values (dimensions, cut length, prices,
   order IDs). Accent #F56E0F (exact value from the logo SVG).

   NOTE: the old #nlc-root scoping from the Squarespace build is
   deliberately stripped (spec 8.4) — this is Firebase Hosting now and
   there is no host theme to fight.

   MOBILE FIRST. Spec section 11 flags mobile upload as "the single most
   likely thing to silently break", so the dropzone is a real tap target
   and every control clears 44px of thumb.

   Contents
     01. Fonts
     02. Tokens (light)
     03. Tokens (dark, prefers-color-scheme)
     04. Reset + base
     05. Typography
     06. Layout + grid
     07. Buttons
     08. Dropzone
     09. Selectable chips + cards
     10. Stat readouts (geometry)
     11. Price breakdown
     12. Status pills
     13. Form fields
     14. Tables
     15. Notices + warnings
     16. Spinner + skeleton
     17. Toast
     18. Tabs
     19. Utilities
     20. Print + reduced motion
   ========================================================================== */

/* --------------------------------------------------------------------------
   01. Fonts
   -------------------------------------------------------------------------- */
@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Bitter:ital,wght@0,300..700;1,300..700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

/* --------------------------------------------------------------------------
   02. Tokens — light (the default). Dark mode redefines ONLY these values.
   -------------------------------------------------------------------------- */
:root {
  /* --- palette: warm off-white paper ground, near-black ink --- */
  --paper:        #F7F8F6;   /* page ground */
  --surface:      #FFFFFF;   /* cards, panels */
  --surface-sunk: #EFF1EE;   /* wells, inset rows, table stripes */
  --surface-alt:  #F2F4F1;   /* secondary panel */

  --ink:          #1D2126;   /* primary text, near-black */
  --ink-soft:     #4A5158;   /* secondary text */
  --ink-faint:    #767D84;   /* tertiary / captions */

  /* --- accent: NLC torch orange, sampled from the logo mark ---
     The logo's orange is the brand anchor; every accent here derives from it.
     --accent-strong is darkened deliberately: the logo orange on the paper
     ground is ~2.6:1, far under the 4.5:1 WCAG AA minimum, so orange TEXT and
     small icons use the strong variant while fills use --accent. */
  --accent:        #F56E0F;  /* exact logo orange, read from the SVG source */
  --accent-hover:  #DD6A11;
  --accent-strong: #BC520B;  /* 4.53:1 on paper, 4.83:1 on white — AA text */
  --accent-tint:   #FEF0E4;  /* selected chip fill */
  --accent-ink:    #1D2126;  /* text ON the accent — orange is light, ink wins */

  /* --- muted steel gray, secondary --- */
  --steel:       #3E5C76;
  --steel-soft:  #6B8199;
  --steel-tint:  #ECF0F4;

  /* --- semantic --- */
  --success:      #1E6F4C;
  --success-tint: #E4F1EA;
  --warn:         #9A6206;
  --warn-tint:    #FBF0DC;
  --danger:       #B3261E;
  --danger-tint:  #FBE9E7;
  --info:         #2C5A80;
  --info-tint:    #E7F0F7;

  /* --- lines --- */
  --line:       #D5DAD6;
  --line-strong:#B9C0BB;
  --line-ink:   #1D2126;

  /* --- type families --- */
  --font-display: 'Instrument Serif', 'Iowan Old Style', Georgia, serif;
  /* Bitter — a slab serif with squared-off terminals that sits closer to the
     logo's industrial letterforms than a bookish text face, while staying
     comfortable at paragraph length. */
  --font-body:    'Bitter', Georgia, 'Times New Roman', serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, 'Courier New', monospace;

  /* --- type scale (mobile first; --fs-h* fluid up) --- */
  --fs-3xs:  10px;
  --fs-2xs:  11px;
  --fs-xs:   12px;
  --fs-sm:   14px;
  --fs-base: 16px;
  /* Bitter carries a noticeably larger x-height than a Newsreader-style text
     face, so it reads about a size bigger at the same px. 16px keeps paragraph
     color even; bumping back to 17 makes body copy feel heavy against the
     Instrument Serif headings. */
  --fs-md:   16px;
  --fs-lg:   19px;
  --fs-xl:   22px;
  --fs-h4:  clamp(19px, 1.4vw + 15px, 24px);
  --fs-h3:  clamp(23px, 2.0vw + 17px, 31px);
  --fs-h2:  clamp(31px, 3.6vw + 20px, 52px);
  --fs-h1:  clamp(38px, 5.2vw + 22px, 76px);
  --fs-price: clamp(38px, 6vw + 18px, 58px);

  --lh-tight: 1.05;
  --lh-snug:  1.35;
  /* Slab serifs need a touch more leading than a text serif — the heavier
     stems close up the line otherwise. */
  --lh-body:  1.65;

  --tracking-mono: 0.02em;
  --tracking-label: 0.13em;

  /* --- spacing scale (4px base) --- */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-8:  32px;
  --sp-10: 40px;
  --sp-12: 48px;
  --sp-16: 64px;
  --sp-20: 80px;

  /* --- radius: the brand is CAD-drawing square. Keep it nearly sharp. --- */
  --r-sm: 2px;
  --r-md: 3px;
  --r-lg: 4px;
  --r-pill: 999px;

  /* --- shadows: paper lifting off paper, never glassy --- */
  --shadow-sm: 0 1px 2px rgba(29, 33, 38, 0.06);
  --shadow-md: 0 2px 6px rgba(29, 33, 38, 0.08), 0 1px 2px rgba(29, 33, 38, 0.05);
  --shadow-lg: 0 8px 28px rgba(29, 33, 38, 0.12), 0 2px 6px rgba(29, 33, 38, 0.06);
  --shadow-hard: 3px 3px 0 var(--line-ink);   /* the brand's offset-block hover */

  /* --- motion --- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur-fast: 0.12s;
  --dur: 0.18s;

  /* --- layout --- */
  --wrap: 1120px;
  --wrap-narrow: 760px;
  --tap: 48px;              /* minimum thumb target */

  color-scheme: light;
}

/* --------------------------------------------------------------------------
   03. Tokens — dark. ONLY token values are redefined below. No component
       rule anywhere in this file is duplicated for dark mode.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root {
    --paper:        #16191D;
    --surface:      #1D2126;
    --surface-sunk: #22272D;
    --surface-alt:  #262C33;

    --ink:          #ECEEEA;
    --ink-soft:     #B4BBC1;
    --ink-faint:    #8A9299;

    --accent:        #F56E0F;   /* same logo orange as light mode */
    --accent-hover:  #FF9038;
    --accent-strong: #FF9A4D;   /* brighter so it stays legible on dark */
    --accent-tint:   #35240F;
    --accent-ink:    #16191D;

    --steel:      #8FA8BF;
    --steel-soft: #6B8199;
    --steel-tint: #232C34;

    --success:      #5FD39B;
    --success-tint: #16281F;
    --warn:         #E8B45E;
    --warn-tint:    #2B2213;
    --danger:       #FF8A80;
    --danger-tint:  #2E1917;
    --info:         #7FB6E0;
    --info-tint:    #182631;

    --line:        #333A42;
    --line-strong: #464F58;
    --line-ink:    #566069;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-lg: 0 8px 28px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.35);
    --shadow-hard: 3px 3px 0 var(--line-ink);

    color-scheme: dark;
  }
}

/* --------------------------------------------------------------------------
   04. Reset + base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
}

img, svg, video, canvas { display: block; max-width: 100%; }

a { color: var(--steel); text-underline-offset: 2px; }
a:hover { color: var(--accent-strong); }

/* One focus treatment everywhere. Never remove it. */
:focus-visible {
  outline: 2.5px solid var(--steel);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

::selection { background: var(--accent); color: var(--accent-ink); }

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--sp-6) 0;
}

/* --------------------------------------------------------------------------
   05. Typography
   -------------------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: -0.015em;
  margin: 0 0 var(--sp-4);
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); letter-spacing: -0.01em; }
h4 { font-size: var(--fs-h4); letter-spacing: -0.01em; }

h1 .accent, h2 .accent, h3 .accent {
  color: var(--accent-strong);
  font-style: italic;
}

p { margin: 0 0 var(--sp-4); }
p:last-child { margin-bottom: 0; }

.lede {
  font-size: var(--fs-lg);
  color: var(--ink-soft);
  max-width: 56ch;
}

.muted  { color: var(--ink-soft); }
.faint  { color: var(--ink-faint); }
.small  { font-size: var(--fs-sm); }
.strong { font-weight: 600; }

/* The eyebrow: mono, uppercase, tracked. Used above every section head. */
.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: var(--sp-2);
}

/* ---- MONO: every technical value on the site ----
   Dimensions, cut length, prices, order IDs, quantities, timestamps.
   tabular-nums is load-bearing: prices update live as the customer moves
   the quantity chips, and proportional digits make the total visibly
   jitter as it changes. */
.mono,
.num,
.price,
.order-id,
.dim,
input[type="number"],
code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1, 'zero' 1;
  letter-spacing: var(--tracking-mono);
}

/* Any element holding a number can opt in explicitly. */
.tabular { font-variant-numeric: tabular-nums; }

.order-id {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-soft);
  word-break: break-all;
  user-select: all;              /* one tap selects the whole ID to copy */
}

/* Small uppercase mono label above a value. */
.label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-1);
}

/* --------------------------------------------------------------------------
   06. Layout + grid
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--sp-5);
}
.wrap-narrow { max-width: var(--wrap-narrow); }

.section {
  padding-block: var(--sp-12);
  border-top: 1px solid var(--line);
}
.section:first-child { border-top: 0; }

.stack > * + * { margin-top: var(--sp-4); }
.stack-lg > * + * { margin-top: var(--sp-8); }
.stack-sm > * + * { margin-top: var(--sp-2); }

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
}
.row-between { justify-content: space-between; }
.row-end { justify-content: flex-end; }

/* Auto-fitting responsive grid. Mobile collapses to one column for free. */
.grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.grid-2 { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; }
.grid-3 { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; }
.grid-4 { display: grid; gap: var(--sp-3); grid-template-columns: repeat(2, 1fr); }

@media (min-width: 720px) {
  .grid-2 { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}

/* Sidebar layout: main + rail, stacks under 900px. */
.split { display: grid; gap: var(--sp-8); grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1.5fr 0.9fr; gap: var(--sp-10); }
  .split-even { grid-template-columns: 1fr 1fr; }
}

/* --- card --- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-5);
  box-shadow: var(--shadow-sm);
}
.card-flush { padding: 0; overflow: hidden; }

/* One part of a multi-part order, inside a review card. Indented and ruled on
   the left so three parts read as three blocks rather than one long list. */
.item-block {
  border-left: 3px solid var(--line);
  padding: var(--sp-3) 0 var(--sp-3) var(--sp-4);
}
.item-block + .item-block { margin-top: var(--sp-3); }
.item-block .deflist { margin: var(--sp-2) 0; }
.card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.card-head h3, .card-head h4 { margin: 0; }
.card-accent { border-color: var(--accent); border-width: 1.5px; }
.card-danger { border-color: var(--danger); }

@media (min-width: 720px) {
  .card { padding: var(--sp-6); }
}

/* --------------------------------------------------------------------------
   07. Buttons
       Every button clears --tap (48px) so it is thumb-sized on a phone.
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-6);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  color: var(--ink);
  background: transparent;
  border: 1.5px solid var(--line-ink);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition:
    transform var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* Primary — the accent. One per screen: "Pay and book this run." */
.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--accent-ink);
  transform: translate(-1px, -1px);
  box-shadow: var(--shadow-hard);
}
.btn-primary:active { transform: translate(0, 0); box-shadow: none; }

/* Secondary — outline. */
.btn-secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line-ink);
}
.btn-secondary:hover {
  color: var(--ink);
  border-color: var(--accent);
  transform: translate(-1px, -1px);
  box-shadow: var(--shadow-hard);
}
.btn-secondary:active { transform: translate(0, 0); box-shadow: none; }

/* Ghost — no chrome until hover. */
.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--ink-soft);
}
.btn-ghost:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

/* Destructive — reject, cancel an authorization. */
.btn-danger {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger);
}
.btn-danger:hover {
  background: var(--danger);
  color: var(--surface);
  border-color: var(--danger);
}

/* Confirming / accept. */
.btn-success {
  background: transparent;
  color: var(--success);
  border-color: var(--success);
}
.btn-success:hover {
  background: var(--success);
  color: var(--surface);
}

.btn-sm {
  min-height: 38px;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-sm);
}
.btn-lg {
  min-height: 56px;
  padding: var(--sp-4) var(--sp-8);
  font-size: var(--fs-lg);
}
.btn-block { display: flex; width: 100%; }

.btn:disabled,
.btn[aria-disabled="true"],
.btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}

/* Button rendering a busy state: swap label for a spinner via .is-loading. */
.btn.is-loading {
  color: transparent !important;
  position: relative;
  pointer-events: none;
}
.btn.is-loading::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 18px;
  height: 18px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--accent-ink);
  animation: nlc-spin 0.7s linear infinite;
}
.btn-secondary.is-loading::after,
.btn-ghost.is-loading::after { color: var(--ink); }

/* A link that behaves as a quiet inline action. */
.link-action {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--steel);
  text-decoration: underline;
  cursor: pointer;
}
.link-action:hover { color: var(--accent-strong); }

/* --------------------------------------------------------------------------
   08. Dropzone
       Spec 8.1 step 2: "Large drag-and-drop target that is ALSO a tap
       target on mobile." Mobile is the risk case, so this is sized big
       first and only grows on desktop.
   -------------------------------------------------------------------------- */
.dropzone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: 200px;                 /* real tap target on a phone */
  padding: var(--sp-8) var(--sp-5);
  text-align: center;
  background: var(--surface-sunk);
  border: 2px dashed var(--line-strong);
  border-radius: var(--r-lg);
  cursor: pointer;
  transition:
    border-color var(--dur) var(--ease),
    background var(--dur) var(--ease);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

@media (min-width: 720px) {
  .dropzone { min-height: 260px; padding: var(--sp-12) var(--sp-8); }
}

.dropzone:hover,
.dropzone:focus-within {
  border-color: var(--accent);
  background: var(--accent-tint);
}

/* dragover — the strongest affirmative state */
.dropzone.is-dragover {
  border-color: var(--accent);
  border-style: solid;
  background: var(--accent-tint);
  box-shadow: inset 0 0 0 3px var(--accent);
}

.dropzone.is-error {
  border-color: var(--danger);
  background: var(--danger-tint);
}
.dropzone.is-busy { cursor: progress; border-color: var(--steel); }

.dropzone.has-file {
  border-style: solid;
  border-color: var(--success);
  background: var(--success-tint);
  min-height: 0;
  padding: var(--sp-5);
}

/* The native input is visually hidden but stays keyboard-reachable and,
   critically, still the element the tap actually hits on iOS Safari. */
.dropzone input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.dropzone-icon {
  width: 40px;
  height: 40px;
  color: var(--steel);
  stroke: currentColor;
  fill: none;
  stroke-width: 1.5;
}
.dropzone.is-error .dropzone-icon { color: var(--danger); }
.dropzone.has-file .dropzone-icon { color: var(--success); }

.dropzone-title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
  color: var(--ink);
}
.dropzone-sub {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.dropzone-hint {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  max-width: 44ch;
}
.dropzone-error {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--danger);
}

/* Upload progress bar under the zone. */
.progress {
  width: 100%;
  height: 4px;
  background: var(--surface-sunk);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: inherit;
  transition: width 0.25s var(--ease);
}

/* --------------------------------------------------------------------------
   09. Selectable chips + cards
       Radio-like controls for material / thickness / quantity tier / lead
       time. Spec 8.1 steps 5-7: "visual chips or cards, not nested
       dropdowns." Built on real inputs so keyboard + screen readers work.
   -------------------------------------------------------------------------- */
.choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  border: 0;
}
.choices-grid {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
  margin: 0;
  padding: 0;
  border: 0;
}
.choices legend {
  padding: 0;
  margin-bottom: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* The hidden input carries state; the label is the visible control. */
.choice { position: relative; display: flex; }
.choice input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

/* --- chip: compact, for thickness + quantity tiers --- */
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  text-align: center;
  transition:
    border-color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.chip:hover { border-color: var(--accent); color: var(--ink); }

.choice input:checked + .chip {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--ink);
  font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--accent);
}
.choice input:focus-visible + .chip {
  outline: 2.5px solid var(--steel);
  outline-offset: 2px;
}
.choice input:disabled + .chip {
  opacity: 0.4;
  cursor: not-allowed;
}

/* --- card choice: roomier, for material + lead time --- */
.choice-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--r-md);
  cursor: pointer;
  text-align: left;
  transition:
    border-color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.choice-card:hover { border-color: var(--accent); }

.choice input:checked + .choice-card {
  border-color: var(--accent);
  background: var(--accent-tint);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.choice input:focus-visible + .choice-card {
  outline: 2.5px solid var(--steel);
  outline-offset: 2px;
}
.choice input:disabled + .choice-card { opacity: 0.4; cursor: not-allowed; }

.choice-name {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
  line-height: var(--lh-snug);
}
.choice-meta {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}

/* The per-part price that falls as the customer taps up the quantity —
   spec calls this the highest-value interaction on the page, so it gets
   the accent when selected. */
.choice-price {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent-strong);
}
.choice input:checked + .chip .choice-price,
.choice input:checked + .choice-card .choice-price { color: var(--accent-strong); }

/* Tier chip stacks its label over its per-part price. */
.chip-tier {
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  min-height: 62px;
}
.chip-tier .chip-qty {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
}
.chip-tier .chip-each {
  font-size: var(--fs-2xs);
  color: var(--ink-faint);
}
.choice input:checked + .chip-tier .chip-each { color: var(--accent-strong); }

/* --------------------------------------------------------------------------
   10. Stat readouts — parsed geometry (cut length, pierces, bbox)
   -------------------------------------------------------------------------- */
.stats {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 620px) {
  .stats { grid-template-columns: repeat(4, 1fr); }
}
.stats-3 { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 620px) { .stats-3 { grid-template-columns: repeat(3, 1fr); } }

.stat {
  padding: var(--sp-3);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.stat-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--sp-1);
}
.stat-value {
  display: block;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink);
  line-height: 1.2;
}
.stat-unit {
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--ink-faint);
}

/* Part preview canvas / svg well. */
.preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 180px;
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.preview svg, .preview canvas { width: 100%; height: auto; max-height: 260px; }
.preview-outline { fill: none; stroke: var(--ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.preview-empty {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}

/* --------------------------------------------------------------------------
   11. Price breakdown
       Spec 8.1 step 8: "Never a bare total. The breakdown is the
       trust-builder." Mono numerals, right-aligned, rule above the total.
   -------------------------------------------------------------------------- */
.pricebox {
  background: var(--surface);
  border: 1.5px solid var(--accent);
  border-radius: var(--r-md);
  padding: var(--sp-5);
}

.price-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-4);
}
.price-total {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-price);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.price-per {
  display: block;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--accent-strong);
  margin-top: var(--sp-2);
}

/* The breakdown table. Right column is mono + tabular so nothing shifts. */
.breakdown {
  width: 100%;
  border-collapse: collapse;
  margin-top: var(--sp-4);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
}
.breakdown th,
.breakdown td {
  padding: var(--sp-2) 0;
  text-align: left;
  vertical-align: baseline;
  border: 0;
}
.breakdown td:last-child,
.breakdown th:last-child {
  text-align: right;
  white-space: nowrap;
  padding-left: var(--sp-4);
  font-variant-numeric: tabular-nums;
}
.breakdown tbody tr > * { color: var(--ink-soft); }
.breakdown tbody tr td:last-child { color: var(--ink); font-weight: 500; }

/* A credit (quantity discount) reads in the accent, not as an error. */
.breakdown .is-credit td:last-child { color: var(--success); }
.breakdown .is-add td:last-child { color: var(--warn); }

/* The parts subtotal on a multi-part order: a light rule above it separates
   the per-part lines from the order-level charges that follow, so the column
   reads as "parts, then what the ORDER costs on top". */
.breakdown .is-subtotal td {
  padding-top: var(--sp-2);
  border-top: 1px solid var(--line);
  font-weight: 600;
  color: var(--ink);
}

/* Rule above the total, per the brief. */
.breakdown tfoot td {
  padding-top: var(--sp-3);
  border-top: 1.5px solid var(--line-ink);
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink) !important;
}

.breakdown caption {
  caption-side: top;
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-bottom: var(--sp-2);
}

/* "Order minimum applied" and similar footnotes. */
.price-note {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   12. Status pills — job + payment state
   -------------------------------------------------------------------------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 5px var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  border: 1px solid currentColor;
  border-radius: var(--r-pill);
  line-height: 1.5;
}
.pill::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.pill-neutral { color: var(--ink-soft);  background: var(--surface-sunk); }
.pill-info    { color: var(--info);      background: var(--info-tint); }
.pill-steel   { color: var(--steel);     background: var(--steel-tint); }
.pill-warn    { color: var(--warn);      background: var(--warn-tint); }
.pill-accent  { color: var(--accent-strong); background: var(--accent-tint); }
.pill-success { color: var(--success);   background: var(--success-tint); }
.pill-danger  { color: var(--danger);    background: var(--danger-tint); }

/* Live/polling indicator. */
.pill-live::before { animation: nlc-pulse 1.8s var(--ease) infinite; }

/* --------------------------------------------------------------------------
   13. Form fields
   -------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field + .field { margin-top: var(--sp-4); }

.field > label,
.field-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.field .req { color: var(--danger); }

.input,
.select,
.textarea,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="tel"],
select,
textarea {
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--fs-base);   /* >=16px: iOS Safari zooms the page below this */
  line-height: 1.4;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color var(--dur-fast) var(--ease);
  -webkit-appearance: none;
  appearance: none;
}

input[type="number"],
.input-mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

textarea { min-height: 96px; resize: vertical; line-height: var(--lh-body); }

select {
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 12px) 50%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: var(--sp-10);
}

.input:hover, select:hover, textarea:hover { border-color: var(--ink-faint); }
.input:focus, select:focus, textarea:focus,
input:focus { border-color: var(--accent); }

input::placeholder, textarea::placeholder { color: var(--ink-faint); opacity: 1; }

input:disabled, select:disabled, textarea:disabled {
  background: var(--surface-sunk);
  color: var(--ink-faint);
  cursor: not-allowed;
}

/* Invalid state, driven by a class so it never fires while typing. */
.input.is-invalid,
textarea.is-invalid,
select.is-invalid { border-color: var(--danger); background: var(--danger-tint); }

.field-error {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--danger);
}
.field-help { font-size: var(--fs-sm); color: var(--ink-soft); }

/* Inline label + input pair, for the config editor's dense rows. */
.field-inline {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 620px) {
  .field-inline { grid-template-columns: 1fr 140px; gap: var(--sp-4); }
  .field-inline > label { text-transform: none; font-family: var(--font-body); font-size: var(--fs-base); letter-spacing: 0; color: var(--ink); }
}

/* --------------------------------------------------------------------------
   14. Tables (admin lists, config editor)
   -------------------------------------------------------------------------- */
.table-scroll {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  font-size: var(--fs-sm);
}
.table th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--sp-3);
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--steel);
  background: var(--surface-sunk);
  border-bottom: 1.5px solid var(--line-ink);
  white-space: nowrap;
}
.table td {
  padding: var(--sp-3);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover td { background: var(--surface-sunk); }
.table .num, .table td.num { text-align: right; }

/* --------------------------------------------------------------------------
   15. Notices + warnings
       Parser warnings must be prominent in the admin review queue —
       they are the DFM signal Ross acts on.
   -------------------------------------------------------------------------- */
.notice {
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-faint);
  border-radius: var(--r-sm);
  background: var(--surface-sunk);
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}
.notice strong { color: var(--ink); }
.notice-info    { border-left-color: var(--info);    background: var(--info-tint);    color: var(--ink); }
.notice-warn    { border-left-color: var(--warn);    background: var(--warn-tint);    color: var(--ink); }
.notice-danger  { border-left-color: var(--danger);  background: var(--danger-tint);  color: var(--ink); }
.notice-success { border-left-color: var(--success); background: var(--success-tint); color: var(--ink); }
.notice-accent  { border-left-color: var(--accent);  background: var(--accent-tint);  color: var(--ink); }

.notice-title {
  display: block;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: var(--sp-1);
}

/* Warning list — prominent, mono, one per line. */
.warnings { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.warnings li {
  display: flex;
  gap: var(--sp-2);
  padding: var(--sp-3);
  background: var(--warn-tint);
  border: 1px solid var(--warn);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink);
}
.warnings li::before {
  content: '!';
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--warn);
  border: 1.5px solid var(--warn);
  border-radius: 50%;
}

.empty-state {
  padding: var(--sp-12) var(--sp-5);
  text-align: center;
  color: var(--ink-faint);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-md);
}
.empty-state h3 { color: var(--ink-soft); margin-bottom: var(--sp-2); }

/* --------------------------------------------------------------------------
   16. Spinner + skeleton
   -------------------------------------------------------------------------- */
.spinner {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: nlc-spin 0.7s linear infinite;
  flex-shrink: 0;
}
.spinner-lg { width: 34px; height: 34px; border-width: 3px; }

.loading-block {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-12) var(--sp-5);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.skeleton {
  background: linear-gradient(90deg, var(--surface-sunk) 25%, var(--surface-alt) 37%, var(--surface-sunk) 63%);
  background-size: 400% 100%;
  border-radius: var(--r-sm);
  animation: nlc-shimmer 1.4s ease infinite;
  color: transparent !important;
  min-height: 1em;
}

@keyframes nlc-spin { to { transform: rotate(360deg); } }
@keyframes nlc-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@keyframes nlc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
@keyframes nlc-toast-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* --------------------------------------------------------------------------
   17. Toast
   -------------------------------------------------------------------------- */
.toast-host {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: min(440px, calc(100vw - var(--sp-8)));
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-sm);
  pointer-events: auto;
  animation: nlc-toast-in 0.22s var(--ease);
}
.toast-success { border-left: 3px solid var(--success); }
.toast-warn    { border-left: 3px solid var(--warn); }
.toast-danger  { border-left: 3px solid var(--danger); }
.toast-close {
  margin-left: auto;
  background: none;
  border: 0;
  color: inherit;
  opacity: 0.6;
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  padding: 0 var(--sp-1);
}
.toast-close:hover { opacity: 1; }

/* --------------------------------------------------------------------------
   18. Tabs (admin console)
   -------------------------------------------------------------------------- */
.tabs {
  display: flex;
  gap: var(--sp-1);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-bottom: 1.5px solid var(--line);
  margin-bottom: var(--sp-6);
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  flex-shrink: 0;
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-4);
  background: none;
  border: 0;
  border-bottom: 2.5px solid transparent;
  margin-bottom: -1.5px;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.tab:hover { color: var(--ink); }
.tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }

.tab-count {
  display: inline-block;
  margin-left: var(--sp-2);
  padding: 1px 7px;
  font-size: var(--fs-3xs);
  font-variant-numeric: tabular-nums;
  color: var(--accent-ink);
  background: var(--accent);
  border-radius: var(--r-pill);
}

/* Kanban columns for the production board. */
.board {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .board { grid-template-columns: repeat(4, minmax(0, 1fr)); align-items: start; }
}
.board-col {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-3);
  min-height: 120px;
}
.board-col-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-soft);
}
.board-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-3);
  margin-bottom: var(--sp-2);
  box-shadow: var(--shadow-sm);
}
.board-card:last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   19. Utilities
   -------------------------------------------------------------------------- */
.hidden, [hidden] { display: none !important; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.text-right  { text-align: right; }
.text-center { text-align: center; }
.nowrap      { white-space: nowrap; }
.full        { width: 100%; }
.mt-0 { margin-top: 0; }
.mt-2 { margin-top: var(--sp-2); }
.mt-4 { margin-top: var(--sp-4); }
.mt-6 { margin-top: var(--sp-6); }
.mt-8 { margin-top: var(--sp-8); }
.mb-0 { margin-bottom: 0; }
.mb-4 { margin-bottom: var(--sp-4); }
.mb-6 { margin-bottom: var(--sp-6); }

/* Page masthead shared by status + admin. */
.masthead {
  border-bottom: 1.5px solid var(--line-ink);
  background: var(--surface);
}
.masthead-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-block: var(--sp-3);
}
.wordmark {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
  /* the logo lockup sits inline with the text fallback */
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
}
.wordmark span { color: var(--accent-strong); }

/* --------------------------------------------------------------------------
   Logo

   Horizontal lockup, ~2.08:1 — the NLC monogram with the wordmark stacked
   beside it. SVG with all type converted to outlines, so it renders identically
   everywhere and carries no font dependency (the original export referenced
   "Guilty Treasure" as live text, which only looked right on machines that
   had the font installed).

   The artwork is black-on-transparent with the orange accent. On the dark
   ground the black would vanish, so it is inverted there; hue-rotate puts the
   orange back where it belongs after the invert. A purpose-made light-on-dark
   asset would be cleaner and can drop in later without touching any caller.
   -------------------------------------------------------------------------- */
.logo-mark {
  display: block;
  height: 44px;
  /* The <img> carries width/height attributes (1589x763) to reserve layout
     space and prevent CLS. Without an explicit max-width those attributes win
     and the mark renders at its full intrinsic width, so both axes are pinned
     here: height drives the size, width follows the aspect ratio. */
  width: auto;
  max-width: 100%;
  flex: none;
}
@media (min-width: 700px) {
  .logo-mark { height: 56px; }
}
@media (prefers-color-scheme: dark) {
  .logo-mark { filter: invert(1) hue-rotate(180deg); }
}

/* Footer: the same lockup with room to breathe. */
.logo-lockup {
  display: block;
  width: auto;
  height: 56px;
  margin-bottom: var(--sp-4);
}
@media (prefers-color-scheme: dark) {
  .logo-lockup { filter: invert(1) hue-rotate(180deg); }
}
.masthead-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.page-footer {
  border-top: 1px solid var(--line);
  padding-block: var(--sp-6);
  margin-top: var(--sp-16);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Definition-list used for spec + geometry readouts. */
.deflist {
  display: grid;
  gap: var(--sp-2) var(--sp-4);
  grid-template-columns: auto 1fr;
  margin: 0;
  font-size: var(--fs-sm);
}
.deflist dt {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  align-self: baseline;
}
.deflist dd {
  margin: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}
.deflist dd.text { font-family: var(--font-body); }

/* --------------------------------------------------------------------------
   20. Print + reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .btn:hover { transform: none !important; box-shadow: none !important; }
}

@media print {
  body { background: #fff; color: #000; }
  .no-print, .tabs, .toast-host, .btn { display: none !important; }
  .card, .pricebox { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
}

/* --------------------------------------------------------------------------
   21. Material stock + lead time  (ADDITIVE — appended, restyles nothing)

   Every thickness carries a stock status. Two of the three are visible to the
   customer:

     lead_time    still sells. Gets a small calm badge on the chip and an
                  honest disclosure above the pay button.
     unavailable  never rendered, so it has no styles here at all.

   COLOUR POLICY. No new colour is introduced — every rule below reaches for a
   token that already exists. The lead-time badge is deliberately STEEL, not
   warn or danger: ordering material in is a normal thing to buy, and painting
   it like an error would talk a customer out of a sale the shop wants. The one
   place that escalates is the rush-vs-lead-time contradiction, which uses the
   existing --warn pair because a customer about to buy "same day" on a
   five-day lead genuinely is about to make a mistake.
   -------------------------------------------------------------------------- */

/* Thickness chip: label and badge sit on one line, badge trailing. .chip is
   already an inline-flex row with a gap, so this only needs the two children
   to behave — no layout override on .chip itself. */
.chip-th {
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  color: inherit;
}

/* "+5d". Small, quiet, steel — a fact on a price tag, not a warning label. */
.chip-lead {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-3xs);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.04em;
  padding: 3px 5px;
  border-radius: var(--r-sm);
  color: var(--steel);
  background: var(--steel-tint);
}
/* On the selected chip the accent tint is already behind it, so the badge
   drops its own fill and keeps only the text — otherwise two tinted blocks
   stack and the chip reads muddy. */
.choice input:checked + .chip .chip-lead {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--steel-soft);
}

/* The line under the turnaround chips. */
.lead-stock-note {
  margin-top: var(--sp-4);
  padding-left: var(--sp-3);
  border-left: 2px solid var(--line-strong);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--ink-soft);
  max-width: 68ch;
}
/* Rush turnaround chosen on material we have to order in. The customer is one
   click from buying a contradiction, so this one is allowed to raise its
   voice — using the warn tokens that already exist. */
.lead-stock-note.is-conflict {
  border-left-color: var(--warn);
  background: var(--warn-tint);
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-weight: 500;
}

/* The disclosure inside the price box, above the pay button. .notice supplies
   the frame; this only fixes the spacing in its new home. */
.stock-note {
  margin-top: var(--sp-4);
  margin-bottom: var(--sp-2);
}
.stock-note .notice-title { font-size: var(--fs-base); }
.stock-note p { max-width: 66ch; }

/* --------------------------------------------------------------------------
   UNAVAILABLE STOCK — the disabled chip state
   Added with the live-config swap (public/js/app.js). Additive: nothing above
   this line is modified, and no new colour token is introduced.

   WHY A DISABLED CHIP EXISTS AT ALL. The server omits unavailable thicknesses
   from getPublicConfig, so normally none of this renders. It renders when the
   page is running on the BUNDLED FALLBACK — the baked-in config still lists
   every thickness the shop ever offered — and in that case the customer must
   be able to see, unmistakably, that a gauge is not on the table. Silently
   dropping it there would leave the fallback advertising metal we cannot cut.

   COLOUR IS NEVER THE ONLY SIGNAL. Each disabled chip also carries the literal
   word "Unavailable" in its label plus a visually-hidden sentence for screen
   readers; the rules below are the visual half of that pair, never the whole
   of it. The real non-interactivity comes from the underlying radio being
   `disabled` — out of the tab order, deaf to clicks — not from this CSS.
   -------------------------------------------------------------------------- */

/* The wrapper stops swallowing the pointer so the not-allowed cursor from the
   label is what the customer actually sees. The input is absolutely positioned
   over the whole chip, and a disabled input would otherwise just eat the
   event and show the default arrow. */
.choice.is-disabled { cursor: not-allowed; }
.choice.is-disabled input { cursor: not-allowed; pointer-events: none; }

/* Unmistakably inert: dimmed, desaturated, flat — no hover lift, no shadow.
   0.45 keeps the text legible enough to read the gauge and the word
   "Unavailable" while reading as clearly switched-off next to a live chip. */
.choice.is-disabled > .chip,
.choice.is-disabled > .choice-card {
  opacity: 0.45;
  cursor: not-allowed;
  background: var(--surface-sunk);
  border-color: var(--line);
  border-style: dashed;
  color: var(--ink-faint);
  box-shadow: none;
}

/* Kill every affordance the live chips have. Hover, focus and checked styles
   must not fire here — a chip that lights up under the cursor reads as
   clickable no matter how dim it is. */
.choice.is-disabled > .chip:hover,
.choice.is-disabled > .choice-card:hover {
  border-color: var(--line);
  color: var(--ink-faint);
  background: var(--surface-sunk);
  box-shadow: none;
  transform: none;
}
.choice.is-disabled > .choice-card .choice-name,
.choice.is-disabled > .choice-card .choice-meta {
  color: var(--ink-faint);
}

/* "Unavailable" on a thickness chip. Same geometry as .chip-lead so the two
   badges sit identically in the row, but muted rather than steel: this one is
   a closed door, not a fact about a purchase the customer can still make. */
.chip-gone {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 5px;
  border-radius: var(--r-sm);
  color: var(--ink-faint);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--line-strong);
  white-space: nowrap;
}

/* Users who ask for less motion still get the full static treatment above —
   nothing here animates — but the dashed border can shimmer on some displays
   at small sizes, so drop it to solid alongside the rest of the app's
   reduced-motion handling. */
@media (prefers-reduced-motion: reduce) {
  .choice.is-disabled > .chip,
  .choice.is-disabled > .choice-card { border-style: solid; }
}


/* --------------------------------------------------------------------------
   PART TABS — one tab per part in the order.
   Replaces an earlier hidden "active row" model that customers found
   confusing: the material and quantity controls applied to whichever row was
   selected, and nothing said so. A tab carries that meaning for free.
   -------------------------------------------------------------------------- */
.parttabs { margin: var(--sp-4) 0; }

.parttab-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: stretch;
}

.parttab {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  position: relative;
  min-width: 9rem;
  max-width: 16rem;
  padding: var(--sp-2) var(--sp-4) var(--sp-2) var(--sp-3);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-bottom-width: 3px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  font: inherit;
  color: var(--ink-soft);
  text-align: left;
  cursor: pointer;
}
.parttab:hover { color: var(--ink); }

/* The selected tab: the one the controls below are editing. */
.parttab.is-active {
  background: var(--surface);
  border-color: var(--line);
  border-bottom-color: var(--accent);
  color: var(--ink);
}

.parttab-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  font-size: var(--fs-sm);
}
.parttab-sub {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  color: var(--ink-soft);
}

.parttab-remove {
  position: absolute;
  top: 2px;
  right: 4px;
  line-height: 1;
  padding: 2px 4px;
  font-size: 14px;
  color: var(--ink-soft);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.parttab-remove:hover { color: var(--danger); background: var(--surface-sunk); }

/* The add tab reads as an action, not as another part. */
.parttab-add {
  justify-content: center;
  min-width: auto;
  border-style: dashed;
  border-bottom-width: 1px;
  background: transparent;
  /* --accent-strong, not --accent: the logo orange is only 2.77:1 on the page
     ground, which fails AA for text. --accent-strong is the palette's
     accessible accent and carries its own dark-mode value. */
  color: var(--accent-strong);
  font-weight: 600;
  font-size: var(--fs-sm);
}
.parttab-add:hover { background: var(--accent-tint); }
