/* ==========================================================================
   Stores Hub UI kit — dense ops direction
   --------------------------------------------------------------------------
   Everything here is namespaced `sh-` / `--sh-`. Verified zero collisions
   against the 1106 classes in style.css, so this file can never fight an
   unmigrated page: both stylesheets load, but a page only picks up the kit
   where its markup actually uses sh- classes.

   Migration is per-module. A page opts in by adding `sh-page` to <body>
   (layout.php: pageClass => 'sh-page'). Until it does, style.css owns it.

   Do not add un-prefixed selectors to this file, and do not reference
   --bento-* tokens from here. The two systems stay separable until the last
   module is migrated, at which point style.css shrinks to nothing and this
   becomes the stylesheet.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Ground & ink — cool neutrals, biased slightly toward the accent so they
     read as chosen rather than inherited grey. */
  --sh-ground:       #F2F4F7;
  --sh-surface:      #FFFFFF;
  --sh-surface-sunk: #F7F8FA;
  --sh-ink:          #191C22;
  --sh-ink-2:        #4E5563;
  --sh-ink-3:        #7A828F;
  --sh-line:         #DFE3E9;
  --sh-line-soft:    #EDEFF3;

  /* Accent: one hue, used only for interactive + selected state.
     Desaturated descendant of the old --bento-primary (#8B5CF6) so the Hub
     still reads as itself, dark enough to sit beside data all day. */
  --sh-accent:       #4A4FCF;
  --sh-accent-ink:   #3A3FB8;
  --sh-accent-wash:  #ECEDFB;

  /* Status. Deliberately NOT the accent hue — these mean something.
     Never use them for decoration. */
  --sh-good:         #12704A;
  --sh-good-wash:    #E4F3EB;
  --sh-warn:         #8A5A00;
  --sh-warn-wash:    #FBF0DC;
  --sh-crit:         #B92B22;
  --sh-crit-wash:    #FBE9E7;

  --sh-r:            8px;
  --sh-r-sm:         5px;
  --sh-r-pill:       999px;

  /* System stack: no webfont fetch, instant first paint on staff phones,
     native on each. Replaces the Inter download. */
  --sh-ui:  ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji",
            "Segoe UI Emoji", "Noto Color Emoji";
  --sh-num: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo,
            Consolas, monospace;

  --sh-shadow:     0 1px 2px rgba(16, 20, 30, .06);
  --sh-shadow-pop: 0 12px 32px -8px rgba(16, 20, 30, .22);

  /* Control height. Bumped on coarse pointers — see §2. */
  --sh-ctl-h:  28px;
  --sh-row-h:  34px;
  --sh-shell-max: 1100px;
}

/* Dark palette is opt-in only (data-theme), NOT prefers-color-scheme.
   Reason: during migration a dark-OS user would otherwise see migrated
   sections dark and unmigrated sections light on the same screen. Flip the
   media query on once the last module is migrated. */
:root[data-theme="dark"] {
  --sh-ground:       #0E1116;
  --sh-surface:      #161A21;
  --sh-surface-sunk: #1B2029;
  --sh-ink:          #E9ECF1;
  --sh-ink-2:        #A2AAB8;
  --sh-ink-3:        #6F7889;
  --sh-line:         #2A313C;
  --sh-line-soft:    #212731;
  --sh-accent:       #8A8DEE;
  --sh-accent-ink:   #A5A8F5;
  --sh-accent-wash:  #23264A;
  --sh-good:         #57C08C;
  --sh-good-wash:    #14301F;
  --sh-warn:         #D9A441;
  --sh-warn-wash:    #33260C;
  --sh-crit:         #EC7A70;
  --sh-crit-wash:    #351815;
  --sh-shadow:     0 1px 2px rgba(0, 0, 0, .4);
  --sh-shadow-pop: 0 12px 32px -8px rgba(0, 0, 0, .6);
}

/* Touch: 34px rows read well but are tight to tap. Anything with an input
   gets real target height on coarse pointers. */
@media (pointer: coarse) {
  :root {
    --sh-ctl-h: 34px;
    --sh-row-h: 40px;
  }
}

/* --------------------------------------------------------------------------
   2. Page base — only applies to opted-in pages
   -------------------------------------------------------------------------- */
body.sh-page {
  margin: 0;
  background: var(--sh-ground);
  color: var(--sh-ink);
  font-family: var(--sh-ui);
  font-size: 13px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

body.sh-page * { box-sizing: border-box; }

.sh-shell {
  max-width: var(--sh-shell-max);
  margin: 0 auto;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Flex/grid children default to min-width:auto, so a wide table would push the
     shell past the viewport instead of scrolling inside .sh-tablewrap. */
  min-width: 0;
  width: 100%;
}

.sh-panel, .sh-panel__body, .sh-tablewrap { min-width: 0; }

.sh-h1 { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -.02em; }
.sh-h2 { margin: 0; font-size: 15px; font-weight: 650; letter-spacing: -.01em; }

.sh-label {
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
}

/* Any figure that lines up in a column. Tabular so 120 and 8 share a track. */
.sh-num {
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.sh-muted { color: var(--sh-ink-2); }
.sh-faint { color: var(--sh-ink-3); }

/* --------------------------------------------------------------------------
   3. Panel — the one container. Replaces every card/box variant.
   -------------------------------------------------------------------------- */
.sh-panel {
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  box-shadow: var(--sh-shadow);
  overflow: hidden;
}

.sh-panel__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--sh-line);
  background: var(--sh-surface-sunk);
}

.sh-panel__title { font-size: 12px; font-weight: 650; letter-spacing: -.005em; }

/* A panel used directly as a content box, with no __body inside it. Padding
   normally lives on .sh-panel__body; without that wrapper the content sits
   flush against the border. */
.sh-panel--pad { padding: 12px; }
.sh-panel--pad > * + * { margin-top: 10px; }
.sh-panel__body  { padding: 10px; }

.sh-panel__foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  padding: 8px 10px;
  border-top: 1px solid var(--sh-line);
  background: var(--sh-surface-sunk);
}

/* --------------------------------------------------------------------------
   4. Buttons — 6 of them, total.
   -------------------------------------------------------------------------- */
.sh-btn {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  height: var(--sh-ctl-h);
  padding: 0 11px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background .12s ease, border-color .12s ease;
}

.sh-btn:hover { background: var(--sh-surface-sunk); border-color: var(--sh-ink-3); }
.sh-btn:focus-visible { outline: 2px solid var(--sh-accent); outline-offset: 1px; }
.sh-btn[disabled], .sh-btn.is-disabled {
  opacity: .6;
  cursor: not-allowed;
  background: var(--sh-surface-sunk);
  border-color: var(--sh-line);
  color: var(--sh-ink-3);
}

.sh-btn--primary {
  background: var(--sh-accent);
  border-color: var(--sh-accent);
  color: #fff;
}
.sh-btn--primary:hover { background: var(--sh-accent-ink); border-color: var(--sh-accent-ink); }
:root[data-theme="dark"] .sh-btn--primary { color: #12141C; }

.sh-btn--danger {
  background: var(--sh-surface);
  border-color: var(--sh-crit);
  color: var(--sh-crit);
}
.sh-btn--danger:hover { background: var(--sh-crit-wash); border-color: var(--sh-crit); }

.sh-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--sh-ink-2);
}
.sh-btn--ghost:hover { background: var(--sh-surface-sunk); border-color: transparent; color: var(--sh-ink); }

/* Pressed / on state for a button used as a toggle (existing JS toggles .active). */
.sh-btn.active {
  background: var(--sh-accent-wash);
  border-color: var(--sh-accent);
  color: var(--sh-accent-ink);
}

.sh-btn--sm    { height: 24px; padding: 0 8px; font-size: 11px; }
.sh-btn--block { width: 100%; }

/* Kiosk action. The clock screen lives on a wall-mounted tablet and is one tap;
   density is for data screens, not for this. Added while migrating time. */
.sh-btn--xl {
  height: 64px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: .04em;
  border-radius: var(--sh-r);
  width: 100%;
}

/* --------------------------------------------------------------------------
   5b. Alerts — page-level messages. Same status vocabulary as chips.
   -------------------------------------------------------------------------- */
.sh-alert {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 11px;
  border-radius: var(--sh-r-sm);
  border: 1px solid var(--sh-line);
  background: var(--sh-surface-sunk);
  border-left: 3px solid var(--sh-ink-3);
}
.sh-alert--good { background: var(--sh-good-wash); border-color: transparent; border-left-color: var(--sh-good); }
.sh-alert--warn { background: var(--sh-warn-wash); border-color: transparent; border-left-color: var(--sh-warn); }
.sh-alert--crit { background: var(--sh-crit-wash); border-color: transparent; border-left-color: var(--sh-crit); }

.sh-alert__title  { font-size: 12.5px; font-weight: 650; }
.sh-alert__detail { font-size: 12px; color: var(--sh-ink-2); }
.sh-alert--good .sh-alert__title { color: var(--sh-good); }
.sh-alert--warn .sh-alert__title { color: var(--sh-warn); }
.sh-alert--crit .sh-alert__title { color: var(--sh-crit); }

/* Large numeric readout — the live clock. */
.sh-readout {
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  font-size: 40px;
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1.1;
  color: var(--sh-ink);
}

/* Plain content list — policy text, help steps. */
.sh-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.sh-list li { font-size: 12.5px; color: var(--sh-ink-2); }

/* --------------------------------------------------------------------------
   5. Chips — status, encoded in form as well as colour.
   -------------------------------------------------------------------------- */
.sh-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  padding: 4px 7px;
  border-radius: var(--sh-r-pill);
  background: var(--sh-surface-sunk);
  color: var(--sh-ink-2);
  border: 1px solid var(--sh-line-soft);
  white-space: nowrap;
}

.sh-chip--good   { background: var(--sh-good-wash);   color: var(--sh-good);       border-color: transparent; }
.sh-chip--warn   { background: var(--sh-warn-wash);   color: var(--sh-warn);       border-color: transparent; }
.sh-chip--crit   { background: var(--sh-crit-wash);   color: var(--sh-crit);       border-color: transparent; }
.sh-chip--accent { background: var(--sh-accent-wash); color: var(--sh-accent-ink); border-color: transparent; }

.sh-chip__dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }

/* --------------------------------------------------------------------------
   6. Forms
   -------------------------------------------------------------------------- */
