/* ===========================================================================
   Itera design system
   ---------------------------------------------------------------------------
   Built for a plant floor, not an office desk:
     · high contrast for bright light and scratched screens
     · large tap targets for gloved hands (48px min, 56px for primary actions)
     · status never carried by colour alone — always colour + text (or icon)
     · dark mode for night shift, automatic and manual
     · no framework, no CDN, no build step — one small stylesheet that loads
       instantly on weak plant wifi
   Full rationale in docs/DESIGN.md.
   ======================================================================== */

/* --- Design tokens ------------------------------------------------------ */
:root {
  /* Brand — deep industrial blue: readable on white, calm next to safety colours */
  --brand-900: #062E52;
  --brand-800: #073E6E;
  --brand-700: #094B84;
  --brand-600: #0B5FA5;
  --brand-500: #1277C7;
  --brand-100: #DCEBF8;
  --brand-050: #F0F7FC;

  color-scheme: light;
  /* Neutrals — slightly cool, never pure white (cuts glare) */
  --bg:        #F4F7FA;
  --surface:   #FFFFFF;
  --surface-2: #EDF1F6;
  --text:      #0F1724;
  --text-2:    #46536B;
  --text-3:    #6B7A93;
  --border:    #D3DCE7;
  --border-2:  #B9C6D6;

  /* Status — each pairs with a text label, never colour alone.
     All chosen to clear 4.5:1 on their own tinted background. */
  --ok:          #0E6E43;
  --ok-bg:       #DDF3E7;
  --warn:        #8A4B00;
  --warn-bg:     #FCEDD8;
  --danger:      #A9231C;
  --danger-bg:   #FBE3E1;
  --info:        #0B5FA5;
  --info-bg:     #DCEBF8;
  --capital:     #5B3391;
  --capital-bg:  #EDE4F8;

  /* Typography — 17px base: comfortable at arm's length on a phone */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --fs-xs:   0.8125rem;
  --fs-sm:   0.9375rem;
  --fs-base: 1.0625rem;
  --fs-lg:   1.1875rem;
  --fs-xl:   1.375rem;
  --fs-2xl:  1.75rem;
  --fs-3xl:  2.25rem;

  /* Space & shape */
  --sp-1: 0.25rem; --sp-2: 0.5rem;  --sp-3: 0.75rem; --sp-4: 1rem;
  --sp-5: 1.25rem; --sp-6: 1.5rem;  --sp-8: 2rem;
  --radius:    0.75rem;
  --radius-lg: 1rem;
  --radius-pill: 999px;

  /* Touch targets — gloves */
  --tap-min: 48px;
  --tap-primary: 56px;

  --shadow: 0 1px 2px rgba(15, 23, 36, .06), 0 1px 3px rgba(15, 23, 36, .08);
  --shadow-lg: 0 4px 12px rgba(15, 23, 36, .10);
  --ring: 0 0 0 3px var(--brand-100);
}

/* Night shift / dim control rooms */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Native controls — the time picker, the date picker, scrollbars — are
       drawn by the browser, not by us. Without this they arrive white in the
       middle of a dark screen. */
    color-scheme: dark;
    --bg:        #0D1117;
    --surface:   #161B22;
    --surface-2: #1F2730;
    --text:      #E7EDF5;
    --text-2:    #AEBBCC;
    --text-3:    #8494A8;
    --border:    #2C3542;
    --border-2:  #3C4757;

    --brand-600: #4A9EDA;
    --brand-700: #6BB2E4;
    --brand-100: #14314A;
    --brand-050: #101D28;

    --ok:         #55C98A;  --ok-bg:      #10301F;
    --warn:       #E0A94A;  --warn-bg:    #33260F;
    --danger:     #F27A72;  --danger-bg:  #3A1614;
    --info:       #4A9EDA;  --info-bg:    #14314A;
    --capital:    #B99AE8;  --capital-bg: #241A38;
    --ring: 0 0 0 3px #14314A;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0D1117; --surface: #161B22; --surface-2: #1F2730;
  --text: #E7EDF5; --text-2: #AEBBCC; --text-3: #8494A8;
  --border: #2C3542; --border-2: #3C4757;
  --brand-600: #4A9EDA; --brand-700: #6BB2E4;
  --brand-100: #14314A; --brand-050: #101D28;
  --ok: #55C98A; --ok-bg: #10301F;
  --warn: #E0A94A; --warn-bg: #33260F;
  --danger: #F27A72; --danger-bg: #3A1614;
  --info: #4A9EDA; --info-bg: #14314A;
  --capital: #B99AE8; --capital-bg: #241A38;
  --ring: 0 0 0 3px #14314A;
}

/* --- Reset -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, p, dl, dd, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img { max-width: 100%; display: block; }
a { color: var(--brand-600); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font: inherit; }

/* Visible focus for keyboard and switch users */
:focus-visible {
  outline: 3px solid var(--brand-600);
  outline-offset: 2px;
  border-radius: 4px;
}

/* --- Layout ------------------------------------------------------------- */
.app-header {
  position: sticky; top: 0; z-index: 20;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.app-header__inner {
  max-width: 34rem; margin: 0 auto;
  padding: var(--sp-3) var(--sp-4);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}
.main { max-width: 34rem; margin: 0 auto; padding: var(--sp-5) var(--sp-4); }

.stack > * + * { margin-top: var(--sp-3); }
.stack-lg > * + * { margin-top: var(--sp-5); }
.row { display: flex; align-items: center; gap: var(--sp-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.row-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.spacer { flex: 1; }

/* --- Brand -------------------------------------------------------------- */
.brand { display: flex; align-items: center; gap: var(--sp-2); color: var(--text); }
.brand:hover { text-decoration: none; }
.brand__mark { width: 32px; height: 32px; flex: none; display: block; }
.brand__name {
  font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.01em;
}

/* --- Cards -------------------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
  box-shadow: var(--shadow);
}
.card--flush { padding: 0; overflow: hidden; }
.card--tight { padding: var(--sp-4); }
.card--alert { border-color: var(--danger); border-left-width: 5px; }
.card--warn  { border-color: var(--warn);   border-left-width: 5px; }
.card__title {
  font-size: var(--fs-sm); font-weight: 700; color: var(--text-2);
  letter-spacing: .01em;
}

/* Two fields abreast at a desk, one under the other on a phone. grid-2 goes
   to four across, which is three too many for a datetime box. */
.pair { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
/* Down a column, one field is pushed off the one above it. Across a grid
   row that same push drops the right-hand field below the left-hand one, so
   two labels side by side sit on different lines. Inside a pair the spacing
   is the grid's. */
.pair > .field + .field { margin-top: 0; }
@media (min-width: 768px) {
  .pair { grid-template-columns: 1fr 1fr; }
}

/* A list too long to lie open — production units, and anything else where a
   plant may have forty of them. Closed it says how many are chosen; open it is
   the same checkboxes, scrolling inside their own box. */
.picker > summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3);
  min-height: var(--tap-primary);
  padding: 0 var(--sp-4);
  border: 2px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface); cursor: pointer; list-style: none;
}
.picker > summary::-webkit-details-marker { display: none; }
.picker > summary::after {
  content: ""; flex: none;
  width: .5rem; height: .5rem;
  border-right: 2px solid var(--text-2); border-bottom: 2px solid var(--text-2);
  transform: rotate(45deg) translate(-2px, -2px);
}
.picker[open] > summary::after { transform: rotate(225deg) translate(-2px, -2px); }
.picker__count { color: var(--text-2); font-size: var(--fs-sm); }
.picker__panel {
  max-height: 16rem; overflow-y: auto;
  margin-top: var(--sp-2); padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface);
}

/* The levels of the structure, stepped so the shape is visible at a glance
   instead of being described in a sentence. */
.ladder__step {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) 0; font-size: var(--fs-sm);
}
.ladder__mark {
  width: .5rem; height: .5rem; flex: none;
  background: var(--brand-500); border-radius: 50%;
}

