@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/vendor/fonts/inter-latin-wght-normal.woff2') format('woff2-variations');
}
@font-face {
  font-family: 'Space Grotesk Variable';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/vendor/fonts/space-grotesk-latin-wght-normal.woff2') format('woff2-variations');
}

/* ===========================================================================
 * A-Z Kitchens — the workshop palette, ported from JoineryFlow's DESIGN.md.
 * Token names and values are the same, so the vocabulary carries between the
 * two apps.
 *
 * The use scene is the same argument: one person on their feet, holding a
 * phone, often under a roller door in daylight. So this is paper-first with a
 * night theme rather than a dark app. It is a tool, not a brand surface — the
 * job is to disappear into the task, so familiarity is a feature and surprise
 * is a cost.
 *
 * Fonts are served from /vendor, never a CDN, for the same reason the ZXing
 * bundle is: a blocked domain must not be able to stop the floor working.
 * =========================================================================== */

:root {
  /* brand — mustard/ochre. Waiting on you. Bright at 300-500 where it ALWAYS
     takes ink lettering; deep bronze at 600+ where white works. */
  --brand-50: #fbf6e6;
  --brand-100: #f6e9c0;
  --brand-200: #eed88a;
  --brand-300: #e4c452;
  --brand-400: #d6ae2b;
  --brand-500: #bd9418;
  --brand-600: #87610f;
  --brand-700: #74530f;
  --brand-800: #5d4310;
  --brand-900: #4b3711;

  /* moss — done, agreed, gone. */
  --moss-50: #f3f5ee;
  --moss-100: #e4e9da;
  --moss-200: #cbd5ba;
  --moss-300: #aabb93;
  --moss-400: #8b9e70;
  --moss-500: #6e8155;
  --moss-600: #566743;
  --moss-700: #455237;
  --moss-800: #37422e;
  --moss-900: #2c3526;

  /* clay — late, damaged, destructive. Deliberately NOT the brand, so nothing
     routine can shout. 600 is a fill; set clay words in 700. */
  --clay-100: #f7ded4;
  --clay-300: #eaa78b;
  --clay-400: #e07a58;
  --clay-500: #d4603c;
  --clay-600: #b94b2c;
  --clay-700: #93381f;

  /* sky — "this came from somewhere else". Here it has exactly one job: the
     piece is out of the building, at the finisher. That is the only state
     where you do not physically have the item, which is the whole reason the
     poly in/out feature exists. Spending sky on anything routine would cost
     that signal. 600 is a fill; set sky words in 700. */
  --sky-50: #f0f9ff;
  --sky-100: #dff2fe;
  --sky-200: #b8e6fe;
  --sky-600: #0084d1;
  --sky-700: #0069a8;

  /* Paper: a sheet on the bench, not a white browser panel. */
  --paper: #fbf8ee;
  --sand: #e8e3d2;

  --ink: #1c1f19;
  --ink-700: #333830;
  /* Secondary text, composited to a solid value. There is no third, fainter
     tier: below this, text stops being readable and starts being decoration. */
  --ink-muted: #52534a;

  --night-800: #262b22;
  --night-850: #1f231c;
  --night-900: #191d17;
  --night-950: #0e110d;
  --night-line: #333a2e;
  --night-muted: #9ca396;

  --font-sans: 'Inter Variable', ui-sans-serif, system-ui, -apple-system,
    'Segoe UI', Roboto, sans-serif;
  --font-display: 'Space Grotesk Variable', var(--font-sans);

  --radius: 18px;
  --radius-lg: 26px;
  --tap: 56px; /* minimum comfortable target with gloves on */

  --shadow-paper:
    0 0 0 1px rgba(28, 31, 25, 0.06),
    0 10px 24px -18px rgba(28, 31, 25, 0.5),
    0 2px 5px -3px rgba(28, 31, 25, 0.08);
  --shadow-lift:
    0 8px 20px -8px rgba(28, 31, 25, 0.16),
    0 2px 6px -2px rgba(28, 31, 25, 0.08);

  /* Semantic surface roles, flipped wholesale by the night theme below. */
  --bg: var(--sand);
  --surface: var(--paper);
  --surface-2: #f4f0e2;
  --text: var(--ink);
  --muted: var(--ink-muted);
  --line: rgba(28, 31, 25, 0.07);
  --line-strong: rgba(28, 31, 25, 0.14);
  --shadow: var(--shadow-paper);
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Deep olive-charcoal, so the night theme is the same green shop after
       dark rather than a neutral grey app. Secondary text is night-muted
       (olive) — reaching for a grey here is the drift to watch, because it
       passes contrast and so nothing catches it. */
    --bg: var(--night-950);
    --surface: var(--night-900);
    --surface-2: var(--night-850);
    --text: #eceee9;
    --muted: var(--night-muted);
    --line: rgba(255, 255, 255, 0.07);
    --line-strong: var(--night-line);
    --shadow:
      0 20px 48px -18px rgba(0, 0, 0, 0.75),
      inset 0 1px 0 0 rgba(255, 255, 255, 0.05);
  }
}