.sh-field { display: flex; flex-direction: column; gap: 4px; }

.sh-input,
select.sh-input,
textarea.sh-input {
  font: inherit;
  font-size: 13px;
  height: var(--sh-ctl-h);
  padding: 0 8px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
  width: 100%;
}

textarea.sh-input { height: auto; padding: 6px 8px; line-height: 1.5; resize: vertical; }

.sh-input:focus {
  outline: 2px solid var(--sh-accent);
  outline-offset: -1px;
  border-color: var(--sh-accent);
}

.sh-input--num {
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.sh-input[readonly], .sh-input[disabled] { background: var(--sh-surface-sunk); color: var(--sh-ink-3); }

/* Inline numeric entry sized for a table cell. */
.sh-cellinput {
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  width: 58px;
  height: calc(var(--sh-ctl-h) - 4px);
  padding: 0 6px;
  text-align: right;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
}
.sh-cellinput:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; }

/* Time-range edit row: label, from, to, optional action. Recurs in time and
   payroll (the old .edit-row / .edit-label / .edit-input trio). Grid rather
   than flex so rows with and without an action still line up. */
.sh-editrow {
  display: grid;
  /* minmax(0,1fr), NOT 1fr. Plain 1fr is minmax(auto,1fr) and refuses to shrink
     below a native time input's min-content width, so on a phone the inputs
     overflow their track and sit on top of the delete control. */
  grid-template-columns: 42px minmax(0, 1fr) 10px minmax(0, 1fr) 34px;
  align-items: center;
  gap: 6px;
  /* Padding and border live on EVERY row, including the head. If only the head
     is padded its 1fr tracks come out 12px narrower and the time inputs stop
     lining up between rows. Variants may change colour, never geometry. */
  padding: 6px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
}
.sh-editrow__label {
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
}
.sh-editrow__dash { text-align: center; color: var(--sh-ink-3); }
.sh-editrow--head { background: var(--sh-surface-sunk); }

.sh-editrow .sh-input { min-width: 0; }

/* Below ~470px a label, two native time inputs and an action cannot share one
   line: Chrome silently clips the control, so "10:00 AM" renders "10:00 AN".
   Stack the label above the pair and let each input take a real half-width.
   The delete box spans both rows so it stays aligned with the pair. */
@media (max-width: 470px) {
  .sh-editrow {
    grid-template-columns: minmax(0, 1fr) 10px minmax(0, 1fr) 34px;
    grid-template-areas:
      "lab lab  lab  del"
      "tin dash tout del";
    row-gap: 5px;
  }
  .sh-editrow__label { grid-area: lab; }
  .sh-editrow__dash  { grid-area: dash; }
  .sh-editrow > input:nth-of-type(1) { grid-area: tin; }
  .sh-editrow > input:nth-of-type(2) { grid-area: tout; }
  .sh-editrow > :last-child { grid-area: del; align-self: center; justify-self: end; }
}

/* Delete toggle in the row's action slot. A bare checkbox does not say what it
   does, so the control is a trash box that turns red when armed — and the row
   turns red with it, which is what answers "will pressing Update delete this?". */
.sh-del { display: inline-flex; cursor: pointer; position: relative; }
.sh-del input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.sh-del__box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: var(--sh-ctl-h);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  font-size: 13px;
  line-height: 1;
  opacity: .55;
}
.sh-del:hover .sh-del__box { border-color: var(--sh-crit); opacity: 1; }
.sh-del input:checked + .sh-del__box {
  background: var(--sh-crit-wash);
  border-color: var(--sh-crit);
  opacity: 1;
}
.sh-del input:focus-visible + .sh-del__box {
  outline: 2px solid var(--sh-accent);
  outline-offset: 1px;
}

/* Progressive enhancement — no :has() support just means the row stays neutral
   while the trash box still reads as armed. */
.sh-editrow:has(.sh-del input:checked) {
  background: var(--sh-crit-wash);
  border-color: var(--sh-crit);
}

/* --------------------------------------------------------------------------
   7. Tabs
   -------------------------------------------------------------------------- */
.sh-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--sh-line); }

.sh-tab {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--sh-ink-3);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 6px 10px;
  margin-bottom: -1px;
  cursor: pointer;
  text-decoration: none;
}
.sh-tab:hover { color: var(--sh-ink); }
.sh-tab[aria-selected="true"], .sh-tab.is-active {
  color: var(--sh-ink);
  border-bottom-color: var(--sh-accent);
}

/* --------------------------------------------------------------------------
   8. Data table
   -------------------------------------------------------------------------- */
.sh-tablewrap { overflow-x: auto; }

.sh-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }

.sh-table th {
  text-align: left;
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
  background: var(--sh-surface-sunk);
  padding: 6px 8px;
  border-bottom: 1px solid var(--sh-line);
  white-space: nowrap;
}

.sh-table td {
  padding: 0 8px;
  height: var(--sh-row-h);
  border-bottom: 1px solid var(--sh-line-soft);
  vertical-align: middle;
}

.sh-table tbody tr:last-child td { border-bottom: none; }
.sh-table tbody tr:hover { background: var(--sh-surface-sunk); }

.sh-table th.sh-num, .sh-table td.sh-num { text-align: right; }

/* Sticky header for long counts. Needs a scroll container with a max-height. */
.sh-table--sticky th { position: sticky; top: 0; z-index: 1; }

/* Ruled variant — vertical hairlines between columns. On a wide row with a
   short name, the eye has nothing to follow from the label across to its box;
   a rule per column plus the zebra band below gives it a track. */
.sh-table--ruled td + td,
.sh-table--ruled th + th { border-left: 1px solid var(--sh-line-soft); }

/* Item separation by line, not by fill. Two weights carry the hierarchy:
   a full line between item groups (<tbody>), and the lighter row rule already
   on td between variant rows inside one item. */
.sh-table--ruled tbody + tbody { border-top: 1px solid var(--sh-line); }

/* Sticky first column — keeps the identity column (date, employee, item) in
   view while the rest scrolls sideways on a phone. Added while migrating time. */
.sh-table--stickycol th:first-child,
.sh-table--stickycol td:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--sh-surface);
  border-right: 1px solid var(--sh-line);
}
.sh-table--stickycol th:first-child { background: var(--sh-surface-sunk); z-index: 3; }
.sh-table--stickycol tbody tr:hover td:first-child { background: var(--sh-surface-sunk); }

/* Row flag. Goes on the <tr>, and tints the whole row as well as striping the
   first cell — a 3px stripe alone is too quiet to read while scanning, which is
   what the old full-row pink got right. Tint wins over :hover, so a flagged row
   never loses its flag under the cursor. */
.sh-table tbody tr.sh-tr--good,
.sh-table tbody tr.sh-tr--good:hover { background: var(--sh-good-wash); }
.sh-table tbody tr.sh-tr--warn,
.sh-table tbody tr.sh-tr--warn:hover { background: var(--sh-warn-wash); }
.sh-table tbody tr.sh-tr--crit,
.sh-table tbody tr.sh-tr--crit:hover { background: var(--sh-crit-wash); }

.sh-table tbody tr[class*="sh-tr--"] td:first-child { border-left: 3px solid transparent; }
.sh-table tbody tr.sh-tr--good td:first-child { border-left-color: var(--sh-good); }
.sh-table tbody tr.sh-tr--warn td:first-child { border-left-color: var(--sh-warn); }
.sh-table tbody tr.sh-tr--crit td:first-child { border-left-color: var(--sh-crit); }

/* A sticky first column paints its own background, so it must inherit the flag
   tint too or the stripe cell reads as unflagged while the rest of the row is. */
.sh-table--stickycol tbody tr.sh-tr--good td:first-child { background: var(--sh-good-wash); }
.sh-table--stickycol tbody tr.sh-tr--warn td:first-child { background: var(--sh-warn-wash); }
.sh-table--stickycol tbody tr.sh-tr--crit td:first-child { background: var(--sh-crit-wash); }

/* Stripe-only, for tables where a full tint would be too loud. */
.sh-row        { border-left: 3px solid transparent; }
.sh-row--good  { border-left-color: var(--sh-good); }
.sh-row--warn  { border-left-color: var(--sh-warn); }
.sh-row--crit  { border-left-color: var(--sh-crit); }

/* --------------------------------------------------------------------------
   8b. Counting screens — toolbar, group pills, quantity entry, action footer
   --------------------------------------------------------------------------
   These carry no behaviour. Existing JS hook classes (.qty-input, .tab-pill,
   .expanded, .sub-row, .step-row …) stay on the same elements untouched; the
   kit classes sit beside them and own appearance only. That split is what lets
   a 2,000-line page restyle without editing a line of its JavaScript.
   -------------------------------------------------------------------------- */

/* Sticky controls above a long list. */
.sh-toolbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 8px 10px;
  background: var(--sh-surface);
  border-bottom: 1px solid var(--sh-line);
}
.sh-toolbar__row { display: flex; align-items: center; gap: 6px; }

.sh-search {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 13px;
  height: var(--sh-ctl-h);
  padding: 0 9px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-pill);
  background: var(--sh-surface-sunk);
  color: var(--sh-ink);
}
.sh-search:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; background: var(--sh-surface); }

/* Group pills. Multi-state: scheduled (due today) vs selected. */
.sh-pillrow { display: flex; gap: 5px; overflow-x: auto; padding-bottom: 2px; }
.sh-pill {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 11px;
  border-radius: var(--sh-r-pill);
  border: 1px solid var(--sh-line);
  background: var(--sh-surface);
  color: var(--sh-ink-2);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
}
.sh-pill:hover { border-color: var(--sh-ink-3); color: var(--sh-ink); }
/* Due today but not open — a dot, so "scheduled" and "selected" stay distinct. */
.sh-pill--due::before {
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  margin-right: 5px;
  border-radius: 50%;
  background: var(--sh-warn);
  vertical-align: middle;
}
.sh-pill.active {
  background: var(--sh-accent);
  border-color: var(--sh-accent);
  color: #fff;
}
.sh-pill.active::before { background: rgba(255, 255, 255, .75); }

/* Category band inside a long count table. */
/* Not sticky: .sh-toolbar already occupies top:0, and a second sticky layer
   there just collides with it. */
.sh-table .sh-catrow td {
  background: var(--sh-surface-sunk);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
  height: 28px;
  border-bottom: 1px solid var(--sh-line);
}