/* A heading over a run of cards, for a page read section by section. */
.section-title {
  font-size: var(--fs-sm); font-weight: 700; color: var(--text-2);
  text-transform: uppercase; letter-spacing: .04em;
}

/* Tappable list rows — the workhorse of the whole app */
.tile {
  display: block; width: 100%; text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-4);
  min-height: var(--tap-min);
  color: inherit;
  box-shadow: var(--shadow);
}
.tile:hover { text-decoration: none; border-color: var(--border-2); }
.tile:active { background: var(--surface-2); }
.tile--flag { border-left: 5px solid var(--danger); }

/* --- Typography helpers ------------------------------------------------- */
.page-title { font-size: var(--fs-xl); font-weight: 700; letter-spacing: -0.01em; }
.title { font-size: var(--fs-lg); font-weight: 700; }
.strong { font-weight: 650; }
.muted { color: var(--text-2); }
.dim   { color: var(--text-3); font-size: var(--fs-sm); }
.tiny  { font-size: var(--fs-xs); color: var(--text-3); }
.mono-code { font-size: var(--fs-xs); font-weight: 700; color: var(--brand-600);
             letter-spacing: .03em; }
.lead { color: var(--text-2); font-size: var(--fs-sm); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A class that sets display beats the browser default for [hidden], so any
   flex or grid element toggled with .hidden stays visible without this. */
[hidden] { display: none !important; }

/* --- Where the money went ---------------------------------------------- */
/* Four components a plant argues about separately, so they are shown
   separately rather than summed into one number that hides all of it. */
.spend-bar { display: flex; height: .9rem; border-radius: var(--radius-pill);
             overflow: hidden; background: var(--surface-2); }
.spend-bar__part { display: block; height: 100%; }
.spend-key { display: inline-block; width: .7rem; height: .7rem;
             border-radius: 2px; margin-right: var(--sp-2); }
.spend--parts   { background: var(--brand-600); }
.spend--service { background: var(--info, #4C8DD9); }
.spend--labour  { background: var(--ok); }
.spend--machine { background: var(--warn); }
.spend--supplies { background: var(--capital, #7A5AF8); }

/* Two rows on the board have to be seen before the reader starts reading:
   a safety job, and a predictive warning nobody has picked up while there is
   still time to prevent the failure. */
.attention-row { padding-left: var(--sp-3); border-left: 3px solid transparent; }
.attention--safety      { border-left-color: var(--danger); background: var(--danger-bg, transparent); }
.attention--preventable { border-left-color: #C2410C; }

/* --- Photographs ------------------------------------------------------- */
/* A photo is evidence: someone took it to show what is wrong, so the whole
   frame has to survive. Cropping to fill a box is what loses the detail the
   picture was taken for. */
.photo {
  display: block; width: 100%; max-height: 18rem;
  object-fit: contain; object-position: center;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border); border-radius: var(--radius);
  cursor: zoom-in;
}
.photo-view {
  border: 0; padding: 0; background: transparent;
  max-width: 100vw; max-height: 100vh; width: 100%; height: 100%;
}
.photo-view::backdrop { background: rgba(0, 0, 0, .88); }
.photo-view img {
  display: block; margin: auto;
  max-width: 96vw; max-height: 92vh;
  width: auto; height: auto; object-fit: contain; cursor: zoom-out;
}
.photo-view form { position: fixed; top: var(--sp-3); right: var(--sp-3); }
/* A meter reading's note runs to 255 characters. It gets its own full-width
   line beneath the figures rather than sharing the row with them, and breaks
   inside a word if someone pastes an unspaced string, so a long note wraps
   instead of squashing the value and date or overflowing the card. */
.note-line { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--text-2);
             overflow-wrap: anywhere; }
.note-cell { max-width: 28rem; overflow-wrap: anywhere; white-space: normal; }
/* A card that holds everything open rather than the first few of it. The box
   is the height of the window, not a limit on the list: the rest is a scroll
   away instead of a page away, and the shadow at the bottom edge says there
   is more below. */
.scroll-list--on {
  max-height: 30rem; overflow-y: auto;
  padding-right: var(--sp-2);
  -webkit-overflow-scrolling: touch;
  mask-image: linear-gradient(to bottom, #000 calc(100% - 2rem), transparent);
}
/* The work-type matrix is a grid of small inputs, so they size to content
   rather than stretching the way a form field does. */
/* Three digits plus the browser's own spinner, or the figure is clipped and
   72 reads as 7. */
.matrix input[type="number"] { width: 5.5rem; text-align: center; }
/* On a phone the name gives up its width to the commitments beside it and
   scrolls inside its own box; at a desk it shows the longest one whole. */
.matrix input[type="text"] {
  width: 9rem; padding-left: var(--sp-3); padding-right: var(--sp-3);
}
/* The arrow sits 1rem from the edge and is 1.25rem wide, so the shared
   2rem of padding lets the longest option run underneath it. */
.matrix select { width: auto; min-width: 9rem; padding-right: 2.5rem; }
.matrix td, .matrix th {
  white-space: nowrap; vertical-align: middle;
  padding-left: var(--sp-2); padding-right: var(--sp-2);
}
/* A heading kept on one line sets the width of the column beneath it, and
   "Approve within (h)" is three times the width of the box it labels. Broken
   over two lines the heading fits the field instead of the field fitting the
   heading, and the table loses a third of its width. */
table.data-table.matrix thead th {
  white-space: normal; text-align: center; line-height: 1.25;
}
/* Sized by its contents, not stretched to the window: a checkbox column
   padded out to 150px puts a hand's width between a row's name and its tick. */
table.data-table.matrix { width: auto; }
table.data-table.matrix thead th.matrix__name { text-align: left; }
/* The code is the row's identity — it never changes and the clocks, the board
   and the reports are wired to it — so it is what stays put while the
   commitments scroll past. Three letters is a cheap column to give up on a
   phone; the name is editable data and travels with the rest. */
.matrix__code {
  position: sticky; left: 0; z-index: 1; text-align: center;
  background: var(--surface); box-shadow: 1px 0 0 var(--border);
}
.matrix thead .matrix__code { background: var(--surface-2); }
/* Tables you read across as well as down: the people list and the permission
   matrix. Both are wider than a phone, so the heading row and the first column
   stay put while the rest scrolls under them — otherwise you scroll two
   columns right and no longer know whose row you are on, or what the tick
   means. The corner cell has to outrank both or it scrolls out from under
   them. */
.frozen-head thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-2);
}
.frozen-col {
  position: sticky; left: 0; z-index: 1;
  background: var(--surface); box-shadow: 1px 0 0 var(--border);
}
.frozen-head thead .frozen-col { z-index: 3; }
/* The scroll container has to be the thing that scrolls and the thing that
   bounds the sticky, so it needs a height to be sticky against. */
.table-wrap--tall { max-height: 80vh; overflow: auto; }

/* data-table aligns to the top for wrapping report text; these two are read
   row by row, so everything in a row sits on the same line. */
table.data-table.people td, table.data-table.people th,
table.data-table.grid-matrix td, table.data-table.grid-matrix th {
  vertical-align: middle;
  padding-left: var(--sp-3); padding-right: var(--sp-3);
}
table.data-table.grid-matrix td.grid-matrix__row-head { vertical-align: top; }
.people td, .people th { white-space: nowrap; }
table.data-table.people thead th,
table.data-table.grid-matrix thead th {
  white-space: normal; line-height: 1.25;
}
.people .pill { margin-right: var(--sp-1); }
/* Every row's action is the same size in the same place, whatever else the
   row happens to contain. */
.people__action { text-align: right; }
/* Outlined rather than filled: the default button ground is the same colour
   as a striped row, so on every other row it vanished into the stripe and the
   column looked like two different controls. */
.people__action .btn {
  min-height: 2.25rem; padding: 0 var(--sp-3);
  background: transparent; border-color: var(--border);
}

/* The permission matrix: names across the top, activities down the side. The
   activity column carries the words, so it is the only wide one. */
.grid-matrix__row-head { min-width: 11rem; max-width: 22rem; }
.grid-matrix__row-head .tiny { display: none; }
.grid-matrix thead th { text-align: center; }
.grid-matrix thead th.grid-matrix__row-head { text-align: left; }
.grid-matrix td.num { text-align: center; }

/* The blank row at the foot, where a plant names a type of its own. */
.matrix td.matrix__code input[type="text"] { width: 4.5rem; text-align: center; }

/* --- Buttons — sized for gloves ---------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap-primary);
  padding: 0 var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: var(--fs-base); font-weight: 650;
  cursor: pointer;
  background: var(--surface-2); color: var(--text);
}
.btn:hover { text-decoration: none; filter: brightness(.97); }
.btn:active { transform: translateY(1px); }
.btn--block { display: flex; width: 100%; }
.btn--primary { background: var(--brand-600); color: #fff; }
.btn--go      { background: var(--ok);       color: #fff; }
.btn--stop    { background: var(--danger);   color: #fff; }
.btn--quiet   { background: var(--surface); border-color: var(--border-2); color: var(--text); }
.btn--sm { min-height: var(--tap-min); padding: 0 var(--sp-4); font-size: var(--fs-sm); }
.btn--link { background: none; border: none; color: var(--brand-600);
             min-height: var(--tap-min); padding: 0 var(--sp-2); }

.back-link {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--tap-min); color: var(--text-2); font-size: var(--fs-sm);
}

/* --- Status pills — colour PLUS text, never colour alone ---------------- */
.pill {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs); font-weight: 700;
  white-space: nowrap;
  background: var(--surface-2); color: var(--text-2);
}
.pill--ok      { background: var(--ok-bg);      color: var(--ok); }
.pill--warn    { background: var(--warn-bg);    color: var(--warn); }
.pill--danger  { background: var(--danger-bg);  color: var(--danger); }
.pill--info    { background: var(--info-bg);    color: var(--info); }
.pill--capital { background: var(--capital-bg); color: var(--capital); }
.dot { width: .5rem; height: .5rem; border-radius: 50%; background: currentColor;
       flex: none; }

/* Banners */
.banner {
  border-radius: var(--radius); padding: var(--sp-4);
  font-size: var(--fs-sm); font-weight: 600;
  border: 1px solid transparent;
}
.banner--ok     { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok); }
.banner--warn   { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn); }
.banner--danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.banner--info   { background: var(--info-bg);   color: var(--info);   border-color: var(--info); }