* {
  box-sizing: border-box;
}

/* --------------------------------------------------------------- base -- */

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.45;
  -webkit-text-size-adjust: 100%;
}

/* The display face is for headings and big editorial figures only. It does not
   go on body text, labels, table cells, list rows or any readout — data is set
   in the one family, and tabular-nums does the work a display face would
   otherwise be asked for. */
h1,
h2,
h3 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
}

a {
  color: var(--moss-700);
  text-underline-offset: 2px;
}

@media (prefers-color-scheme: dark) {
  a {
    color: var(--moss-300);
  }
}

.wrap {
  max-width: 1100px;
  margin: 0 auto;
  padding: 16px;
}
.wrap--narrow {
  max-width: 620px;
}

/* ------------------------------------------------------------- chrome -- */

header.bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 20;
}
header.bar h1 {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  white-space: nowrap;
}
/* Nothing in the bar wraps: at 420px the title, the links and the sign-out
   button were each breaking across two lines and tripling its height. */
header.bar a,
header.bar .who,
header.bar button {
  white-space: nowrap;
}
header.bar button {
  min-height: 40px;
  padding: 8px 14px;
}
@media (max-width: 520px) {
  header.bar {
    gap: 8px;
    padding: 10px 12px;
  }
  header.bar h1 {
    font-size: 16px;
  }
  /* The account name is the first thing to go: it is the least useful word in
     the bar and the only one that is not a control. */
  header.bar .who {
    display: none;
  }
}
header.bar .spacer {
  flex: 1;
}
header.bar .who {
  color: var(--muted);
  font-size: 13px;
}

nav.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 16px 0;
}
nav.tabs button {
  min-height: 44px;
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  background: var(--surface);
  color: var(--muted);
  border: 1px solid var(--line);
}
/* The selected tab or chip anywhere in the app: mustard with ink lettering. */
nav.tabs button[aria-selected='true'] {
  background: var(--brand-300);
  color: var(--ink);
  border-color: transparent;
  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.4);
}
@media (prefers-color-scheme: dark) {
  nav.tabs button[aria-selected='true'] {
    background: var(--brand-400);
  }
}

/* ------------------------------------------------------------ surface -- */

.card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 20px;
  margin: 16px 0;
  box-shadow: var(--shadow);
}
.card h2 {
  margin: 0 0 12px;
  font-size: 17px;
}
.card h3 {
  margin: 16px 0 8px;
  font-size: 14px;
  color: var(--muted);
}

.muted {
  color: var(--muted);
}
.small {
  font-size: 13px;
}
.warn {
  color: var(--clay-700);
}
@media (prefers-color-scheme: dark) {
  .warn {
    color: var(--clay-300);
  }
}

/* ------------------------------------------------------------ buttons -- */

button,
.btn {
  font: inherit;
  font-weight: 600;
  min-height: var(--tap);
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: filter 150ms ease-out, transform 150ms ease-out;
}
button:active {
  transform: scale(0.98);
}
button:disabled {
  opacity: 0.5;
  pointer-events: none;
}

/* The mustard button: flat-ish paint with ink lettering, like a stencilled
   sign rather than a glowing gradient. Identical in both themes, so "the thing
   to tap" is always the yellow pill. Never white on mustard below 600. */