/* An item added ad-hoc during a count. Sits on <tbody> so the item and all its
   variant rows highlight as one block — the tr-level flags cannot do that. */
.sh-table tbody.sh-added td { background: var(--sh-good-wash); }
.sh-table tbody.sh-added td:first-child { border-left: 3px solid var(--sh-good); }

/* Item identity cell. */
.sh-item__name { font-weight: 600; }
.sh-item__var  { font-size: 11.5px; color: var(--sh-ink-2); display: flex; align-items: center; gap: 6px; }
.sh-item__note { font-size: 11px; color: var(--sh-warn); margin-top: 2px; }
.sh-item__toggle { cursor: pointer; user-select: none; font-size: 11px; }

/* Quantity entry: number over its unit. Two may sit side by side (packs/loose). */
/* Flex, right-aligned — NOT a 2-column grid. A grid reserved the second column
   even on single-size items, and since most items have one size that empty slot
   sat between the name and the box as dead space. Flexing from the right means a
   single box hugs the right edge with no gap, and a two-size item simply grows
   left. Boxes still line up down the right edge either way. */
.sh-qtygrid { display: flex; justify-content: flex-end; gap: 5px; }
.sh-qty { flex: none; width: 54px; }
@media (pointer: coarse) { .sh-qty { width: 58px; } }
.sh-qty { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.sh-qty__unit {
  font-size: 9.5px;
  font-weight: 650;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
  text-align: center;
}
.sh-qty input, input.sh-qty__in {
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  width: 100%;
  min-width: 0;
  height: 36px;
  padding: 0 6px;
  text-align: center;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
  -moz-appearance: textfield;
}
.sh-qty input::-webkit-outer-spin-button,
.sh-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sh-qty input:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; border-color: var(--sh-accent); }
.sh-qty input.input-error { border-color: var(--sh-crit); background: var(--sh-crit-wash); }
.sh-qty input:not(:placeholder-shown) { font-weight: 600; }

/* Storage column hides until toggled — .show-storage is set by existing JS. */
.sh-col-storage { display: none; }
.show-storage .sh-col-storage { display: table-cell; }

/* Fixed action footer. Replaces .fixed-footer / .footer-inner, which is one of
   the few genuinely site-wide classes (10 files), so this earns its place. */
.sh-footerbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  background: var(--sh-surface);
  border-top: 1px solid var(--sh-line);
  padding: 8px 10px calc(8px + env(safe-area-inset-bottom));
  box-shadow: 0 -2px 12px -6px rgba(16, 20, 30, .18);
}
.sh-footerbar__inner {
  max-width: var(--sh-shell-max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
}
.sh-footerbar__info { font-size: 12px; color: var(--sh-ink-2); min-width: 0; flex: 1; }
/* Page padding so the footer never covers the last row. */
.sh-haspin-footer { padding-bottom: 76px; }

/* Progress steps in a submit dialog. */
.sh-step { display: flex; align-items: center; gap: 8px; font-size: 12.5px; padding: 5px 0; color: var(--sh-ink-2); }
.sh-step__dot {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--sh-line); border: 1px solid var(--sh-line);
}
.sh-step.active { color: var(--sh-ink); }
.sh-step.active .sh-step__dot { background: var(--sh-accent); border-color: var(--sh-accent); }
.sh-step.done   .sh-step__dot { background: var(--sh-good); border-color: var(--sh-good); }
.sh-step.error  { color: var(--sh-crit); }
.sh-step.error  .sh-step__dot { background: var(--sh-crit); border-color: var(--sh-crit); }

/* --------------------------------------------------------------------------
   9. Modal
   -------------------------------------------------------------------------- */
/* Hidden by default, so a modal is never visible on first paint. Two ways to
   open it, both supported on purpose: add .is-open, or set an inline
   display:flex from JS (inline beats the class rule, no !important involved).
   Never use .sh-hide on a backdrop — its !important would beat inline display. */
.sh-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 20, 30, .45);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 900;
}
.sh-backdrop.is-open { display: flex; }

.sh-modal {
  width: 100%;
  max-width: 380px;
  max-height: 90vh;
  overflow: auto;
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  box-shadow: var(--sh-shadow-pop);
}

.sh-modal--wide { max-width: 620px; }

/* --------------------------------------------------------------------------
   10. Module tile — dashboard
   -------------------------------------------------------------------------- */
.sh-modgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
  gap: 8px;
}

.sh-mod {
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  padding: 9px 10px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.sh-mod__head { display: flex; align-items: center; gap: 6px; }
.sh-mod__icon { font-size: 13px; line-height: 1; flex: none; }
.sh-mod__name { font-size: 12.5px; font-weight: 650; }
.sh-mod__badge { margin-left: auto; }
.sh-mod__links { display: flex; flex-wrap: wrap; gap: 4px; }

.sh-mod__link {
  font-size: 11px;
  font-weight: 600;
  color: var(--sh-ink-2);
  background: var(--sh-surface-sunk);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  padding: 2px 7px;
  text-decoration: none;
}
.sh-mod__link:hover { color: var(--sh-accent-ink); border-color: var(--sh-accent); }

/* --------------------------------------------------------------------------
   11. Layout utilities — enough to stop new inline styles appearing.
   -------------------------------------------------------------------------- */
.sh-shell--narrow { max-width: 460px; }
.sh-shell--mid    { max-width: 560px; }

.sh-text-sm { font-size: 12.5px; }
.sh-text-xs { font-size: 11px; }
.sh-center  { text-align: center; }

/* Media that fills its slot — camera snapshots, attachments. */
.sh-thumb {
  width: 100%;
  display: block;
  border-radius: var(--sh-r-sm);
  border: 1px solid var(--sh-line);
}

.sh-stack  { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.sh-stack--nowrap { flex-wrap: nowrap; }
.sh-vstack { display: flex; flex-direction: column; gap: 8px; }
.sh-spacer { margin-left: auto; }
.sh-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.sh-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }

@media (max-width: 640px) {
  .sh-grid-2, .sh-grid-3 { grid-template-columns: minmax(0, 1fr); }
}

.sh-hide { display: none !important; }

@media (prefers-reduced-motion: reduce) {
  body.sh-page *, body.sh-page *::before, body.sh-page *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
  }
}

/* ==========================================================================
   12. BRIDGE — the shared chrome from core/layout.php
   --------------------------------------------------------------------------
   The ONLY place in this file allowed to name classes it does not own. Every
   rule is scoped under `body.sh-page`, so an unmigrated page is untouched and
   keeps the style.css chrome exactly as it is.

   This exists because the header, sub-header and .main-container are emitted by
   layout.php and shared by all 47 pages. Restyling them globally would put a new
   header on old bodies everywhere at once; scoping to sh-page lets the chrome
   migrate one page at a time along with its content.

   Delete this whole section when the last module is migrated — at that point the
   markup itself moves to sh- classes and none of this is needed.
   ========================================================================== */

/* .main-container already pads and caps width. Left alone it double-pads against
   .sh-shell (16+12 top, 15+12 sides) and caps the shell at the old 768px. */
/* Opt-in: only pages that wrap their content in .sh-shell want main-container
   neutralised. Applying it to every sh-page broke .page-wide / .page-fullwidth,
   which set their own width and expect main-container's padding to remain. */
body.sh-page--shell .main-container {
  max-width: none;
  padding: 0;
  min-width: 0;
  background: transparent;
}
body.sh-page .main-container { min-width: 0; }

/* --- top header --- */
body.sh-page .header-outer {
  background: var(--sh-surface);
  border-bottom: 1px solid var(--sh-line);
  box-shadow: none;
}
body.sh-page .header-inner {
  max-width: var(--sh-shell-max);
  padding: 0 12px;
  height: 46px;
}
body.sh-page .header-page-title {
  font-family: var(--sh-ui);
  font-size: 14px;
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--sh-ink);
}
body.sh-page .header-user-pill {
  background: var(--sh-surface-sunk);
  border: 1px solid var(--sh-line-soft);
  border-radius: var(--sh-r-pill);
  color: var(--sh-ink-2);
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
}
/* The env pill must never shrink — "T…" instead of "Test" loses the one signal
   that says you are not on production. The user pill may shrink, but below a
   phone width it drops entirely rather than truncating to a single letter. */
body.sh-page .header-env-pill { flex: none; }
body.sh-page .header-user-pill { min-width: 0; }
@media (max-width: 460px) {
  body.sh-page .header-user-pill { display: none; }
}

body.sh-page .header-icon-svg { width: 19px; height: 19px; color: var(--sh-ink-2); }
body.sh-page .header-icon-btn:hover .header-icon-svg { color: var(--sh-accent-ink); }

/* --- sub-header --- */
body.sh-page .sub-header {
  background: var(--sh-surface-sunk);
  border-bottom: 1px solid var(--sh-line);
  padding: 5px 12px;
}
body.sh-page .sub-header-inner { max-width: var(--sh-shell-max); gap: 10px; }

body.sh-page .store-select,
body.sh-page .week-select {
  font-family: var(--sh-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--sh-ink);
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  min-height: var(--sh-ctl-h);
  padding: 0 7px;
}
body.sh-page .store-label { font-size: 12px; font-weight: 650; color: var(--sh-ink); }

body.sh-page .sub-header-nav { gap: 2px; }
body.sh-page .sub-nav-link {
  font-family: var(--sh-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--sh-ink-3);
  padding: 4px 9px;
  border-radius: var(--sh-r-sm);
}
body.sh-page .sub-nav-link:hover { color: var(--sh-ink); background: var(--sh-surface); }
body.sh-page .sub-nav-link.active {
  color: var(--sh-accent-ink);
  background: var(--sh-accent-wash);
}

/* Empty-state cell — a table showing only its header reads as broken. */
.sh-table td.sh-empty { height: auto; padding: 18px 8px; }

/* Shrink-to-fit column. Table auto-layout hands surplus width to every column,
   so a 54px entry box sat in a 260px cell with the slack showing as dead space
   before it. width:1% makes the column take only what it needs and gives the
   rest to the name column. */
.sh-table th.sh-col-fit, .sh-table td.sh-col-fit { width: 1%; white-space: nowrap; }

/* --------------------------------------------------------------------------
   8c. Planning screens — driver cards, matrix stat cells, status block
   --------------------------------------------------------------------------
   Same split as the counting components: JS hook classes (.tab-btn, .store-driver,
   .status-main, .min-warn, .sub-row, .link-stat …) stay on the element and keep
   their behaviour; these own appearance only.
   -------------------------------------------------------------------------- */