/* --- Metrics ------------------------------------------------------------ */
.metric { background: var(--surface); border: 1px solid var(--border);
          border-radius: var(--radius); padding: var(--sp-4); box-shadow: var(--shadow); }
.metric__label { font-size: var(--fs-xs); color: var(--text-2); font-weight: 600;
                 text-transform: uppercase; letter-spacing: .04em; }
.metric__value { font-size: var(--fs-2xl); font-weight: 700; line-height: 1.15;
                 margin-top: var(--sp-1); letter-spacing: -0.02em; }
.metric__unit  { font-size: var(--fs-sm); font-weight: 500; color: var(--text-3); }
.metric--alert .metric__value { color: var(--danger); }

/* --- Forms — large, unambiguous ---------------------------------------- */
.field + .field { margin-top: var(--sp-5); }
.label { display: block; font-size: var(--fs-sm); font-weight: 650; margin-bottom: var(--sp-2); }
.hint  { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--sp-2); }
.error { font-size: var(--fs-sm); color: var(--danger); font-weight: 600; margin-top: var(--sp-2); }

input[type="text"], input[type="password"], input[type="email"], input[type="number"],
input[type="date"], input[type="time"], input[type="search"],
input[type="url"], select, textarea {
  width: 100%;
  min-height: var(--tap-primary);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-base);           /* 16px+ stops iOS zooming on focus */
  color: var(--text);
  background: var(--surface);
  border: 2px solid var(--border-2);   /* 2px: visible on a scratched screen */
  border-radius: var(--radius);
  appearance: none;
}
textarea { min-height: 7rem; padding-top: var(--sp-3); resize: vertical; }
select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2346536B'%3E%3Cpath d='M4.5 6.5 8 10l3.5-3.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sp-4) center;
  background-size: 1.25rem; padding-right: var(--sp-8);
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand-600); box-shadow: var(--ring);
}
/* A file field is two controls in one: the box, which is ours, and the button
   inside it, which the browser draws and which no amount of color-scheme will
   restyle. Both are dressed here so an upload screen does not arrive with a
   grey Windows button sitting in the middle of it. */
input[type="file"] {
  display: flex; align-items: center;
  width: 100%;
  min-height: var(--tap-primary);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--text-2);
  background: var(--surface);
  border: 2px solid var(--border-2);
  border-radius: var(--radius);
}
input[type="file"]::file-selector-button {
  margin-right: var(--sp-3);
  min-height: 2.25rem;
  padding: 0 var(--sp-4);
  font: inherit; font-weight: 650;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { filter: brightness(1.15); }
input[type="file"]:focus-within {
  outline: none; border-color: var(--brand-600); box-shadow: var(--ring);
}

/* Checkbox / radio big enough to hit with a glove */
input[type="checkbox"], input[type="radio"] {
  width: 1.5rem; height: 1.5rem; accent-color: var(--brand-600);
  flex: none; margin: 0;
}
.check {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--tap-min); cursor: pointer; font-size: var(--fs-base);
}
.choice {                     /* big tappable radio/checkbox card */
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--tap-primary);
  padding: var(--sp-3) var(--sp-4);
  border: 2px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface); cursor: pointer; font-weight: 600;
}
.choice--ok:has(input:checked)     { border-color: var(--ok);     background: var(--ok-bg);     color: var(--ok); }
.choice--bad:has(input:checked)    { border-color: var(--danger); background: var(--danger-bg); color: var(--danger); }
.choice--plain:has(input:checked)  { border-color: var(--brand-600); background: var(--brand-050); }
.option {                     /* one way out of a decision, and why */
  padding: var(--sp-4); border: 2px solid var(--border-2);
  border-radius: var(--radius); background: var(--surface);
}
.option .hint { margin-top: var(--sp-2); }
.switch { display: flex; align-items: center; justify-content: space-between;
          gap: var(--sp-4); min-height: var(--tap-primary); cursor: pointer; }

/* --- Main navigation -----------------------------------------------------
   Lived fixed to the bottom until the owner, 2026-09-28: on a phone it sat
   over whatever the page's own last row was — a table's bottom line, a
   form's submit button. It now scrolls up with the header instead of
   floating over content, on a phone and a desk alike. */
.tabbar { border-top: 1px solid var(--border); }
.tabbar__inner { max-width: 34rem; margin: 0 auto; display: grid;
                 grid-template-columns: repeat(4, 1fr); }
.tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; min-height: var(--tap-primary); padding: var(--sp-2) 0;
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-2);
}
.tab:hover { text-decoration: none; color: var(--brand-600); }
.tab--active { color: var(--brand-600); }
.tab__icon { width: 22px; height: 22px; display: block; }

/* --- Tables (reports, count sheets) ------------------------------------- */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.data { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
table.data th, table.data td { padding: var(--sp-3); text-align: left; }
table.data thead th {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-2); border-bottom: 2px solid var(--border);
  white-space: nowrap;
}
table.data tbody tr + tr td { border-top: 1px solid var(--border); }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
table.data tfoot td { border-top: 2px solid var(--border-2); font-weight: 700; }

/* A report table that is read across the row rather than skimmed: a part
   number, its minimum, what is on the shelf. Vertical rules between the
   columns and roomier padding, so the eye does not slide into the wrong
   column halfway across. */