button.primary {
  background-image: linear-gradient(160deg, #ecd066 0%, #d9ae23 100%);
  color: var(--ink);
  border-color: transparent;
  box-shadow:
    0 6px 18px -10px rgba(143, 103, 16, 0.55),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.45);
}
button.primary:hover {
  filter: brightness(1.04);
}

/* The green button: the quiet second-tier "do it". */
button.ok {
  background-image: linear-gradient(160deg, #4c5b3b 0%, #333f2a 100%);
  color: var(--moss-50);
  border-color: transparent;
  box-shadow:
    0 6px 18px -10px rgba(44, 53, 38, 0.6),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.14);
}
button.ok:hover {
  filter: brightness(1.12);
}

button.danger {
  background: var(--clay-600);
  color: #fff;
  border-color: transparent;
}
button.danger:hover {
  background: var(--clay-500);
}

button.ghost {
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--muted);
}
button.ghost:hover {
  color: var(--text);
}

button.big {
  width: 100%;
  min-height: 64px;
  font-size: 17px;
}

/* Focus is a moss ring — mustard on cream is too quiet to find. */
input:focus,
select:focus,
textarea:focus,
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--moss-600);
  outline-offset: 2px;
}
@media (prefers-color-scheme: dark) {
  input:focus,
  select:focus,
  textarea:focus,
  button:focus-visible,
  a:focus-visible {
    outline-color: var(--moss-400);
  }
}

.row {
  display: flex;
  gap: 10px;
  align-items: center;
}
.row > * {
  min-width: 0;
}
.grow {
  flex: 1;
}

/* -------------------------------------------------------------- forms -- */

input,
select,
textarea {
  font: inherit;
  width: 100%;
  min-height: 48px;
  padding: 10px 14px;
  border-radius: 14px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
}
input::placeholder,
textarea::placeholder {
  color: var(--muted);
}

label {
  display: block;
  margin-bottom: 12px;
}
/* Sentence case, not uppercase. Tracked caps turn a form into a wall of
   shouting, and these are read while standing in a workshop. */
label span {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
}

/* -------------------------------------------------------------- pills -- */

.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

/* Cut and waiting on you, at the lowest emphasis. */
.pill.made {
  background: var(--brand-100);
  color: var(--brand-800);
}
/* Out of the building, at the finisher — the one state where you do not have
   the item. This is sky's single job here. */
.pill.at_poly {
  background: var(--sky-100);
  color: var(--sky-700);
}
/* Back, and now waiting on you to load it. */
.pill.back {
  background: var(--brand-200);
  color: var(--brand-900);
}
.pill.loaded {
  background: var(--moss-200);
  color: var(--moss-800);
}
.pill.delivered {
  background: var(--moss-600);
  color: var(--moss-50);
}
.pill.damaged {
  background: var(--clay-600);
  color: #fff;
}

@media (prefers-color-scheme: dark) {
  .pill.made {
    background: var(--brand-900);
    color: var(--brand-200);
  }
  .pill.at_poly {
    background: #08324b;
    color: #9dd7f7;
  }
  .pill.back {
    background: var(--brand-800);
    color: var(--brand-200);
  }
  .pill.loaded {
    background: var(--moss-800);
    color: var(--moss-200);
  }
}

/* ------------------------------------------------------------- result -- */

.result {
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  margin: 16px 0;
  background: var(--surface);
  box-shadow: var(--shadow);
}
/* Tint the surface rather than standing a coloured bar beside it: a 4px edge
   is decoration standing in for a signal. */
.result.good {
  background: var(--moss-100);
  color: var(--moss-900);
}
.result.bad {
  background: var(--clay-100);
  color: var(--clay-700);
}
.result.warn {
  background: var(--brand-100);
  color: var(--brand-900);
}
.result h2 {
  margin: 0;
  font-size: 22px;
}
/* On a coloured card, secondary text is a percentage of that card's own text
   colour, never grey. */
.result .sub {
  font-size: 14px;
  margin-top: 4px;
  opacity: 0.75;
}