/* Per-store driver cards above the matrix. */
.sh-drivers {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 8px;
}
.sh-driver {
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  overflow: hidden;
}
.sh-driver__head {
  padding: 6px 9px;
  background: var(--sh-surface-sunk);
  border-bottom: 1px solid var(--sh-line);
  font-size: 12px;
  font-weight: 650;
}

/* label / value / aux — the three-column definition grid a driver card uses. */
.sh-deflist {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) 34px;
  gap: 5px 8px;
  align-items: center;
  padding: 8px 9px;
  font-size: 11.5px;
}
.sh-deflist__k   { color: var(--sh-ink-2); }
.sh-deflist__v   { min-width: 0; }
.sh-deflist__aux { color: var(--sh-ink-3); font-size: 10.5px; text-align: right; }
.sh-deflist input[type="number"],
.sh-deflist input[type="text"],
.sh-deflist input[type="date"],
.sh-deflist select {
  font: inherit;
  font-size: 12px;
  width: 100%;
  min-width: 0;
  height: var(--sh-ctl-h);
  padding: 0 6px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
}
.sh-deflist input:focus, .sh-deflist select:focus {
  outline: 2px solid var(--sh-accent);
  outline-offset: -1px;
}

/* Matrix cell: a figure with its rate underneath. */
.sh-stat {
  display: block;
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--sh-ink);
}
.sh-stat__sub {
  display: block;
  font-family: var(--sh-num);
  font-size: 9.5px;
  color: var(--sh-ink-3);
}
.sh-stat--link { cursor: pointer; color: var(--sh-accent-ink); text-decoration: underline; }
.sh-stat--link:hover { color: var(--sh-accent); }
/* Staleness of the on-hand figure. */
.sh-stat--stale { color: var(--sh-warn); }
.sh-stat--old   { color: var(--sh-crit); }
/* Existing JS hooks that carry meaning. */
.sh-table .min-warn      { color: var(--sh-crit); font-weight: 700; }
.sh-table .rate-v2-bad   { color: var(--sh-crit); font-weight: 600; }

/* Store column band in a wide matrix. */
.sh-table th.sh-colgroup { border-left: 1px solid var(--sh-line); text-align: center; }
.sh-table td.sh-colgroup { border-left: 1px solid var(--sh-line); }

/* Separator band inside the matrix ("Not counted in last count"). */
.sh-table .sep-row td {
  background: var(--sh-surface-sunk);
  color: var(--sh-ink-3);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-align: center;
  height: 26px;
}

/* Draft / published state block in the action footer. */
.sh-statusblock { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.sh-statusblock .status-main {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
}
.sh-statusblock .status-main.status-published,
.sh-statusblock .status-main.is-published { color: var(--sh-good); }
.sh-statusblock .status-main.is-draft      { color: var(--sh-warn); }
.sh-statusblock .status-main.is-mixed      { color: var(--sh-warn); }
.sh-statusblock .status-sub { font-size: 11.5px; color: var(--sh-ink-2); }

/* Summary figures at the top of the usage-history modal. */
.sh-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.sh-metric {
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  padding: 7px 9px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sh-metric__k { font-size: 10px; font-weight: 650; letter-spacing: .07em; text-transform: uppercase; color: var(--sh-ink-3); }
.sh-metric__v { font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-size: 17px; font-weight: 600; }

/* Narrow fixed control widths, for toolbars where a field must not stretch. */
.sh-w-56 { width: 56px; flex: none; }
.sh-w-88 { width: 88px; flex: none; }

.sh-w-40 { width: 44px; flex: none; text-align: center; }
.sh-clickable { cursor: pointer; }
.sh-link { color: var(--sh-accent-ink); text-decoration: underline; cursor: pointer; }
.sh-link:hover { color: var(--sh-accent); }
.sh-dim { opacity: .75; }

/* Controls in a toolbar row must flex, not collapse. .sh-input is width:100%,
   which as a flex child with min-width:0 shrinks to nothing — the plan date and
   run selects vanished to a bare chevron. Give them a basis and a floor. */
.sh-toolbar__row > .sh-input { flex: 1 1 150px; width: auto; min-width: 110px; }
.sh-toolbar__row > .sh-w-56 { flex: none; width: 56px; min-width: 0; }
.sh-toolbar__row > .sh-w-88 { flex: none; width: 88px; min-width: 0; }
.sh-toolbar__row { flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   12b. BRIDGE — modules/settings/set_nav.php
   --------------------------------------------------------------------------
   renderSetNav() is shared PHP infrastructure like layout.php: 7 pages across
   admin AND inventory emit its .tabs / .tab-btn markup. Editing it would
   restyle unmigrated admin pages, so it is bridged here instead, scoped to
   sh-page. Delete when admin and inventory are both migrated.

   .tab-content display was defined under .page-inv-settings. Dropping that page
   scope removed the show/hide, so every tab panel rendered at once — it is
   restored here.
   -------------------------------------------------------------------------- */
body.sh-page .tabs {
  display: flex;
  gap: 5px;
  overflow-x: auto;
  padding: 8px 12px 0;
}
body.sh-page .tab-btn {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 11px;
  border-radius: var(--sh-r-pill);
  border: 1px solid var(--sh-line);
  background: var(--sh-surface);
  color: var(--sh-ink-2);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
body.sh-page .tab-btn:hover { border-color: var(--sh-ink-3); color: var(--sh-ink); }
body.sh-page .tab-btn.active {
  background: var(--sh-accent);
  border-color: var(--sh-accent);
  color: #fff;
}

body.sh-page .tab-content { display: none; }
body.sh-page .tab-content.active { display: block; }

/* Small layout utilities — these exist so a one-off never needs a style
   attribute. Every one of them replaced a real inline style in the wild. */
.sh-pad        { padding: 20px; }
.sh-end        { justify-content: flex-end; }
.sh-w-full     { width: 100%; }
.sh-w-auto     { width: auto; }
.sh-w-30       { width: 34px; flex: none; text-align: center; }
.sh-w-50       { width: 54px; flex: none; text-align: center; }
.sh-w-60       { width: 64px; flex: none; text-align: center; }
.sh-mt-1       { margin-top: 4px; }
.sh-mb-1       { margin-bottom: 8px; }
.sh-ml-1       { margin-left: 5px; }
.sh-mr-1       { margin-right: 3px; }
.sh-crit-text  { color: var(--sh-crit); }
.sh-disabled   { opacity: .5; cursor: default; }
.sh-vmid       { vertical-align: middle; }
.sh-narrow     { max-width: 520px; }
.sh-inline-flex{ display: inline-flex; align-items: center; gap: 4px; }

/* Config matrix: one column per store, sticky row-label column, columns wide
   enough to hold controls rather than collapsing to their text. */
.sh-table--matrix th:not(:first-child),
.sh-table--matrix td:not(:first-child) { min-width: 150px; }
.sh-table--matrix td { vertical-align: top; padding: 8px; height: auto; }
.sh-table--matrix td:first-child {
  width: 34px;
  padding: 0;
  text-align: center;
  vertical-align: middle;
  background: var(--sh-surface-sunk);
  position: sticky;
  left: 0;
  z-index: 2;
  border-right: 1px solid var(--sh-line);
}
.sh-table--matrix tbody tr { border-bottom: 1px solid var(--sh-line); }
.sh-text-9  { font-size: 9px; font-weight: 700; color: var(--sh-ink-2); }
.sh-text-sm-dim { font-size: .9em; opacity: .8; }
.sh-right   { text-align: right; }
.sh-toggle-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 600; color: var(--sh-ink-2);
  margin-bottom: 6px; cursor: pointer;
}

/* --------------------------------------------------------------------------
   8d. Sortable list rows and variant cards (item catalogue editor)
   -------------------------------------------------------------------------- */
.sh-rowitem {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--sh-line-soft);
  background: var(--sh-surface);
  text-align: left;
}
.sh-rowitem:last-child { border-bottom: none; }
.sh-rowitem.dragging { opacity: .5; background: var(--sh-accent-wash); }
.sh-rowitem__name { flex: 1; min-width: 0; font-weight: 600; }

/* A variant inside the item editor. The accent rail marks it as one unit. */
.sh-vcard {
  border: 1px solid var(--sh-line);
  border-left: 3px solid var(--sh-accent);
  border-radius: var(--sh-r-sm);
  padding: 8px;
  margin-bottom: 8px;
  background: var(--sh-surface);
  position: relative;
}
.sh-vcard.dragging { opacity: .5; border: 2px dashed var(--sh-accent); }

/* Draggable config row (categories, groups). */
.sh-sortrow {
  display: flex;
  gap: 5px;
  align-items: center;
  margin-bottom: 5px;
  padding: 5px;
  background: var(--sh-surface-sunk);
  border: 1px solid var(--sh-line-soft);
  border-radius: var(--sh-r-sm);
}
.sh-sortrow.dragging { opacity: .5; background: var(--sh-accent-wash); }

/* Explanatory note under a form — reference, not instruction. */
.sh-legend {
  font-size: 11px;
  color: var(--sh-ink-2);
  background: var(--sh-surface-sunk);
  border-top: 1px solid var(--sh-line);
  padding: 7px 9px;
  margin-top: 10px;
  border-radius: var(--sh-r-sm);
}

/* Grouping band in a sortable list. */
.sh-groupband {
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--sh-ink-3);
  background: var(--sh-surface-sunk);
  padding: 6px 10px;
  border-bottom: 1px solid var(--sh-line);
}

/* Rows inside the variant editor — a label, controls, and an optional rule above. */
.sh-vrow      { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
.sh-vrow--top { border-top: 1px solid var(--sh-line); padding-top: 8px; }
.sh-vrow--dash{ border-top: 1px dashed var(--sh-line); padding-top: 8px; }
.sh-vrow__k   { font-size: 11px; margin-right: 5px; min-width: 55px; text-align: right; color: var(--sh-ink-2); }
.sh-vrow__note{ font-size: 11px; color: var(--sh-ink-2); font-weight: 600; margin-left: 5px; }
.sh-between   { display: flex; justify-content: space-between; margin-bottom: 5px; }
.sh-flex1     { flex: 1; min-width: 0; }
.sh-bold-sm   { font-weight: 700; font-size: 11px; }
.sh-good-text { color: var(--sh-good); }
.sh-underline-input {
  font-weight: 700; border: none; border-bottom: 1px solid var(--sh-line);
  border-radius: 0; background: transparent;
}
.sh-w-150 { width: 150px; }
/* Group band is a flex row so its open/closed marker sits at the far right. */
.sh-groupband { display: flex; align-items: center; gap: 6px; cursor: pointer; }

/* Recipe / ingredient editor rows inside the item modal. These were defined
   under .page-inv-settings; without them the header and store list collapse
   into vertical stacks. */
.sh-recheader {
  display: flex; gap: 5px; font-size: 10px; color: var(--sh-ink-3);
  margin-bottom: 2px; padding: 0 5px;
}
.sh-recitem { display: flex; align-items: center; gap: 5px; }
.sh-recchk  {
  width: 35px; display: flex; flex-direction: column; justify-content: center;
  font-size: 9px; text-align: center; line-height: 1;
}
.sh-chipgrid { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

/* Variant field widths — narrow, numeric, and flexible variants. */
input.sh-var-txt  { width: 90px; }
input.sh-var-num  { width: 70px; text-align: center; }
input.sh-var-flex { flex: 1; min-width: 60px; }
input.sh-var-txt, input.sh-var-num, input.sh-var-flex {
  padding: 0 6px; height: var(--sh-ctl-h); font: inherit; font-size: 12px;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink);
}
input.sh-var-txt:focus, input.sh-var-num:focus, input.sh-var-flex:focus {
  outline: 2px solid var(--sh-accent); outline-offset: -1px;
}
.sh-lbl-dyn {
  font-size: 11px; margin-right: 5px; font-weight: 700;
  color: var(--sh-ink-2); min-width: 60px; text-align: right;
}

/* Selectable option row — a radio presented as a full-width choice. */
.sh-option {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 12px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  background: var(--sh-surface);
  cursor: pointer;
}
.sh-option:hover { border-color: var(--sh-accent); background: var(--sh-accent-wash); }
.sh-option__hint { margin-left: auto; font-size: 11.5px; color: var(--sh-ink-3); }

/* --------------------------------------------------------------------------
   8e. Workspace shell — toolbar fixed, body scrolls, footer fixed
   --------------------------------------------------------------------------
   For screens operated like an app rather than read like a document: the buy
   matrix. The middle region is the only thing that scrolls, so the toolbar and
   the finalize bar are always reachable with a long list open.
   -------------------------------------------------------------------------- */
.sh-workspace {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--sh-shell-max);
  margin: 0 auto;
  min-height: 0;
  background: var(--sh-surface);
  border-left: 1px solid var(--sh-line);
  border-right: 1px solid var(--sh-line);
  position: relative;
}
.sh-workspace__body { flex: 1 1 auto; overflow: auto; width: 100%; -webkit-overflow-scrolling: touch; }

/* Dropdown menus in a toolbar. .dropdown / .dropdown-content stay as JS hooks. */
.sh-dropdown { position: relative; display: inline-block; }
.sh-dropdown__menu {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 120;
  min-width: 220px;
  max-height: 60vh;
  overflow: auto;
  padding: 6px;
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  box-shadow: var(--sh-shadow-pop);
}
.sh-dropdown__menu.show { display: block; }
.sh-dropdown__section { padding: 5px 4px; border-bottom: 1px solid var(--sh-line-soft); }
.sh-dropdown__section:last-child { border-bottom: none; }
.sh-dropdown__title {
  font-size: 10px; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--sh-ink-3);
  display: block; margin-bottom: 3px;
}
.sh-dropdown__opt {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 4px; font-size: 12.5px; border-radius: var(--sh-r-sm);
  cursor: pointer;
}
.sh-dropdown__opt:hover { background: var(--sh-surface-sunk); }