table.data-table {
  width: 100%; border-collapse: collapse; font-size: var(--fs-sm);
}
table.data-table th, table.data-table td {
  padding: var(--sp-3) var(--sp-5); text-align: left; vertical-align: top;
  border-right: 1px solid var(--border);
}
table.data-table th:last-child, table.data-table td:last-child {
  border-right: 0;
}
table.data-table thead th {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-2); background: var(--surface-2);
  border-bottom: 2px solid var(--border-2); white-space: nowrap;
}
table.data-table tbody tr + tr td { border-top: 1px solid var(--border); }
table.data-table tbody tr:nth-child(even) td { background: var(--surface-2); }
table.data-table tbody tr:hover td { background: var(--brand-050); }
table.data-table .num {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}
table.data-table tfoot td {
  border-top: 2px solid var(--border-2); font-weight: 700;
}

/* --- Misc --------------------------------------------------------------- */
.divider { height: 1px; background: var(--border); margin: var(--sp-4) 0; }
.empty {
  text-align: center; color: var(--text-2); padding: var(--sp-8) var(--sp-4);
  background: var(--surface); border: 1px dashed var(--border-2);
  border-radius: var(--radius-lg);
}
/* --- Charts -------------------------------------------------------------
   Drawn as SVG by the app. Every colour is a token, so charts follow light
   and dark mode. Series colours are deliberately distinguishable without
   relying on hue alone — each has a legend label beside it.
   ---------------------------------------------------------------------- */
:root {
  --series-0: #0B5FA5; --series-1: #0E8F6E; --series-2: #B4690E;
  --series-3: #6B3FA0; --series-4: #1D7FA8; --series-5: #A32F6B;
  --series-6: #7A5C00; --series-7: #2D6A4F; --series-8: #8C2F2F;
  --series-9: #3B4D9E; --series-10: #5E6B00; --series-11: #7A3E8F;
  --series-12: #00606B; --series-13: #8A4B1F;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --series-0: #4A9EDA; --series-1: #3FC79B; --series-2: #E0A94A;
    --series-3: #B99AE8; --series-4: #5AC0DE; --series-5: #E27FAE;
    --series-6: #D4C06A; --series-7: #7FD6B4; --series-8: #E99090;
    --series-9: #9AA8E8; --series-10: #BFC97A; --series-11: #D0A3DE;
  --series-12: #6FD0DC; --series-13: #E2A878;
    --series-12: #6FD0DC; --series-13: #E2A878;
  }
}
:root[data-theme="dark"] {
  --series-0: #4A9EDA; --series-1: #3FC79B; --series-2: #E0A94A;
  --series-3: #B99AE8; --series-4: #5AC0DE; --series-5: #E27FAE;
  --series-6: #D4C06A; --series-7: #7FD6B4; --series-8: #E99090;
  --series-9: #9AA8E8; --series-10: #BFC97A; --series-11: #D0A3DE;
}

.chart-block { margin-top: var(--sp-5); border-top: 1px solid var(--border);
               padding-top: var(--sp-4); }
.chart-title {
  margin: var(--sp-5) 0 0; font-size: var(--fs-base); font-weight: 700;
  color: var(--text); text-align: center;
}
.chart { margin: var(--sp-4) 0 0; }
.chart svg { width: 100%; height: auto; display: block; overflow: visible;
             max-height: 65vh; }
/* A ring is round: stretched across a wide page it becomes a poster. */
.chart--donut svg { max-width: 24rem; margin: 0 auto; }

.chart .grid   { stroke: var(--border); stroke-width: 1; }
.chart .tick   { fill: var(--text-2); font-size: 12px; font-family: inherit; }
.chart .tick.cat { fill: var(--text-2); font-size: 11.5px; }
.chart .tick.pct { fill: var(--text-3); font-size: 11px; }
.chart .value  { fill: var(--text); font-size: 11.5px; font-weight: 650; }
.chart .value--part { font-size: 10px; font-weight: 600;
  paint-order: stroke; stroke: var(--surface); stroke-width: 2.5px; }
.chart .value--inside { fill: var(--surface); stroke: none; }
.chart .axis-title {
  fill: var(--text-3); font-size: 11px; font-weight: 650;
  text-transform: uppercase; letter-spacing: .05em;
}

.chart .bar    { fill: var(--brand-600); }
.chart .bar.is-good { fill: var(--ok); }
.chart .bar.is-bad  { fill: var(--danger); }

/* The top of a column that is not part of the measure drawn inside it: the
   column's own colour, hatched, so the part above the split still carries the
   verdict the colour is making. The blank underneath it takes the solid bar
   back to the page so the hatching reads as hatching. */
.chart .bar-blank { fill: var(--surface); }
.chart .bar-hatch { stroke: none; }
.chart .hatch-line { stroke: var(--brand-600); stroke-width: 3.5; }
.chart .hatch-line.is-good { stroke: var(--ok); }
.chart .hatch-line.is-bad  { stroke: var(--danger); }

.chart .series-line { fill: none; stroke: var(--brand-600); stroke-width: 2.5;
                      stroke-linejoin: round; stroke-linecap: round; }
.chart .area   { fill: var(--brand-600); opacity: .12; stroke: none; }
.chart .dot    { fill: var(--brand-600); stroke: var(--surface); stroke-width: 2; }
.chart .dot.is-good { fill: var(--ok); }
.chart .dot.is-bad  { fill: var(--danger); }

.chart .cumulative { fill: none; stroke: var(--warn); stroke-width: 2.5;
                     stroke-linejoin: round; }
.chart .cum-dot    { fill: var(--warn); stroke: var(--surface); stroke-width: 1.5; }
.chart .eighty     { stroke: var(--warn); stroke-width: 1.5; stroke-dasharray: 2 4;
                     opacity: .55; }

.chart .target  { stroke: var(--text); stroke-width: 2; stroke-dasharray: 7 4; }
.chart .average { stroke: var(--text-3); stroke-width: 1.75; stroke-dasharray: 3 4; }
.chart .trend   { stroke: var(--brand-600); stroke-width: 2; stroke-dasharray: 9 5;
                  opacity: .85; }
.chart .overlay-tag { font-size: 11px; font-weight: 650;
                      paint-order: stroke; stroke: var(--surface); stroke-width: 4px; }
.chart .bar--total { stroke: var(--text); stroke-width: 1.5; }
.chart .target-tag  { fill: var(--text); }
.chart .average-tag { fill: var(--text-3); }

.chart .slice { stroke: var(--surface); stroke-width: 1.5; }
.chart .slice--0 { fill: var(--series-0); } .chart .slice--1 { fill: var(--series-1); }
.chart .slice--2 { fill: var(--series-2); } .chart .slice--3 { fill: var(--series-3); }
.chart .slice--4 { fill: var(--series-4); } .chart .slice--5 { fill: var(--series-5); }
.chart .slice--6 { fill: var(--series-6); } .chart .slice--7 { fill: var(--series-7); }
.chart .slice--8 { fill: var(--series-8); } .chart .slice--9 { fill: var(--series-9); }
.chart .slice--10 { fill: var(--series-10); } .chart .slice--11 { fill: var(--series-11); }
.chart .slice--12 { fill: var(--series-12); } .chart .slice--13 { fill: var(--series-13); }
.chart .donut-total { fill: var(--text); font-size: 30px; font-weight: 700; }
.chart .donut-cap   { fill: var(--text-3); font-size: 12px; }

/* Overlays and data labels are hidden until their tick box is on. */
.chart [data-overlay], .chart [data-values] { display: none; }
.chart.show-target  [data-overlay="target"],
.chart.show-average [data-overlay="average"],
.chart.show-trend   [data-overlay="trend"] { display: inline; }
.chart.show-values  [data-values] { display: inline; }
/* The legend is a row of flex items, so it cannot be shown with the rule
   above: display:inline collapses the swatch and leaves the word alone. */