@media (prefers-color-scheme: dark) {
  .result.good {
    background: var(--moss-900);
    color: var(--moss-100);
  }
  .result.bad {
    background: #431a0e;
    color: var(--clay-300);
  }
  .result.warn {
    background: var(--brand-900);
    color: var(--brand-100);
  }
}

/* --------------------------------------------------------------- data -- */

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
th,
td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  /* Figures line up column to column. This is the work a display face would
     otherwise be asked to do on data, and it does it better. */
  font-variant-numeric: tabular-nums;
}
th {
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}
tbody tr:hover {
  background: var(--surface-2);
}
td.wrap-cell,
th.wrap-cell {
  white-space: normal;
  min-width: 160px;
}

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.stat {
  flex: 1 1 120px;
  background: var(--surface-2);
  border-radius: var(--radius);
  padding: 14px;
}
/* A big editorial figure: display face, tight, lining. */
.stat .n {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 26px;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}
.stat .k {
  font-size: 13px;
  color: var(--muted);
  margin-top: 2px;
}

.thumb {
  width: 64px;
  height: 64px;
  object-fit: contain;
  border-radius: 12px;
  /* Paper in both themes. The drawings are dark line art on a transparent
     ground, so on the night surface they disappear entirely. */
  background: var(--paper);
  flex: none;
}

.list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}
.list li:last-child {
  border-bottom: 0;
}

.empty {
  color: var(--muted);
  padding: 14px 0;
}

/* -------------------------------------------------------------- toast -- */

.toast {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  background: var(--ink);
  color: var(--paper);
  padding: 12px 18px;
  border-radius: 999px;
  font-weight: 600;
  z-index: 100;
  box-shadow: var(--shadow-lift);
  max-width: calc(100vw - 32px);
}
.toast.bad {
  background: var(--clay-600);
  color: #fff;
}

pre.report {
  white-space: pre-wrap;
  font-size: 13px;
  background: var(--surface-2);
  padding: 14px;
  border-radius: var(--radius);
  overflow-x: auto;
}

.hidden {
  display: none !important;
}

/* ---------------------------------------------------------- checklist -- */

/* Touch targets are deliberately large. This is used standing up, one-handed,
   often with a glove on, and a mis-tap marks the wrong panel. */

.progress-head {
  margin-top: 14px;
}
.progress-bar {
  height: 8px;
  border-radius: 4px;
  background: var(--line-strong);
  overflow: hidden;
}
.progress-bar > span {
  display: block;
  height: 100%;
  background: var(--moss-500);
  transition: width 160ms ease-out;
}

.cabinet-head {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* A cabinet head is a readout of the cut list, not page chrome, so it takes the
   one family — the display face does not go on data. That matters here rather
   than being pedantry: these names carry the digits that tell one cabinet from
   another ("Floor 1 Door" against "Floor 2 Door"), and Space Grotesk's `1` is a
   heavy flag with no foot, which is a poor thing to squint at across a
   workshop. Card titles and the page heading keep the display face. */
.cabinet-head h2 {
  margin: 0;
  font-size: 17px;
  font-family: var(--font-sans);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  font-weight: 700;
}
.cabinet.is-done {
  opacity: 0.55;
}

.checkrow {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 56px;
}
.checkrow-name {
  font-weight: 600;
}
.checkrow.is-done .checkrow-name {
  text-decoration: line-through;
  color: var(--muted);
}
.checkrow.is-blocked {
  opacity: 0.75;
}
.checkrow button {
  min-width: 64px;
  min-height: 48px;
  padding: 8px 14px;
}

.checkline {
  display: flex;
  align-items: center;
  gap: 8px;
}
.checkline input {
  width: 20px;
  height: 20px;
  min-height: 0;
}

/* A card that needs looking at is tinted, not edged. A coloured border above a
   hairline is decoration standing in for a signal, and these sit inside a page
   of other cards where a ring would read as a mistake. */
.card--attention {
  background: var(--brand-50);
}
.card--clean {
  background: var(--moss-50);
}
@media (prefers-color-scheme: dark) {
  .card--attention {
    background: #2c2413;
  }
  .card--clean {
    background: var(--moss-900);
  }
}