/* Two-line picker button: what you are buying over what is in it. */
.sh-pickerbtn {
  display: flex; align-items: center; gap: 6px;
  max-width: 100%; height: var(--sh-ctl-h);
  padding: 0 8px;
  font: inherit; font-size: 12px; text-align: left;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink);
  cursor: pointer; overflow: hidden;
}
.sh-pickerbtn:hover { border-color: var(--sh-ink-3); }
.sh-pickerbtn__text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.sh-pickerbtn__text > span:first-child { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sh-pickerbtn__meta { font-size: 10.5px; color: var(--sh-ink-3); white-space: nowrap; }
.sh-pickerbtn__caret { margin-left: auto; color: var(--sh-ink-3); font-size: 9px; }

/* File-attach control styled as a button; .has-file is set by existing JS. */
.sh-filebtn.has-file {
  background: var(--sh-good-wash);
  border-color: var(--sh-good);
  color: var(--sh-good);
}

/* Transient status strip above the action footer. */
.sh-statusbar {
  display: none;
  padding: 5px 10px;
  font-size: 11.5px;
  color: var(--sh-ink-2);
  background: var(--sh-surface-sunk);
  border-top: 1px solid var(--sh-line);
  text-align: center;
}
.sh-statusbar.show { display: block; }
.sh-modal__h  { margin: 0 0 8px; font-size: 14px; font-weight: 650; }
.sh-modal__p  { margin: 0 0 10px; font-size: 12px; color: var(--sh-ink-2); }
.sh-divider-t { border-top: 1px solid var(--sh-line); padding-top: 5px; margin-top: 5px; }
.sh-divider-b { border-bottom: 1px solid var(--sh-line); padding-bottom: 10px; }
.sh-pad-sm    { padding: 5px 10px; }
.sh-dropdown__opt--all { border-bottom: 1px dashed var(--sh-line); font-weight: 650; }

/* Buy-matrix cell internals. Every one of these was a flex container defined
   under .inv-purchase; without a twin they collapse to stacked text. */
.sh-cellitem { display: flex; flex-direction: column; justify-content: space-between; height: 100%; min-height: 42px; width: 100%; }
.sh-cellitem__top,
.sh-cellitem__bot { display: flex; justify-content: space-between; align-items: center; width: 100%; }
.sh-cellitem__bot { margin-top: 2px; }
.sh-cellbuy { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; min-height: 44px; padding-bottom: 2px; width: 100%; }
.sh-cellcap {
  display: flex; width: 44px; font-size: 9px; font-weight: 700;
  margin-bottom: 1px; color: var(--sh-ink-2);
}
.sh-cellcap--split  { justify-content: space-between; }
.sh-cellcap--center { justify-content: center; }

/* Buy-matrix inputs: fixed and narrow. Left to .sh-input's width:100% they
   sprawled and squeezed the item column to four wrapped lines. */
.sh-cellbuy .qty-input {
  width: 56px;
  height: 30px;
  padding: 0 4px;
  text-align: center;
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
  -moz-appearance: textfield;
}
.sh-cellbuy .qty-input::-webkit-outer-spin-button,
.sh-cellbuy .qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sh-cellbuy .qty-input:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; }

/* Buy and per-store columns shrink to their content so the item name gets the
   remaining width. */
.sh-table td.buy-col-cell, .sh-table th.buy-col-header,
.sh-table td.store-col,   .sh-table th.store-col { width: 1%; white-space: nowrap; }
.sh-table td.buy-col-cell, .sh-table td.store-col { padding: 2px 6px; }
/* Cap the identity block so name and price stay legible and close together even when
   the column is wide. Without this, space-between strands the price at the far
   right of a 700px cell. */
.sh-cellitem { max-width: 340px; }
/* Action footers wrap rather than crushing a text field to two characters. */
.sh-footerbar__inner { flex-wrap: wrap; }
.sh-footerbar__inner > .sh-input { flex: 1 1 140px; min-width: 120px; width: auto; }

/* --------------------------------------------------------------------------
   8f. Long entry forms — grouped sections with label/value rows
   --------------------------------------------------------------------------
   The shift-count shape: a stack of titled groups, each a list of rows with the
   label on the left and the figure entered on the right.
   -------------------------------------------------------------------------- */
.sh-group {
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-left: 3px solid var(--sh-accent);
  border-radius: var(--sh-r);
  padding: 10px 12px;
  margin-bottom: 10px;
}
.sh-group--good { border-left-color: var(--sh-good); }
.sh-group--warn { border-left-color: var(--sh-warn); }
.sh-group--crit { border-left-color: var(--sh-crit); }
.sh-group__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 8px;
}
.sh-group__title { margin: 0; font-size: 13px; font-weight: 650; }

/* Label left, entry right. The workhorse row of every counting screen. */
.sh-inputrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
  padding: 5px 0;
  border-bottom: 1px solid var(--sh-line-soft);
}
.sh-inputrow:last-child { border-bottom: none; }
.sh-inputrow > label { font-size: 12.5px; color: var(--sh-ink-2); }
.sh-inputrow input, .sh-inputrow select {
  font: inherit;
  font-family: var(--sh-num);
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  width: 110px;
  height: var(--sh-ctl-h);
  padding: 0 8px;
  text-align: right;
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r-sm);
  background: var(--sh-surface);
  color: var(--sh-ink);
}
.sh-inputrow input:focus, .sh-inputrow select:focus {
  outline: 2px solid var(--sh-accent); outline-offset: -1px;
}
.sh-inputrow input[readonly] {
  background: transparent; border-color: transparent;
  font-weight: 600; color: var(--sh-ink-2);
}
.sh-inputrow--sunk { background: var(--sh-surface-sunk); border-radius: var(--sh-r-sm); padding: 5px 8px; }

/* Right-aligned two-line meta (last synced, etc). */
.sh-meta {
  display: flex; flex-direction: column; align-items: flex-end;
  font-size: 10.5px; line-height: 1.2; color: var(--sh-ink-3); text-align: right;
}

/* Running total at the foot of a group. */
.sh-total {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 10px; padding-top: 8px;
  border-top: 1px solid var(--sh-line);
  font-size: 13px; font-weight: 650;
}
.sh-total__v { font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-size: 15px; }

/* Collapsible group header. */
.sh-group__head--toggle { cursor: pointer; user-select: none; }

/* Over/short readout. */
.sh-os { font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-weight: 700; }
.sh-os.os-good, .sh-os-good { color: var(--sh-good); }
.sh-os.os-bad,  .sh-os-bad  { color: var(--sh-crit); }
.sh-textarea-lg { width: 100%; height: 80px; padding: 8px; font-size: 15px;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink); font-family: inherit; }
.sh-textarea-lg:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; }
.sh-sectionlabel { margin: 14px 0 6px; font-size: 10.5px; font-weight: 650;
  text-transform: uppercase; letter-spacing: .07em; color: var(--sh-ink-3); }