.chart .legend [data-key] { display: none; }
.chart.show-average .legend [data-key="average"],
.chart.show-trend   .legend [data-key="trend"] { display: flex; }

.legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
          margin-top: var(--sp-4); font-size: var(--fs-sm); }
.legend li { display: flex; align-items: center; gap: var(--sp-2); }
.legend-val { color: var(--text-3); }
.swatch { width: .85rem; height: .85rem; border-radius: 3px; flex: none; }
.swatch--0 { background: var(--series-0); } .swatch--1 { background: var(--series-1); }
.swatch--2 { background: var(--series-2); } .swatch--3 { background: var(--series-3); }
.swatch--4 { background: var(--series-4); } .swatch--5 { background: var(--series-5); }
.swatch--6 { background: var(--series-6); } .swatch--7 { background: var(--series-7); }
.swatch--8 { background: var(--series-8); } .swatch--9 { background: var(--series-9); }
.swatch--10 { background: var(--series-10); } .swatch--11 { background: var(--series-11); }
.swatch--12 { background: var(--series-12); } .swatch--13 { background: var(--series-13); }

.chart-caption { display: flex; flex-wrap: wrap; align-items: center;
                 gap: var(--sp-3); margin-top: var(--sp-4); font-size: var(--fs-sm); }

.chart-controls { margin-top: var(--sp-4); }
.chart-controls__row { display: grid; grid-template-columns: 1fr 1fr;
                       gap: var(--sp-3); margin-bottom: var(--sp-3); }
.chart-field .label { margin-bottom: var(--sp-1); }
.chart-toggles { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
                 padding-top: var(--sp-2); }
.chart-toggles .check { font-size: var(--fs-sm); min-height: var(--tap-min); }
.chart-apply { margin-top: var(--sp-2); }

@media print {
  .chart-controls { display: none !important; }
  .chart [data-overlay], .chart [data-values] { display: inline !important; }
}
.timeline { font-size: var(--fs-sm); }
.timeline__row { display: flex; justify-content: space-between; gap: var(--sp-3);
                 padding: var(--sp-2) 0; }
.timeline__row + .timeline__row { border-top: 1px solid var(--border); }
.timeline__row--total { border-top: 2px solid var(--border-2); font-weight: 700; }
.kpi-chip { color: var(--brand-600); font-weight: 650; }

.qr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.qr-card { background: var(--surface); border: 1px solid var(--border);
           border-radius: var(--radius); padding: var(--sp-3); text-align: center; }
.qr-card img { width: 118px; height: 118px; margin: 0 auto; }

/* --- Print (QR labels, cycle-count sheets) ------------------------------ */
@media print {
  /* Printing always reads on paper as the light palette, whatever the
     screen is set to — dark mode otherwise leaves every card and pill
     drawn in its on-screen dark colours on top of the page's white,
     since only `body`'s own background was reset here before. */
  :root {
    color-scheme: light !important;
    --bg: #FFFFFF !important; --surface: #FFFFFF !important;
    --surface-2: #F4F7FA !important;
    --text: #0F1724 !important; --text-2: #46536B !important;
    --text-3: #6B7A93 !important;
    --border: #D3DCE7 !important; --border-2: #B9C6D6 !important;
    --brand-600: #0B5FA5 !important; --brand-700: #094B84 !important;
    --brand-100: #DCEBF8 !important; --brand-050: #F0F7FC !important;
    --ok: #0E6E43 !important; --ok-bg: #DDF3E7 !important;
    --warn: #8A4B00 !important; --warn-bg: #FCEDD8 !important;
    --danger: #A9231C !important; --danger-bg: #FBE3E1 !important;
    --info: #0B5FA5 !important; --info-bg: #DCEBF8 !important;
    --capital: #5B3391 !important; --capital-bg: #EDE4F8 !important;
  }
  .app-header, .tabbar, .btn, .no-print { display: none !important; }
  body { background: #fff; }
  .main { max-width: none; padding: 0; }
  .card, .qr-card, .tile { box-shadow: none; border-color: #999; break-inside: avoid; }
  .qr-grid { grid-template-columns: repeat(3, 1fr); }
}

/* --- Wider screens: planners and admins are at a desk ------------------- */
@media (min-width: 768px) {
  :root { --fs-base: 1rem; }
  .matrix input[type="text"] { width: 12.5rem; }
  /* Room for the sentence under each activity. On a phone the label carries
     it alone, so the ticks are not pushed off the side of the screen. */
  .grid-matrix__row-head { min-width: 16rem; }
  .grid-matrix__row-head .tiny { display: block; }
  .app-header__inner, .main, .tabbar__inner { max-width: 60rem; }
  .grid-2 { grid-template-columns: repeat(4, 1fr); }
  .qr-grid { grid-template-columns: repeat(4, 1fr); }
  .tabbar__inner { grid-template-columns: repeat(4, 7rem); }
}

/* Not now, rather than not ever: a button switched off while something is
   ticked. It stays on the screen, because a button that vanishes reads as a
   fault. */
.btn--off { opacity: .45; pointer-events: none; }

/* One list, two shapes. A storeroom at a desk reads a spare across the row —
   where it sits, when it was last counted, when it was last used. A phone
   has room for the number and nothing else, so it keeps the tiles. */
.only-wide { display: none; }
@media (min-width: 1100px) {
  .only-wide { display: block; }
  .only-narrow { display: none; }
}

/* A page of figures is not a page of prose. The reading width that suits text
   squeezes a wide table into a sideways scroll on a monitor with room to
   spare, so a table page is allowed the width it actually has. */
@media (min-width: 1100px) {
  body.wide .main { max-width: min(94vw, 100rem); }
  body.wide .app-header__inner,
  body.wide .tabbar__inner { max-width: min(94vw, 100rem); }
}

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ── Lubrication map ────────────────────────────────────────────────────
   A photo of the machine with numbered pins on it. Positions are stored as
   percentages so a pin lands in the same place on a phone and on a monitor. */
.lube-map { position: relative; margin: 0; line-height: 0; }
.lube-map img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius); border: 1px solid var(--border);
}
.lube-map figcaption { line-height: 1.4; margin-top: var(--sp-2); }
.lube-map--pick img { cursor: crosshair; }

.lube-map__pin {
  --pin-colour: var(--info);
  position: absolute; transform: translate(-50%, -50%);
  min-width: 1.65rem; height: 1.65rem; padding: 0 .25rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: var(--pin-colour); color: #fff;
  border: 2px solid #fff; box-shadow: 0 1px 4px rgb(0 0 0 / .45);
  font-size: var(--fs-xs); font-weight: 700; line-height: 1;
  text-decoration: none;
}
.lube-map__pin:hover, .lube-map__pin:focus-visible {
  outline: 2px solid var(--ring); outline-offset: 2px;
}
/* The same badge used inline in the point list, so the number in the table
   and the number on the photo read as one thing. */
.lube-map__pin--inline {
  position: static; transform: none;
  min-width: 1.4rem; height: 1.4rem; box-shadow: none;
}

/* ── Report sections ────────────────────────────────────────────────────
   Tabs scroll sideways rather than wrapping into three rows on a phone, so
   the chart below them stays where the eye expects it. */
.tabstrip {
  display: flex; gap: var(--sp-2);
  margin: var(--sp-4) 0 var(--sp-3);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  border-bottom: 1px solid var(--border);
}
.tabstrip::-webkit-scrollbar { display: none; }
.tabstrip__tab {
  flex: none; padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-2); text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabstrip__tab:hover { color: var(--text); }
.tabstrip__tab--active { color: var(--brand-600); border-bottom-color: var(--brand-600); }
.tabstrip__tab:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }

.filters {
  display: flex; flex-wrap: wrap; gap: var(--sp-3);
  align-items: flex-end;
}
/* Every filter the same width, so a bar that wraps reads as columns instead
   of as fields scattered across the line at whatever width each could shrink
   to. */