.sh-subnote { font-size: 10.5px; color: var(--sh-ink-3); font-weight: 400; margin-top: 2px; }
.sh-invisible { opacity: 0; }
.sh-hr { margin: 16px 0; border: 0; border-top: 1px solid var(--sh-line); }
.sh-alert--good { background: var(--sh-good-wash); border-color: transparent; border-left-color: var(--sh-good); }
.sh-alert--good .sh-alert__title { color: var(--sh-good); }

/* Width utilities must out-specify `select.sh-input { width:100% }` (0,1,1),
   so they carry the element qualifier too. A bare .sh-w-auto silently lost. */
select.sh-w-auto, input.sh-w-auto, textarea.sh-w-auto { width: auto; }
select.sh-w-full, input.sh-w-full { width: 100%; }
select.sh-w-56, input.sh-w-56 { width: 56px; }
select.sh-w-88, input.sh-w-88 { width: 88px; }
input.sh-w-30 { width: 34px; }
input.sh-w-50 { width: 54px; }
input.sh-w-60 { width: 64px; }

/* Hours entry inside the tip table. */
.sh-table input.calc-qty {
  width: 70px; height: 30px; padding: 0 6px; text-align: right;
  font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-size: 13px;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink);
}
.sh-table input.calc-qty:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; }

/* Two sticky identity columns (audit: date + closing cash). The second column's
   offset must match the first's real width or they overlap on scroll. */
.sh-table--sticky2 th.sticky-1, .sh-table--sticky2 td.sticky-1,
.sh-table--sticky2 th.sticky-2, .sh-table--sticky2 td.sticky-2 {
  position: sticky; z-index: 2; background: var(--sh-surface);
}
.sh-table--sticky2 th.sticky-1, .sh-table--sticky2 td.sticky-1 { left: 0;    min-width: 72px; }
.sh-table--sticky2 th.sticky-2, .sh-table--sticky2 td.sticky-2 { left: 72px; min-width: 68px;
  border-right: 1px solid var(--sh-line); }
.sh-table--sticky2 th.sticky-1, .sh-table--sticky2 th.sticky-2 { z-index: 3; background: var(--sh-surface-sunk); }
.sh-table--sticky2 tbody tr:hover td.sticky-1,
.sh-table--sticky2 tbody tr:hover td.sticky-2 { background: var(--sh-surface-sunk); }
.sh-table--sticky2 tbody tr.selected td { background: var(--sh-accent-wash); }

/* Compact centred checkbox cell in a wide audit grid. */
.sh-table td .audit-check { width: 17px; height: 17px; accent-color: var(--sh-accent); }
.sh-os-alert { color: var(--sh-crit); font-weight: 700; }
.sh-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The hidden attribute must beat any kit component that sets display.
   .sh-alert is display:flex, which was overriding [hidden] on the audit toast
   and leaving an empty pink bar across the top of the page on load. */
body.sh-page [hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   12c. BRIDGE — floating denomination calculator (.calc-panel)
   --------------------------------------------------------------------------
   Shared markup between audit and cm. cm is not migrated, so its own rules stay
   put; these are scoped to sh-page so only the audit page picks them up.
   Delete when cm is migrated and the markup moves to sh- classes.
   -------------------------------------------------------------------------- */
body.sh-page .calc-panel {
  display: none;
  position: fixed;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  z-index: 3001;
  width: min(280px, calc(100vw - 24px));
  padding: 10px;
  font-size: 12.5px;
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  box-shadow: var(--sh-shadow-pop);
  color: var(--sh-ink);
}
body.sh-page .calc-panel.is-open { display: block; }
body.sh-page .calc-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  font-weight: 650; margin-bottom: 6px;
}
body.sh-page .calc-panel table { width: 100%; border-collapse: collapse; }
body.sh-page .calc-panel td { padding: 3px 2px; vertical-align: middle; }
body.sh-page .calc-panel input {
  width: 64px; text-align: right; padding: 0 6px; height: var(--sh-ctl-h);
  font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-size: 13px;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink);
}
body.sh-page .calc-panel-close {
  border: none; background: transparent; color: var(--sh-ink-3);
  font-size: 18px; line-height: 1; padding: 0 5px; cursor: pointer; border-radius: var(--sh-r-sm);
}
body.sh-page .calc-panel-close:hover { color: var(--sh-ink); background: var(--sh-surface-sunk); }
body.sh-page .line-total { font-size: 11px; color: var(--sh-ink-3); text-align: right; }
body.sh-page .calc-toggle { }
body.sh-page .sticky-footer {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; gap: 6px; padding: 8px 10px calc(8px + env(safe-area-inset-bottom));
  background: var(--sh-surface); border-top: 1px solid var(--sh-line);
}

/* --------------------------------------------------------------------------
   8g. Gelato planning — day picker, legend bar, stat boxes, order rows
   -------------------------------------------------------------------------- */
.sh-controlsrow {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 10px; background: var(--sh-surface);
  border-bottom: 1px solid var(--sh-line); flex-wrap: wrap;
}
.sh-controlsrow--center { justify-content: center; }

.sh-picker { position: relative; }
.sh-picker__menu {
  display: none; position: absolute; top: calc(100% + 4px); left: 0; z-index: 120;
  min-width: 190px; padding: 4px;
  background: var(--sh-surface); border: 1px solid var(--sh-line);
  border-radius: var(--sh-r); box-shadow: var(--sh-shadow-pop);
}
.sh-picker__menu.show, .sh-picker__menu.is-open { display: block; }
.sh-picker__opt {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 7px; font-size: 12.5px; border-radius: var(--sh-r-sm);
}
.sh-picker__opt:hover { background: var(--sh-surface-sunk); }

.sh-legendbar {
  display: flex; gap: 14px; justify-content: center; flex-wrap: wrap;
  padding: 8px 10px; font-size: 11.5px; font-weight: 600; color: var(--sh-ink-2);
  background: var(--sh-surface); border-bottom: 1px solid var(--sh-line);
}
.sh-legenditem { display: flex; align-items: center; gap: 5px; }

.sh-statrow { display: flex; gap: 8px; }
.sh-statbox {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; padding: 8px; min-height: 50px;
  background: var(--sh-surface-sunk); border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
}
.sh-statbox__k { font-size: 10px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--sh-ink-3); }
.sh-statbox__v { font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-size: 16px; font-weight: 650; }

.sh-orderrow {
  display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 10px; align-items: center;
  padding: 7px 0; border-bottom: 1px solid var(--sh-line-soft); font-size: 12.5px;
}
.sh-inputgroup { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }

/* Drag affordance in a sortable table. */
.sh-drag {
  cursor: grab; font-size: 15px; font-weight: 700; color: var(--sh-ink-3);
  padding: 4px; display: flex; align-items: center; justify-content: center;
}
.sh-btn--tall  { height: 46px; padding: 0 15px; }
.sh-mono-area  { width: 100%; height: 80px; font-family: var(--sh-num); font-size: 12px;
  padding: 6px; border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink); }
.sh-scroll-300 { max-height: 300px; overflow: auto; }
.sh-underline  { cursor: pointer; text-decoration: underline; }
.sh-bold       { font-weight: 700; }
.sh-sunk       { background: var(--sh-surface-sunk); }
.sh-btn--x  { color: var(--sh-crit); background: none; border: none; cursor: pointer;
  font-weight: 700; font-size: 15px; padding: 2px 6px; }
.sh-listbox { border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  margin-bottom: 10px; max-height: 100px; overflow-y: auto; }
.sh-center-flex { display: flex; align-items: center; justify-content: center; }
.sh-rowline { display: flex; justify-content: space-between; padding: 5px;
  border-bottom: 1px solid var(--sh-line-soft); }
.sh-input--flex { flex: 1; min-width: 0; }
.sh-th-strong { font-weight: 700; border-bottom: 2px solid var(--sh-line); }
.sh-warn-text { color: var(--sh-warn); }

/* Gelato plan grid: one narrow entry per weekday, name column takes the rest. */
.sh-table .qty-input, .sh-table .order-input {
  width: 62px; height: 30px; padding: 0 5px; text-align: center;
  font-family: var(--sh-num); font-variant-numeric: tabular-nums; font-size: 13px;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface); color: var(--sh-ink); -moz-appearance: textfield;
}
.sh-table .qty-input:focus, .sh-table .order-input:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; }
.sh-table .qty-input::-webkit-outer-spin-button, .sh-table .qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sh-table .qty-input[readonly] { background: var(--sh-surface-sunk); color: var(--sh-ink-3); }
.sh-table .qty-input.input-error { border-color: var(--sh-crit); background: var(--sh-crit-wash); }
/* Weekday columns shrink to their entry box; the flavour name takes the slack. */
.sh-table--gelato td:not(:nth-child(2)), .sh-table--gelato th:not(:nth-child(2)) { width: 1%; white-space: nowrap; }
.sh-table--gelato td:nth-child(2) { min-width: 190px; white-space: normal; }

/* Status dots in the gelato legend and grid. Colour is the meaning here, so
   these map onto the kit's status tokens rather than raw hues. */
.sh-legendbar .dot, .sh-table .dot {
  width: 12px; height: 12px; border-radius: 50%; flex: none;
  border: 1px solid var(--sh-line); display: inline-block;
}
.dot.white  { background: var(--sh-surface); }
.dot.orange { background: var(--sh-warn); border-color: var(--sh-warn); }
.dot.green  { background: var(--sh-good); border-color: var(--sh-good); }

/* Opt-out for pages that keep their own layout instead of adopting .sh-shell.
   The bridge zeroes .main-container so sh-shell can own spacing; a page that
   never wraps its content in sh-shell needs that padding back or it goes
   full-bleed. Use `pageClass => 'sh-page sh-page--boxed <own-scope>'`. */


/* --------------------------------------------------------------------------
   13. Login — the front door. Standalone page, no layout.php.
   -------------------------------------------------------------------------- */