.filters > .field--inline,
.filters__dates > .field--inline { flex: 0 0 13rem; max-width: 100%; }
/* The window, the two days it came out as, and the button that applies them
   are one control, so they take a line of their own above the pickers rather
   than wrapping apart from each other. */
.filters__dates {
  display: flex; flex-wrap: wrap; gap: var(--sp-3);
  align-items: flex-end; flex-basis: 100%;
}
/* The filters and the Save and Schedule buttons on one line while both fit.
   A tab whose only filter is the class left the buttons stranded on a line of
   their own. A page of filters still fills its line and the buttons drop
   below it, which is where they were. */
/* Every report page puts the same thing in the same place, so a reader
   stops hunting: the window and its dates on the first line, the pickers in
   even columns under it, the buttons that act on the whole report on a line
   of their own at the foot. Stacked rather than ranged beside each other,
   because a row that is wide on one tab and narrow on the next moves the
   Save button around the page from report to report. */
.report-controls {
  display: flex; flex-direction: column; align-items: stretch;
  gap: var(--sp-3); margin-top: var(--sp-3);
}
.report-controls > .filters { flex: 0 1 auto; }
.field--inline { margin: 0; min-width: 10rem; flex: 0 1 14rem; }
.field--inline select, .field--inline input { width: 100%; }
/* The period names its dates, so it needs room to say them. */
.field--period { flex: 0 1 26rem; }
.field--inline .label { display: block; margin-bottom: var(--sp-1); }
/* A box that is ticked and not the reader's to change: always unplanned work
   is what the planned figure is measured against. */
.field--inline.is-locked { color: var(--text-3); cursor: default; }
.field--inline.is-locked input { cursor: default; }

/* ── Finding one work order among thousands ─────────────────────────────
   A box you type in, and the narrower questions folded away underneath it.
   Everything lands in the address bar, so a filtered list is a link. */
.wo-find__box { display: flex; align-items: center; gap: var(--sp-2); }
.wo-find__box input[type="search"] { flex: 1 1 auto; min-width: 0; }
.wo-find__more > summary {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--tap-min);
  color: var(--text-2); font-size: var(--fs-sm);
  cursor: pointer; list-style: none;
}
.wo-find__more > summary::-webkit-details-marker { display: none; }
.wo-find__more > summary::after {
  content: ""; flex: none;
  width: .4rem; height: .4rem;
  border-right: 2px solid var(--text-2); border-bottom: 2px solid var(--text-2);
  transform: rotate(45deg) translateY(-2px);
}
.wo-find__more[open] > summary::after {
  transform: rotate(225deg) translate(-2px, -1px);
}
.wo-find__more > summary:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.wo-find__grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-3); }
@media (min-width: 560px) {
  .wo-find__grid { grid-template-columns: 1fr 1fr; }
}
.wo-find__grid label {
  display: block; color: var(--text-2); font-size: var(--fs-sm);
}
.wo-find__grid select,
.wo-find__grid input { width: 100%; margin-top: var(--sp-1); }
/* A range sits on one line, on a phone as well. */
.wo-find__span { grid-column: 1 / -1; }
.wo-find__legend { display: block; color: var(--text-2); font-size: var(--fs-sm); }
.wo-find__dates { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3);
                  margin-top: var(--sp-1); }

/* ── Type, see what matches, pick one ───────────────────────────────────
   Nothing is listed until somebody types: a plant with twenty thousand
   spares cannot be put in a dropdown. */
.picker-box { position: relative; }
.picker-box__hits {
  position: absolute; left: 0; right: 0; z-index: 30;
  margin-top: var(--sp-1);
  max-height: 16rem; overflow-y: auto;
  border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
}
.picker-box__hit {
  display: flex; flex-direction: column; gap: 2px;
  width: 100%; min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  border: 0; border-bottom: 1px solid var(--border);
  background: transparent; color: var(--text);
  font-size: var(--fs-sm); text-align: left; cursor: pointer;
}
.picker-box__hit:last-child { border-bottom: 0; }
.picker-box__hit:hover, .picker-box__hit:focus-visible {
  background: var(--surface-2);
}
.picker-box__hit strong { font-weight: 650; }
.picker-box__hit span { color: var(--text-2); font-size: var(--fs-xs); }
.picker-box__hit--new { color: var(--brand-600); font-weight: 650; }
.picker-box__more {
  margin: 0; padding: var(--sp-2) var(--sp-3);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-2); font-size: var(--fs-xs);
}

/* A note in a write-off. Stacked in one column and read top to bottom: it
   is a record of what was said, not a chat window (the owner, 2026-09-24). */
.say {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--sp-3); background: var(--surface-2);
}

/* The stockroom's transactions. A desk lays them along the top; a phone
   cannot, and five of them wrapping into a title is what the owner found on
   2026-09-24. On a phone they become two columns of equal buttons under the
   heading, which is the only shape that stays readable as more are added. */
.stock-actions {
  display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: flex-end;
}
@media (max-width: 720px) {
  .stock-head { display: block; }
  .stock-actions {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: var(--sp-2); margin-top: var(--sp-3);
  }
  .stock-actions .btn { width: 100%; padding: 0 var(--sp-2); }
}

/* The camera beside the box you type in. One picture, read on the server,
   and the answer lands in the box — the screen does not change. */
.picker-box__row { display: flex; align-items: stretch; gap: var(--sp-2); }
.picker-box__row > input[type="text"] { flex: 1; min-width: 0; }
.picker-box__camera {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap-min); min-height: var(--tap-min);
  border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface-2); color: var(--text-2); cursor: pointer;
}
.picker-box__camera:hover { color: var(--brand-600); border-color: var(--brand-600); }
.picker-box__camera svg { width: 22px; height: 22px; }
.picker-box__saying {
  margin: var(--sp-2) 0 0; color: var(--text-2); font-size: var(--fs-xs);
}
.picker-box__saying:empty { display: none; }
.sr-text {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* A hit that both opens and travels: the row opens the part, the tick beside
   it takes it along. The same rule as a row in the list underneath. */
.picker-box__hit--pair {
  display: flex; flex-direction: row; align-items: center; gap: var(--sp-3);
  padding: 0 var(--sp-3); min-height: var(--tap-min);
}
.picker-box__hit--pair input[type="checkbox"] { flex: none; margin: 0; }
.picker-box__open {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1; min-width: 0; min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  border: 0; background: transparent; color: var(--text);
  font-size: var(--fs-sm); text-align: left; cursor: pointer;
}
.picker-box__open strong { font-weight: 650; }
.picker-box__open span { color: var(--text-2); font-size: var(--fs-xs); }

/* Taking a few spares along: a tick beside every row, in step with the chips
   in the search bar. The tick takes it along; the row still opens it. */
.pick-cell { width: 1%; white-space: nowrap; }
.pick-cell input[type="checkbox"] { margin: 0; }
.pick-row { display: flex; align-items: center; gap: var(--sp-3); }
.pick-row > .tile { flex: 1; min-width: 0; }
.pick-row input[type="checkbox"] { flex: none; margin: 0; }
.picked-count { margin: var(--sp-2) 0 0; color: var(--text-2);
                font-size: var(--fs-xs); }
.picked-count:empty { display: none; }

/* ── Chips: the things already picked ───────────────────────────────────
   A plant has hundreds of machines, so they are typed and added rather than
   scrolled. Each one that is on the list can be taken off again. */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.chips:empty { display: none; }
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--text);
  font-size: var(--fs-sm);
}
.chip__off {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.4rem; height: 1.4rem; padding: 0;
  border: 0; border-radius: 50%;
  background: transparent; color: var(--text-2);
  font-size: 1rem; line-height: 1; cursor: pointer;
}
.chip__off:hover { background: var(--border); color: var(--text); }

/* ── Which work orders am I looking at ─────────────────────────────────
   Four equal segments across the full width, wrapping to two on a phone.
   They used to sit on one row beside the primary button, which took the
   width and left them as four small taps crushed against the right edge. */
.wo-views {
  display: grid; gap: var(--sp-2);
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}
.wo-views__tab {
  display: flex; align-items: center; justify-content: center;
  gap: var(--sp-2); flex-wrap: wrap;
  min-height: var(--tap-min); padding: 0 var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text-2);
  font-size: var(--fs-sm); font-weight: 650; text-align: center;
}
.wo-views__tab:hover { text-decoration: none; border-color: var(--border-2); }
.wo-views__tab.is-on {
  background: var(--brand-600); border-color: var(--brand-600); color: #fff;
}
.wo-views__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.6rem; padding: 0 var(--sp-2);
  border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--text-2);
  font-size: var(--fs-xs); font-weight: 700;
}
.wo-views__tab.is-on .wo-views__count {
  background: rgba(255, 255, 255, .22); color: #fff;
}

/* ── The circled "i" beside a KPI ───────────────────────────────────────
   A disclosure, not a hover pop-up — a definition you cannot reach on a
   tablet is the same as no definition at all. */
.kpi-info { position: relative; flex: none; }
.kpi-info > summary {
  list-style: none; cursor: pointer; display: inline-flex;
  min-width: var(--tap-min); min-height: var(--tap-min);
  align-items: center; justify-content: center;
}
.kpi-info > summary::-webkit-details-marker { display: none; }
.kpi-info__mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.25rem; height: 1.25rem; border-radius: 50%;
  border: 1.5px solid var(--text-3); color: var(--text-3);
  font-size: .7rem; font-weight: 700; font-style: italic; line-height: 1;
}
.kpi-info > summary:hover .kpi-info__mark,
.kpi-info[open] > summary .kpi-info__mark {
  border-color: var(--brand-600); color: var(--brand-600);
}
.kpi-info > summary:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.kpi-info__panel {
  position: absolute; z-index: 30; top: 100%; left: 0;
  /* Sized in rem with the viewport only trimming it, and a floor underneath:
     an absolutely positioned box in a narrow parent must not collapse to the
     width of the little "i" it hangs from. */
  width: 30rem; min-width: 16rem; max-width: calc(100vw - 2.5rem);
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: var(--sp-4);
  font-size: var(--fs-sm); line-height: 1.5;
}
.kpi-info__panel p { margin: 0 0 var(--sp-3); }
.kpi-info__panel p:last-child { margin-bottom: 0; }
.kpi-info__panel .label { display: block; margin-bottom: 2px; }
.kpi-info__q { font-weight: 600; }

/* ── The count sheet ────────────────────────────────────────────────────
   A list somebody walks the store with: one line per shelf, the figure the
   system believes, and a box to write what is actually there. Wider than a
   phone, so it scrolls inside its own box rather than tipping the page. */
.sheet-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.sheet {
  width: 100%; min-width: 34rem;
  border-collapse: collapse; font-size: var(--fs-sm);
}
.sheet th, .sheet td {
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  text-align: left; vertical-align: middle;
}
.sheet thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-2); color: var(--text-2);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em;
}
.sheet__num { text-align: right; white-space: nowrap; }
.sheet td .dim { display: block; }
.sheet input[type="number"] { width: 6rem; text-align: right; }
.sheet input[type="text"] { width: 100%; min-width: 8rem; }
.sheet select { min-width: 9rem; }
/* A line already counted stays readable but stops asking for attention. */
.sheet__done td { color: var(--text-3); background: var(--surface-2); }
.sheet__done .mono-code { color: var(--text-2); }


/* ── A box that holds one short number ──────────────────────────────────
   Beside its label, not under a field the width of the screen. A page of
   one-digit boxes each taking two lines is a page nobody can see the shape
   of. It folds back to two lines on a phone, where there is no width to
   share. */
@media (min-width: 560px) {
  .field--short {
    display: grid; grid-template-columns: 1fr 11rem;
    align-items: center; gap: var(--sp-3) var(--sp-4);
  }
  .field--short .label { margin-bottom: 0; }
  .field--short input, .field--short select { width: 100%; }
  .field--short .error, .field--short .hint { grid-column: 1 / -1; margin-top: 0; }
  .field--short input[type="checkbox"] { width: auto; justify-self: end; }
}


/* --- Asset tree: opens to the right ---------------------------------------- */
.tree { overflow-x: auto; padding: var(--sp-3); }
.tree ul { list-style: none; margin: 0; padding: 0; }
.tree__children { margin-left: 13px; padding-left: var(--sp-3);
                  border-left: 1px solid var(--border); }
.tree__children[hidden] { display: none; }
.tree__row { display: flex; align-items: center; gap: var(--sp-1);
             min-width: max-content; padding: 1px 0; }
.tree__toggle { width: 28px; height: 28px; flex: none; border: 0;
                background: none; color: var(--text-2); cursor: pointer;
                border-radius: 6px; line-height: 1; }
.tree__toggle::before { content: "\25B8"; display: inline-block;
                        transition: transform 0.1s; }
.tree__toggle[aria-expanded="true"]::before { transform: rotate(90deg); }
.tree__toggle:hover { background: var(--surface-2); }
.tree__toggle--none { cursor: default; }
.tree__toggle--none::before { content: "\00B7"; opacity: 0.4; }
.tree__label { display: flex; gap: var(--sp-2); align-items: baseline;
               color: var(--text); text-decoration: none; padding: 4px 6px;
               border-radius: 6px; }
.tree__label:hover { background: var(--surface-2); }
.tree__meta { color: var(--text-3); font-size: var(--fs-xs); }
.tree__node--focus > .tree__row .tree__label { background: var(--info-bg);
                                               outline: 1px solid var(--info); }

/* --- Planning & Scheduling ------------------------------------------------ */
.plan-chip { display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
             padding: 2px 6px; margin: 2px 2px 0 0; border-radius: 6px;
             font-size: var(--fs-xs); line-height: 1.3; border: 1px solid var(--border);
             background: var(--surface-2); color: var(--text); }
.plan-chip a { color: inherit; text-decoration: none; overflow: hidden;
               text-overflow: ellipsis; white-space: nowrap; }
.plan-chip--wide { display: flex; flex-wrap: wrap; width: 100%; padding: 6px 8px; margin: 4px 0 0; }
.plan-chip[draggable="true"] { cursor: grab; }
.plan-chip--planned { background: var(--info-bg); border-color: var(--info); }
.plan-chip--released { background: var(--warn-bg); border-color: var(--warn); }
.plan-chip--done { background: var(--ok-bg); border-color: var(--ok); }
.plan-chip--overdue { background: var(--danger-bg); border-color: var(--danger); }
.except-row { display: grid; gap: var(--sp-2); padding: var(--sp-2) 0;
              border-bottom: 1px solid var(--border); }
.except-row:last-child { border-bottom: 0; }
@media (min-width: 640px) {
  .except-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
}
.except-row__who .picker-box { margin: 0; }
/* Planning & Scheduling: a control is as wide as what it holds, not as the
   screen. */
.fit-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.fit-row > input, .fit-row > select { width: auto; flex: 0 1 15rem; min-width: 10rem; }
.fit-row > .btn { width: auto; flex: none; }
.plan-add { max-width: 44rem; }
.notice-card { width: fit-content; max-width: 100%; }
.notice-table { width: auto; }
table.data-table.notice-table td { vertical-align: middle; padding-top: var(--sp-2); padding-bottom: var(--sp-2); }
.notice-table input, .notice-table select {
  min-height: 2.5rem; padding: var(--sp-1) var(--sp-3); font-size: var(--fs-sm); }
.notice-table input[type="number"] { width: 5rem; }
.notice-table select { width: auto; min-width: 12rem; padding-right: 2.25rem; }
.plan-confirm { margin: 0; padding: var(--sp-2) var(--sp-3); border-radius: 6px;
                border: 1px solid var(--warn); background: var(--warn-bg);
                color: var(--warn); font-size: var(--fs-sm); }