body.sh-login {
  margin: 0; min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: var(--sh-ground);
  color: var(--sh-ink);
  font-family: var(--sh-ui); font-size: 13px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
body.sh-login * { box-sizing: border-box; }
.sh-login__wrap { width: 100%; max-width: 340px; display: flex; flex-direction: column; gap: 10px; }
.sh-login__card {
  background: var(--sh-surface);
  border: 1px solid var(--sh-line);
  border-radius: var(--sh-r);
  box-shadow: var(--sh-shadow);
  padding: 24px 20px;
  text-align: center;
}
.sh-login__title { margin: 0; font-size: 20px; font-weight: 650; letter-spacing: -.02em; }
.sh-login__sub   { margin: 4px 0 16px; font-size: 12.5px; color: var(--sh-ink-2); }
.sh-login__input {
  width: 100%; height: 44px; padding: 0 12px; margin-bottom: 10px;
  font: inherit; font-size: 16px; text-align: center;
  font-family: var(--sh-num); font-variant-numeric: tabular-nums; letter-spacing: .25em;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  background: var(--sh-surface-sunk); color: var(--sh-ink);
}
.sh-login__input:focus { outline: 2px solid var(--sh-accent); outline-offset: -1px; background: var(--sh-surface); }
.sh-login__submit {
  width: 100%; height: 44px;
  font: inherit; font-size: 14px; font-weight: 650;
  border: 1px solid var(--sh-accent); border-radius: var(--sh-r-sm);
  background: var(--sh-accent); color: #fff; cursor: pointer;
}
.sh-login__submit:hover:not([disabled]) { background: var(--sh-accent-ink); border-color: var(--sh-accent-ink); }
.sh-login__submit[disabled] { background: var(--sh-surface-sunk); border-color: var(--sh-line); color: var(--sh-ink-3); cursor: not-allowed; }
.sh-login__err { margin: 10px 0 0; font-size: 12px; color: var(--sh-crit); }
.sh-login__err[hidden] { display: none; }
/* Environment strip — the one signal that says you are not on production. */
.sh-login__env {
  padding: 7px 10px; border-radius: var(--sh-r-sm);
  font-size: 12px; font-weight: 650; text-align: center;
}
.sh-login__env--test   { background: var(--sh-warn-wash); color: var(--sh-warn); }
.sh-login__env--backup { background: var(--sh-accent-wash); color: var(--sh-accent-ink); }

/* --------------------------------------------------------------------------
   14. Behaviour restored after pruning
   --------------------------------------------------------------------------
   These class names carry no styling of their own — JavaScript toggles them to
   show, hide or lay something out. Their rules lived inside page scopes that
   were deleted once each module migrated, and pruning them silently disabled
   the behaviour: gelato's weekday columns stopped hiding, purchase's store
   toggles stopped hiding, variant sub-rows stopped collapsing. Nothing errored.

   Anything below is here because JS depends on it. Do not delete a rule from
   this section without first grepping the JS for the class name.
   -------------------------------------------------------------------------- */

/* Column visibility toggles — gelato weekday picker, purchase store filter. */
body.sh-page .hidden-col { display: none !important; }
body.sh-page .store-col.hidden,
body.sh-page .buy-col-header.hidden { display: none; }

/* Variant sub-rows: collapsed until their parent item is expanded. */
body.sh-page tr.sub-row { display: none; }
body.sh-page tr.sub-row.visible { display: table-row; }

/* Rows built in JS that were relying on a pruned flex/grid rule. */
body.sh-page .order-row {
  display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 10px; align-items: center;
  padding: 7px 0; border-bottom: 1px solid var(--sh-line-soft); font-size: 12.5px;
}
body.sh-page .recipe-row,
body.sh-page .group-row {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin-bottom: 5px;
}
body.sh-page .plan-opt {
  display: flex; align-items: center; gap: 6px; padding: 7px 8px;
  border-bottom: 1px solid var(--sh-line-soft); cursor: pointer; font-size: 12.5px;
}
body.sh-page .plan-opt:hover { background: var(--sh-surface-sunk); }
body.sh-page .plan-opt-main  { display: flex; flex-direction: column; min-width: 0; flex: 1; }
body.sh-page .plan-opt-label { font-weight: 650; }
body.sh-page .plan-opt-meta  { font-size: 10.5px; color: var(--sh-ink-3); }
body.sh-page .plan-opt-empty { color: var(--sh-ink-3); }
body.sh-page .plan-opt-del   { margin-left: auto; color: var(--sh-crit); cursor: pointer; }
body.sh-page .search-item strong { display: block; }
body.sh-page .prog-icon {
  width: 14px; height: 14px; border-radius: 50%; flex: none;
  background: var(--sh-line); border: 1px solid var(--sh-line);
}
body.sh-page .prog-icon.active { background: var(--sh-accent); border-color: var(--sh-accent); }
body.sh-page .prog-icon.done   { background: var(--sh-good);   border-color: var(--sh-good); }

/* --- Drag feedback -------------------------------------------------------
   The touch handler moves the real row through the DOM rather than dragging a
   floating clone, so the ONLY thing telling you which row is under your finger
   is this highlight. Without it the drag is invisible. */
body.sh-page .tr-item { border-bottom: 1px solid var(--sh-line-soft); height: 46px; }
body.sh-page .tr-item:hover { background: var(--sh-surface-sunk); }
body.sh-page .tr-item.dragging td {
  background: var(--sh-accent-wash) !important;
  box-shadow: inset 0 0 0 1px var(--sh-accent);
}
body.sh-page .tr-whitebase td { background: var(--sh-good-wash) !important; }
body.sh-page .recipe-row.dragging,
body.sh-page .group-row.dragging { opacity: .5; border: 1px dashed var(--sh-accent); }
body.sh-page .drag-handle { cursor: grab; touch-action: none; color: var(--sh-ink-3); }
body.sh-page .drag-handle:active { cursor: grabbing; }

/* --- Gelato row states, also lost with .page-gelato --- */
body.sh-page .sect-header {
  position: sticky; left: 0; z-index: 15;
  background: var(--sh-surface-sunk);
  font-size: 10.5px; font-weight: 650; letter-spacing: .07em; text-transform: uppercase;
  color: var(--sh-ink-3); padding: 5px 10px; border-top: 1px solid var(--sh-line);
}
body.sh-page .stock-red  { color: var(--sh-crit); font-weight: 700; cursor: pointer; text-decoration: underline; }
body.sh-page .stock-good { color: var(--sh-ink);  cursor: pointer; text-decoration: underline; }
body.sh-page .sugg-green { color: var(--sh-good); font-weight: 700; }
body.sh-page .sugg-link  { cursor: pointer; text-decoration: underline; }
body.sh-page .text-error { color: var(--sh-crit); font-weight: 700; }

/* --- Gelato production view (page-gelato-view) --- */
body.sh-page .today-header { background: var(--sh-accent) !important; color: #fff; font-weight: 650; }
body.sh-page .qty-box {
  width: 36px; height: 36px; line-height: 34px; margin: 0 auto;
  border: 1px solid var(--sh-line); border-radius: var(--sh-r-sm);
  font-family: var(--sh-num); font-weight: 650; font-size: 15px;
  cursor: pointer; user-select: none; text-align: center; transition: background .1s;
}
body.sh-page .qty-box.status-1 { background: var(--sh-warn-wash); border-color: var(--sh-warn); color: var(--sh-warn); }
body.sh-page .qty-box.status-2 { background: var(--sh-good); border-color: var(--sh-good); color: #fff; }
body.sh-page .qty-box.locked   { pointer-events: none; opacity: .7; }


/* --------------------------------------------------------------------------
   15. Restored from the pre-migration stylesheet
   --------------------------------------------------------------------------
   Found by auditing every class the site still references (markup AND JS)
   against what it used to be styled with. Each of these lost a rule that set
   display/position/flex/grid when its page scope was pruned, and had no kit
   twin on the element. Rules are the originals, retokenised onto kit colours
   and re-scoped from the dead page class to body.sh-page.
   -------------------------------------------------------------------------- */
body.sh-page .audit-days-label {
    font-weight: 600;
}
body.sh-page .audit-select,
body.sh-page .audit-date-input {
    border: 1px solid var(--sh-line);
    border-radius: var(--sh-r-sm);
    background: var(--sh-surface);
    color: var(--sh-ink);
    font-size: 0.8125rem;
    font-weight: 600;
    min-height: 36px;
    padding: 4px 8px;
    box-sizing: border-box;
    flex: 0 0 auto;
}
body.sh-page .audit-select {
    cursor: pointer;
    max-width: none;
}
body.sh-page .audit-select--filter {
    max-width: 8.5rem;
}
body.sh-page .audit-select:focus-visible,
body.sh-page .audit-date-input:focus-visible {
    outline: none;
    outline: 2px solid var(--sh-accent); outline-offset: 1px;
}
body.sh-page .btn-flex { flex: 1; text-align: center; padding: 15px; text-decoration: none; border-radius: 6px; font-weight: bold; color: white; font-size: 1rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
body.sh-page .inv-modal-box.compact .btn-row { display: flex; gap: 10px; justify-content: center; }
body.sh-page #runModal .btn-row button { min-height: 36px; padding: 0 14px; border: none; border-radius: 4px; font-weight: bold; font-size: 13px; cursor: pointer; }
body.sh-page #runModal .btn-row button:active { transform: scale(0.98); }
body.sh-page .btn-row { display: flex; gap: 10px; margin-top: 25px; }
body.sh-page .col-date { width: 110px; }
body.sh-page .plan-table th.col-drag,
body.sh-page .plan-table td.td-drag { position: sticky; left: 0; width: 30px; min-width: 30px; z-index: 10; background-color: #fff; border-right: 1px solid var(--sh-line-soft); }
body.sh-page .col-info { width: auto; }
body.sh-page .col-inv,
body.sh-page .col-stock,
body.sh-page .col-sugg { width: 34px; min-width: 34px; max-width: 34px; }
body.sh-page .col-inv { background: #fff; }
body.sh-page /* Safe-count column in Audit Manager: header is a lock icon,
body.sh-page not the word "Changes" — the
   table is nowrap and read on a phone and an iPad mini,
body.sh-page so a long word costs real width. */
.audit-table th.col-safe,
body.sh-page .audit-table td.col-safe { width: 1%; padding-left: 6px; padding-right: 6px; }
body.sh-page .audit-table th.col-safe { font-size: 1rem; line-height: 1; }
body.sh-page .col-stock { background: var(--sh-accent-wash); color: #0d47a1; }
body.sh-page .matrix-wrapper .col-store-group { border-left: 2px solid var(--sh-line); }
body.sh-page .list-table .col-store { width: 27%; text-align: center; }
body.sh-page .col-store { width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.sh-page .col-sugg { background: #f0f0f0; color: var(--sh-ink); font-weight: bold; }
body.sh-page .col-type { width: 90px; }
body.sh-page .config-cell { display: flex; flex-direction: column; gap: 6px; font-size: 12px; }
body.sh-page .debug-list { font-family: monospace; font-size: 12px; background: var(--sh-surface-sunk); padding: 10px; border: 1px solid var(--sh-line-soft); max-height: 200px; overflow-y: auto; }
body.sh-page .del-day-chk { display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 9px; background: #fff; border: 1px solid var(--sh-line); border-radius: 2px; height: 24px; cursor: pointer; user-select: none; }
body.sh-page .del-day-chk.checked { background: var(--sh-ink); color: #fff; border-color: var(--sh-ink); }
body.sh-page .del-day-chk input { display: none; }
body.sh-page .del-days-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-top: 2px; }
body.sh-page .footer-buttons { flex: 1; display: flex; gap: 10px; justify-content: flex-end; }
body.sh-page .footer-info { flex: 1; font-size: 12px; color: var(--sh-ink-2); text-align: left; padding-right: 10px; line-height: 1.3; }
body.sh-page .matrix-wrapper .i-name { font-weight: bold; font-size: 12px; color: var(--sh-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 70px; }
body.sh-page .list-table .i-name { font-weight: 800; font-size: 14px; color: #000; margin-bottom: 4px; padding-left: 2px; }
body.sh-page .plan-quick-label {
  display: inline-flex;
  align-items: center;
  padding: 0 10px;
  height: var(--sh-ctl-h, 32px);
  box-sizing: border-box;
  font-size: 12px;
  font-weight: 600;
  color: var(--sh-ink-2);
  background: var(--sh-surface-sunk, #f0f4f8);
  border: 1px solid var(--sh-line);
  border-radius: 4px;
}
body.sh-page .plan-select-box { width: 100%; padding: 8px; border: 1px solid var(--sh-line); border-radius: 6px; font-weight: bold; font-size: 13px; background: var(--sh-surface-sunk); color: var(--sh-ink); }
body.sh-page .plan-select-box.is-today { border-color: var(--sh-accent, var(--sh-accent)); background: var(--sh-accent-wash, #EDE9FE); color: var(--sh-accent, var(--sh-accent)); }
body.sh-page .plan-select { padding: 0 8px; border-radius: 4px; border: 1px solid var(--sh-line); height: 32px; background: #fff; font-size: 12px; font-weight: bold; color: var(--sh-ink); }
body.sh-page .plan-select.is-today { border-color: var(--sh-accent, var(--sh-accent)); background: var(--sh-accent-wash, #EDE9FE); color: var(--sh-accent, var(--sh-accent)); }
body.sh-page .prog-bar-ctn { width: 100%; background: #444; height: 6px; border-radius: 3px; margin-top: 5px; display: none; }
body.sh-page .prog-bar-fill { height: 100%; background: var(--sh-good); width: 0%; transition: width 0.2s; border-radius: 3px; }
body.sh-page .prog-step { margin-bottom: 10px; font-size: 14px; display: flex; align-items: center; }
body.sh-page .prog-step span { margin-left: 10px; }
body.sh-page /* Long-window rate,
body.sh-page shown beside the legacy period average while both are being compared.
   Deliberately quiet — it is a second opinion,
body.sh-page not the answer,
body.sh-page until Eric picks one. */
.matrix-wrapper .rate-v2 {
    display: block;
    color: var(--sh-ink-2);
    border-top: 1px dotted var(--sh-line);
    margin-top: 2px;
    padding-top: 1px;
}
body.sh-page .matrix-wrapper .rate-v2-bad { color: var(--sh-crit, #b91c1c); font-weight: 600; }
body.sh-page .rec-select { padding: 4px; border: 1px solid var(--sh-line); font-size: 11px; border-radius: 3px; width: 100%; box-sizing: border-box; }
body.sh-page .recipe-panel { display: none; margin-top: 10px; background: #fefefe; border: 1px dashed var(--sh-line); padding: 8px; border-radius: 4px; }
body.sh-page .safe-box-foot { display: flex; justify-content: space-between; align-items: baseline; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--sh-line-soft); }
body.sh-page .safe-box-foot .label { font-weight: bold; color: var(--sh-ink-2); }
body.sh-page .safe-box-note { font-size: 0.85rem; color: var(--sh-ink-2); margin: 0 0 12px; line-height: 1.35; }
body.sh-page .safe-empty-check { display: none; width: 100%; text-align: right; font-size: 0.85rem; color: #ef6c00; margin-top: 4px; }
body.sh-page .safe-empty-check input { width: auto; margin-right: 5px; }
body.sh-page .search-wrap { position: relative; width: 100%; }
body.sh-page .tr-calc .td-drag,
body.sh-page .tr-calc .td-name { background-color: #d6d8db !important; }
body.sh-page .tr-total .td-drag,
body.sh-page .tr-total .td-name { background-color: #545b62 !important; color: #fff; }
body.sh-page .tip-input { width: 60px; padding: 5px; text-align: center; border: 1px solid var(--sh-line); border-radius: 4px; font-size: 1rem; background: var(--sh-surface-sunk); color: var(--sh-ink-2); }
body.sh-page .audit-table .user-cell {
    font-size: 0.75rem;
    color: var(--sh-ink-2);
    max-width: 72px;
    overflow: hidden;
    text-overflow: ellipsis;
}
body.sh-page .v-drag-handle { cursor: grab; font-size: 16px; color: var(--sh-line); font-weight: bold; margin-right: 8px; line-height: 1; user-select: none; }
body.sh-page textarea.v-notes { width: 100%; padding: 6px; border: 1px solid var(--sh-line); border-radius: 4px; font-size: 12px; box-sizing: border-box; resize: vertical; min-height: 32px; font-family: inherit; }
body.sh-page .vertical-text { writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap; font-weight: bold; font-size: 12px; color: var(--sh-ink-2); height: 100px; width: 100%; display: flex; align-items: center; justify-content: center; }
body.sh-page .warning-text { color: var(--sh-crit); font-size: 0.8rem; display: none; margin-top: 2px; text-align: right; width: 100%; }
body.sh-page .date-display-btn { width: 100%; border: 1px solid var(--sh-line); background: var(--sh-surface-sunk); padding: 6px; border-radius: 4px; text-align: center; font-weight: bold; cursor: pointer; color: var(--sh-ink); margin-top: 5px; }
body.sh-page .date-display-btn.override { background: #ffebee; border-color: #ef9a9a; color: var(--sh-crit); }
body.sh-page .date-display-btn.today { background: #e8f5e9; border-color: #a5d6a7; color: #2e7d32; }
body.sh-page .dd-opt { display: flex; align-items: center; gap: 8px; padding: 6px 0; cursor: pointer; font-size: 13px; }
body.sh-page .matrix-wrapper .i-arrow { cursor: pointer; font-size: 14px; line-height: 1; padding: 0 5px; }
body.sh-page .list-table .i-arrow { cursor: pointer; font-size: 14px; padding: 0 5px; }
body.sh-page .matrix-wrapper .i-var { font-size: 11px; color: var(--sh-ink-2); font-style: italic; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 80px; }
body.sh-page .list-table .i-var { font-size: 13px; color: var(--sh-ink); font-weight: 700; display: flex; align-items: center; justify-content: space-between; margin-top: 2px; padding-left: 16px; }
body.sh-page .ri-del { width: 25px; justify-content: center; }
body.sh-page .ri-drag { width: 20px; justify-content: center; }
body.sh-page .ri-name { flex: 1; min-width: 120px; }
body.sh-page .ri-qty { width: 70px; }
body.sh-page .ri-sec { width: 80px; }
body.sh-page .ri-unit { width: 60px; }
body.sh-page .row-line { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; white-space: nowrap; }
body.sh-page .search-res { flex: 1; overflow-y: auto; margin-top: 10px; border: 1px solid var(--sh-line-soft); border-radius: 4px; }
body.sh-page .page-clock .std-box {
    width: 100%;
    max-width: min(100%, 560px);
    box-sizing: border-box;
    margin-left: auto;
    margin-right: auto;
}
body.sh-page select.std,
body.sh-page input.std { font-size: 12px; padding: 2px; height: 26px; border: 1px solid var(--sh-line); border-radius: 3px; background: #fff; }
body.sh-page input.std { width: 50px; text-align: center; }
body.sh-page select.std { width: auto; max-width: 100px; }
body.sh-page .table-scroll-wrapper { flex: 1; overflow-x: auto; -webkit-overflow-scrolling: touch; position: relative; width: 100%; }
body.sh-page .plan-table th.col-name,
body.sh-page .plan-table td.td-name { position: sticky; left: 30px; width: 160px; min-width: 160px; max-width: 160px; z-index: 10; background-color: #fff; border-right: 2px solid var(--sh-line); box-shadow: 2px 0 5px rgba(0,0,0,0.05); }
body.sh-page .td-name { text-align: left; font-weight: 600; padding-left: 5px !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.sh-page .tr-prep .td-name { font-style: italic; color: #d35400; text-decoration: underline; }
body.sh-page .buy-input { width: 40px; padding: 6px 0; border: 1px solid var(--sh-line); border-radius: 4px; text-align: center; font-size: 13px; color: var(--sh-ink-2); }
body.sh-page .buy-input.is-def { border: 2px solid var(--sh-good); color: #000; font-weight: bold; }
body.sh-page .buy-input:focus { border-color: var(--sh-accent); outline: none; background: var(--sh-accent-wash); }
body.sh-page .quick-btn { width: 100%; padding: 12px 5px; border: 1px solid var(--sh-line); border-radius: 6px; background: var(--sh-line-soft); font-weight: bold; color: var(--sh-ink-2); cursor: pointer; font-size: 13px; white-space: nowrap; }
body.sh-page .quick-btn.active { background: var(--sh-good); color: white; border-color: var(--sh-good); }
body.sh-page .store-title.saved-state { background: #2e7d32; }
body.sh-page .store-title.saved-state .save-badge { display: block; }
body.sh-page .update-status-bar.saved { color: #fff; background: var(--sh-good); font-weight: bold; }