.plan-chip--paused { background: var(--surface-2); border: 1px dashed var(--border-2); color: var(--text-3); }
.plan-chip--selected { outline: 2px solid var(--brand-600); outline-offset: 1px; }
.plan-chip__remove { border: 0; background: none; color: inherit; cursor: pointer;
                     padding: 0 2px; font-size: 1rem; line-height: 1; }
.plan-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.plan-grid--head > div { text-align: center; padding: var(--sp-2) 0; color: var(--text-3);
                         font-size: var(--fs-xs); font-weight: 600; }
.plan-day { min-height: 5.5rem; padding: 4px; border: 1px solid var(--border); }
.plan-day--out { opacity: 0.45; }
.plan-day--today { outline: 2px solid var(--brand-600); outline-offset: -2px; }
.plan-day--over { background: var(--info-bg); }
.plan-day__n { display: block; font-size: var(--fs-xs); color: var(--text-3); }
.plan-year { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
             gap: var(--sp-3); }
.plan-mini { width: 100%; border-collapse: collapse; text-align: center;
             font-size: var(--fs-xs); margin-top: var(--sp-2); }
.plan-mini th { color: var(--text-3); font-weight: 600; }
.plan-mini td { padding: 3px 0; }
.plan-mini a { color: var(--text); text-decoration: none; display: block; }
.plan-mini--out { visibility: hidden; }
.plan-mini--has a { background: var(--info-bg); border-radius: 4px; font-weight: 700; }
.plan-mini--today a { outline: 1px solid var(--brand-600); border-radius: 4px; }
.plan-dialog { border: 1px solid var(--border); border-radius: 10px; padding: var(--sp-4);
               background: var(--surface); color: var(--text); max-width: 22rem; }
@media (max-width: 40rem) {
  .plan-day { min-height: 4rem; }
  .plan-chip { font-size: 0.7rem; }
}

/* Two short numbers abreast on any screen, narrow, aligned at the top. */
.pair--short { grid-template-columns: repeat(2, minmax(0, 11rem)); gap: var(--sp-3);
               align-items: start; }
@media (min-width: 768px) { .pair--short { grid-template-columns: repeat(2, minmax(0, 11rem)); } }
.picker-warn { color: var(--danger); font-size: var(--fs-xs); margin-top: 4px; }

/* A long table keeps its header and its first column while it scrolls. */
.table-sticky { max-height: 70vh; overflow: auto; }
.table-sticky thead th { position: sticky; top: 0; z-index: 2; background: var(--surface-2); }
.table-sticky tbody td:first-child, .table-sticky thead th:first-child {
  position: sticky; left: 0; background: var(--surface); z-index: 1; }
.table-sticky thead th:first-child { z-index: 3; background: var(--surface-2); }
/* A shutdown or holiday, where it falls. */
.plan-day--window { background: var(--warn-bg); }
.plan-window { display: block; font-size: var(--fs-xs); color: var(--warn); font-weight: 600; }
.plan-mini--window a { background: var(--warn-bg); border-radius: 4px; }

/* A long list on a wide screen: every row one line, the description allowed to
   wrap so a long name does not push the rest off the page. On a phone the
   table scrolls sideways instead. */
.list-fit { font-size: var(--fs-sm); }
.list-fit th, .list-fit td { padding: var(--sp-2) var(--sp-3); }
.list-fit td { white-space: nowrap; }
.list-fit th { vertical-align: bottom; line-height: 1.2; }
.list-fit td.wrap { white-space: normal; min-width: 12rem; }
.list-fit td.wrap-text { white-space: normal; min-width: 8rem; max-width: 15rem; }
.list-fit th .pick { display: block; margin: 0 auto 2px; }
@media print {
  .no-print, .app-header, .tabbar, .back-link { display: none !important; }
  .print-hide { display: none !important; }
  .list-fit { font-size: 10px; }
}
.bar-link { cursor: pointer; }
.bar-link:hover .bar { opacity: .8; }
.bar.is-picked { stroke: var(--text); stroke-width: 2; }
table.data-table.list-fit tbody tr.row--capital td { background: var(--warn-bg); }

.legend--chart { margin-top: var(--sp-3); }
.swatch--ok { background: var(--ok); }
.swatch--bad { background: var(--danger); }
.swatch--target { background: none; border-top: 2px dashed var(--text); height: 0; border-radius: 0; margin-top: 1px; }
.swatch--trend { background: none; border-top: 2px dashed var(--brand-600); height: 0; border-radius: 0; margin-top: 1px; }
.swatch--average { background: none; border-top: 2px dashed var(--text-3); height: 0; border-radius: 0; margin-top: 1px; }
.swatch--total { background: none; border: 1.5px solid var(--text); }
.swatch--solid { background: var(--brand-600); }
.swatch--solid.is-good { background: var(--ok); }
.swatch--solid.is-bad  { background: var(--danger); }
.swatch--hatch {
  background: repeating-linear-gradient(135deg, var(--brand-600) 0 2.5px,
                                        var(--surface) 2.5px 5px);
  border: 1px solid var(--brand-600);
}
.swatch--hatch.is-good {
  background: repeating-linear-gradient(135deg, var(--ok) 0 2.5px,
                                        var(--surface) 2.5px 5px);
  border-color: var(--ok);
}
.swatch--hatch.is-bad {
  background: repeating-linear-gradient(135deg, var(--danger) 0 2.5px,
                                        var(--surface) 2.5px 5px);
  border-color: var(--danger);
}

/* A report printed from the page: the chart and the list, with what they are
   made from at the top, and none of the controls. */
.print-only { display: none; }
@media print {
  .print-only { display: block; }
  .print-basis { margin: 0 0 var(--sp-3); font-size: 11px; }
  .tabstrip, .filters, .kpi-info, .btn, .chart-controls, .lead,
  .back-link { display: none !important; }
  .chart svg { max-height: none; }
}

/* Two charts side by side on a wide screen, one above the other on a phone. */
.chart-pair > div { min-width: 0; }
@media (min-width: 1100px) {
  .chart-pair { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
                gap: var(--sp-5); align-items: start; }
  .chart-pair .chart--donut svg { max-width: 100%; }
}

/* Place picker: a dropdown holding the functional location tree. */
.place-picker { position: relative; }
.place-picker__box { display: block; position: relative; }
.place-picker__summary {
  display: block; box-sizing: border-box; width: 100%; list-style: none;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: var(--sp-3) var(--sp-8) var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--radius);
  background-color: var(--surface); color: var(--text); font: inherit;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2346536B'%3E%3Cpath d='M4.5 6.5 8 10l3.5-3.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sp-4) center;
  background-size: 1.25rem;
}
.place-picker__summary::-webkit-details-marker { display: none; }
.place-picker__panel {
  position: absolute; z-index: 20; top: calc(100% + var(--sp-1)); left: 0;
  min-width: 22rem; max-height: 60vh; overflow-y: auto;
  padding: var(--sp-3); background: var(--surface); color: var(--text);
  border: 1px solid var(--border-2); border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
}
.place-picker__box:not([open]) .place-picker__panel { display: none; }
.place-picker__box[open] .place-picker__panel { display: block; }
.place-picker__list { list-style: none; margin: 0; padding: 0; }
.place-picker__list li { padding-top: var(--sp-1); padding-bottom: var(--sp-1); }
.place-picker__list label { display: flex; gap: var(--sp-2); align-items: center; cursor: pointer; white-space: nowrap; }
.place-picker__list input[type="checkbox"] { width: auto; flex: none; display: inline-block; margin: 0; }
/* Save and Schedule act on the report as a whole, so they sit under
   everything that defines it rather than beside one row of it. */
.report-actions { justify-content: flex-start; }
@media print { .place-picker__panel, .report-actions { display: none; } }
