:root {
  --bg: #f4f5f7;
  --surface: #ffffff;
  --border: #e2e4e9;
  --text: #1f2430;
  --text-muted: #6b7280;
  --primary: #2563eb;
  --primary-dark: #1d4ed8;
  --success: #16a34a;
  --danger: #dc2626;
  --warn: #d97706;
  --radius: 8px;

  /* Type scale -- every font-size in this file comes from one of these. Sizes are named by
     role, not by number, so a new screen picks the role instead of inventing a size. */
  --fs-xs: 11px;        /* badges, table headers, timestamps */
  --fs-sm: 12px;        /* hints, field + stat labels, footers, captions */
  --fs-base: 13px;      /* body default, table cells, buttons, tabs, nav */
  --fs-control: 14px;   /* form inputs -- deliberately one step up for typing legibility */
  --fs-title: 15px;     /* section titles */
  --fs-icon: 18px;      /* bare glyph buttons (close, remove-row) */
  --fs-display: 20px;   /* app + login title */
  --fs-stat: 26px;     /* stat-card hero numbers */

  /* Spacing scale. Every gap and padding in the standardised components comes from here, so
     density is changed in one place rather than by editing 350 inline styles again. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;

  /* Neutrals the palette was missing. Everything used to reach for --bg or --border for jobs
     neither was meant for -- a hover fill, a group heading, a rail. */
  --surface-sunken: #f8f9fb;
  --surface-hover: #eef1f6;
  --border-strong: #cdd2db;
  --text-faint: #98a0ad;
  --accent-soft: #eaf1fe;

  --elev-1: 0 1px 2px rgba(16, 24, 40, 0.05);
  --elev-2: 0 4px 12px rgba(16, 24, 40, 0.08);

  --shell-nav-h: 44px;
  --sidebar-w: 248px;
  --sidebar-rail-w: 56px;
  --control-h: 37px;   /* every single-line form control */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* Explicit base: without this, anything lacking a font-size falls back to the browser's
     16px default -- larger than every heading here except .stat-value. */
  font-size: var(--fs-base);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}

/* Form controls do not inherit the page font by default -- they take the UA's own font
   shorthand. Without this, every <button> in the app renders in Arial rather than the system
   stack above, which reads as a different typeface next to the text around it. */
button, input, select, textarea { font-family: inherit; }

/* ---------- login ---------- */
.login-body {
  display: flex; align-items: center; justify-content: center; min-height: 100vh;
}
.login-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 32px; width: 360px; box-shadow: 0 10px 30px rgba(0,0,0,0.06);
}
.login-brand { margin-bottom: 20px; text-align: center; }
.brand-row { display: flex; align-items: center; justify-content: center; gap: 10px; }
.brand-logo { height: 48px; width: auto; max-width: 260px; object-fit: contain; }
.brand-subtitle { display: block; font-weight: 400; font-size: var(--fs-base); color: var(--text-muted); margin-top: 4px; }
.login-brand .brand-subtitle {
  font-weight: 700; font-size: var(--fs-display); text-transform: uppercase; letter-spacing: 0.02em;
  color: var(--text); margin-top: 10px; line-height: 1.3;
}
.login-submit { width: 100%; margin-top: 6px; }
.login-links { margin-top: 14px; text-align: center; font-size: var(--fs-base); }
.login-links a { color: var(--primary); text-decoration: none; }
.login-footer {
  margin-top: 16px; text-align: center; font-size: var(--fs-xs); color: var(--text-muted);
}

/* ---------- app footer ---------- */
.app-footer {
  padding: 14px 24px; text-align: center; font-size: var(--fs-sm); color: var(--text-muted);
  border-top: 1px solid var(--border); margin-top: 24px;
}

/* ---------- top bar / nav ---------- */
.topbar {
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px 24px; background: var(--surface); border-bottom: 1px solid var(--border);
}
.topbar-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand-logo-sm { height: 36px; width: auto; max-width: 160px; object-fit: contain; }
.brand .brand-subtitle {
  display: inline-block; margin-top: 0; margin-left: 0; white-space: normal; max-width: 420px; line-height: 1.2;
  font-weight: 700; font-size: var(--fs-display); text-transform: uppercase; letter-spacing: 0.02em; color: var(--text);
}
@media (max-width: 640px) {
  .brand-logo-sm { height: 28px; }
  .brand .brand-subtitle { font-size: var(--fs-control); max-width: 220px; }
}

.main-nav { display: flex; gap: 4px; flex-wrap: wrap; }
.main-nav button {
  border: none; background: none; padding: 8px 12px; font-size: var(--fs-base); cursor: pointer;
  color: var(--text-muted); border-radius: 6px; font-weight: 500;
}
.main-nav button.active { color: var(--primary); background: #eff4ff; }
.user-box { display: flex; align-items: center; gap: 10px; }
.user-info { font-size: var(--fs-sm); color: var(--text-muted); text-align: right; line-height: 1.4; }
.user-info b { color: var(--text); }

.user-menu { position: relative; }
.user-info-btn { background: none; border: none; padding: 4px 8px; border-radius: 6px; cursor: pointer; font-size: var(--fs-base); }
.user-info-btn:hover { background: #f4f5f7; }
.user-menu-dropdown {
  position: absolute; top: calc(100% + 4px); right: 0; min-width: 170px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 4px 14px rgba(0,0,0,0.08); padding: 4px; z-index: 20;
}
.user-menu-dropdown.hidden { display: none; }
.user-menu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 8px 10px; font-size: var(--fs-base); color: var(--text); border-radius: 6px; cursor: pointer;
}
.user-menu-item:hover { background: #f4f5f7; }
.btn-sm { padding: 6px 10px; font-size: var(--fs-sm); }

main#appContent { max-width: 1180px; margin: 24px auto; padding: 0 24px 60px; }

/* ---------- cards / dashboard ---------- */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; margin-bottom: 28px; }
.stat-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 18px;
}
.stat-card .stat-value { font-size: var(--fs-stat); font-weight: 700; }
.stat-card .stat-label { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 4px; }
.section-title { font-size: var(--fs-title); font-weight: 700; margin: 24px 0 12px; }
.subsection-title { margin: 0 0 8px; font-size: var(--fs-base); font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.panel-header-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.inline-checkbox-label { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-base); font-weight: 400; color: var(--text-muted); }
.group-label { display: block; font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted); margin-bottom: 10px; }
/* Week Selector label -- classed rather than left bare so it inherits the scale, not the
   browser default (it previously rendered at 16px, larger than .section-title above it). */

/* ---------- generic panels / forms ---------- */
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 22px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 14px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 14px; }
/* One row of filters instead of a column of near-empty thirds. A fixed 3-column grid gave each
   control a third of the page whether it needed it or not, and pushed the date box onto its own
   line below the Level select -- so choosing a scope and choosing a day looked like two
   unrelated steps. Flex lets each control take the width it actually needs and wrap only when
   the row genuinely runs out of room. */
.filter-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; margin: 14px 0; }
.filter-bar > .field { flex: 0 1 auto; min-width: 150px; }
.filter-bar > .field > select, .filter-bar > .field > input { min-width: 150px; }
/* The period's own control is injected here after render. The slot is a bare div, not a .field:
   the injected control brings its own .field (with its own label), and nesting one inside
   another put the "Date" label on a different baseline from "Level" beside it. */
/* display:contents, so whatever the period renders here becomes a direct child of the bar and
   lines up with Level like any other control. As a box of its own it was bottom-aligned as a
   unit, so Custom Date Range's explanatory line -- which sits under its two date boxes -- made
   the whole slot taller and pushed Level 30px below the date inputs beside it. */
.filter-bar > .period-slot { display: contents; }
.filter-bar > .period-slot > .field { margin: 0; min-width: 150px; }
.filter-bar > .period-slot > .action-row { margin: 0; }
/* Custom Date Range ships its two date boxes in a .grid-3; three fixed columns would leave a
   hole where the third should be. */
.filter-bar > .period-slot > .grid-3 { display: flex; gap: 16px; margin: 0; }
/* Its explanatory line takes a line of its own under the bar rather than wedging between
   controls. */
.filter-bar > .period-slot > .hint { flex: 1 0 100%; order: 2; margin: 2px 0 0; }
/* Only the buttons are pushed right, so the row reads scope -> period -> compare ... actions.
   Putting the auto margin on the checkbox instead stranded it alone at the far right whenever
   the buttons wrapped to a second line, which Custom Date Range's two date boxes force. */
.filter-bar-check { flex: 0 0 auto; }
.filter-bar-actions { flex: 0 0 auto; margin-left: auto; }
@media (max-width: 768px) {
  .filter-bar-check { margin-left: 0; }
  .filter-bar > .field { flex: 1 1 100%; }
}

/* min-width:0 is load-bearing, not tidying. A grid or flex item defaults to min-width:auto,
   which refuses to shrink below its content's intrinsic width -- and a <select> is as wide as
   its longest option. A product dropdown listing "SKU - Name (supplier)" therefore forced its
   column to ~490px, pushing the whole page sideways once .grid-3 collapsed to one column on a
   phone. */
.field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.field label { font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted); }
/* Inside a grid, let the label take up the slack so the INPUTS line up across a row. Grid rows
   stretch every cell to the tallest, so without this a label that wraps to two lines pushes its
   own input down while its single-line neighbour's input stays up -- the boxes end up on
   different lines. Growing the label instead bottom-aligns every input in the row. */
.grid-2 > .field label, .grid-3 > .field label { flex: 1 0 auto; }

input, select, textarea {
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: var(--fs-control);
  font-family: inherit; background: var(--surface); color: var(--text);
  line-height: 1.35;
  /* Never wider than whatever is holding it. Inline width/max-width still win where a control
     is deliberately sized (a 90px quantity box, say); this only stops the runaway case. */
  max-width: 100%;
}
/* Left to themselves these three size differently -- a <select> lands ~2px taller than a text
   input, and a date input ~2px taller again -- so any row mixing them staircases. One height
   for every single-line control instead. Box-sizing is already border-box globally, so this is
   the outer height exactly. */
input:not([type="checkbox"]):not([type="radio"]), select { height: var(--control-h); }
textarea { resize: vertical; }

/* ...except that the height above is a request WebKit does not have to grant. A date or time
   field is drawn from its own shadow content, and WebKit sizes the box to that content rather
   than to the height set here -- so on an iPad (where every browser, Chrome included, is
   WebKit underneath) these stand visibly taller than the text fields and selects beside them.

   Resetting the native appearance is what makes the box model apply to them at all. The two
   shadow parts then need their own margin and padding cleared, or the value inside props the
   box back open from within. Chrome on the desktop already honoured the height, and none of
   this changes what it renders -- the calendar button and the value are untouched. */
input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] {
  -webkit-appearance: none;
  appearance: none;
}
input::-webkit-date-and-time-value { margin: 0; text-align: left; }
input::-webkit-datetime-edit { padding: 0; }

.btn {
  border: none; border-radius: 6px; padding: 9px 16px; font-size: var(--fs-base); font-weight: 600; cursor: pointer;
}
/* A link styled as a button still carries the anchor underline and link colour. */
a.btn { display: inline-flex; align-items: center; text-decoration: none; color: var(--text); }
a.btn-primary { color: #fff; }
.btn-primary { background: var(--primary); color: white; }
.btn-primary:hover { background: var(--primary-dark); }
.btn-secondary { background: #eef0f4; color: var(--text); }
.btn-secondary:hover { background: #e2e5ec; }
.btn-danger { background: #fde8e8; color: var(--danger); }
.btn-danger:hover { background: #fbd5d5; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.form-actions { display: flex; gap: 10px; margin-top: 18px; }
.form-msg { margin-top: 10px; font-size: var(--fs-base); }
.form-msg.error { color: var(--danger); }
.form-msg.success { color: var(--success); }
.hint { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 6px; }

/* ---------- tables ---------- */
.items-table, .data-table { width: 100%; border-collapse: collapse; }
.items-table th, .data-table th {
  text-align: left; font-size: var(--fs-xs); color: var(--text-muted); text-transform: uppercase;
  letter-spacing: 0.03em; padding: 8px 6px; border-bottom: 1px solid var(--border);
}
.items-table td, .data-table td { padding: 6px; vertical-align: middle; font-size: var(--fs-base); border-bottom: 1px solid var(--border); }
.items-table input, .items-table select { width: 100%; font-size: var(--fs-base); padding: 6px 8px; }
.items-table .qty-input { width: 80px; }
.items-table .remove-item { background: none; border: none; color: var(--danger); cursor: pointer; font-size: var(--fs-icon); }
.num { text-align: right; }
.items-table th.num, .data-table th.num { text-align: right; }
/* Dashboard breakdown tables sit stacked one under another -- Sales by Payment Method, by
   Store, by Brand. Left to the default auto layout each one sizes its columns from its own
   content, so "Gross Sales" lands at a different x in every table and the money appears to
   wander left and right down the page. Fixed layout with one width for every figure column
   puts them in a single vertical line, which is the whole point of stacking them. */
/* Desktop only. Below 768px these same tables become .table-cards -- each row a card with
   flex cells in a two-column grid -- and a forced column width would fight that layout. */
@media (min-width: 769px) {
  .metric-table { table-layout: fixed; }
  /* Wide enough for a peso figure in the millions, narrow enough that a six-column P2S table
     still leaves its name column readable. */
  .metric-table th.num, .metric-table td.num { width: 130px; }
  /* A trailing flag column holds a short badge, not a figure. Without a width of its own it
     splits the leftover space equally with the name column beside it, which squeezed "Brand"
     down to 90px while every number had 170. */
  .metric-table th:last-child:not(.num) { width: 110px; }
  .metric-table th:first-child, .metric-table td:first-child { overflow-wrap: anywhere; }
}

.data-table tbody tr:not(.week-group-header) { cursor: pointer; }
.data-table tbody tr:not(.week-group-header):hover { background: #f7f9fc; }
.data-table th.sortable { cursor: pointer; user-select: none; }
.data-table th.sortable:hover { color: var(--text); }
.sort-arrow { margin-left: 4px; font-size: var(--fs-xs); }
.data-table tr.week-group-header td {
  background: var(--bg); font-weight: 600; font-size: var(--fs-sm); color: var(--text);
  padding: 10px 6px; border-bottom: 1px solid var(--border); cursor: default;
}
.week-group-meta { margin-left: 10px; font-weight: 400; color: var(--text-muted); text-transform: none; letter-spacing: normal; }
.dr-matrix-cell { cursor: pointer; }
.dr-matrix-cell:hover { filter: brightness(0.95); }

.list-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; font-size: var(--fs-base); color: var(--text-muted); flex-wrap: wrap; }
/* A button standing in a toolbar beside a date box or a filter has to be the same height as
   it, or the row staircases -- the date steppers and the 30d/60d/90d presets were 33px against
   37px controls. Only toolbars: a button in a page header, in the action row under a form, or
   in a table row has nothing beside it to line up with, and keeps its own compact size. */
.list-toolbar .btn { height: var(--control-h); }

/* ---------- badges ---------- */
.badge { display: inline-block; padding: 3px 9px; border-radius: 999px; font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; }
.badge-draft { background: #eef0f4; color: #4b5563; }
.badge-submitted_rm, .badge-submitted_om, .badge-pending_approval, .badge-generated, .badge-approved_pending_release, .badge-pending_final_approval, .badge-central_pending_approval { background: #fef3c7; color: var(--warn); }
.badge-ready_for_batch, .badge-in_batch, .badge-sent_to_supplier { background: #dbeafe; color: var(--primary-dark); }
.badge-approved, .badge-issued, .badge-fully_delivered, .badge-supplier_acknowledged { background: #dcfce7; color: var(--success); }
.badge-partially_delivered { background: #fef3c7; color: var(--warn); }
.badge-cancelled, .badge-superseded { background: #eef0f4; color: var(--text-muted); }
.badge-returned_kitchen, .badge-returned_rm, .badge-returned, .badge-rejected, .badge-final_approval_rejected { background: #fde2e2; color: var(--danger); }
.badge-active { background: #dcfce7; color: var(--success); }
.badge-inactive { background: #eef0f4; color: var(--text-muted); }
.badge-alert { background: #fee2e2; color: var(--danger); }

/* Daily Inventory (spec #22) */
.badge-pending, .badge-counted, .badge-pending_rm { background: #fef3c7; color: var(--warn); }
.badge-recount_required, .badge-returned_recount, .badge-returned_correction, .badge-recounted { background: #fde2e2; color: var(--danger); }
.badge-escalated, .badge-escalated_om, .badge-late { background: #fee2e2; color: var(--danger); }
.badge-missed { background: #eef0f4; color: var(--text-muted); }

/* Daily Sales Reports */
.badge-pending_rm_verification { background: #fef3c7; color: var(--warn); }
.badge-posted { background: #dcfce7; color: var(--success); }
.badge-returned_for_correction { background: #fde2e2; color: var(--danger); }
.badge-reopen_requested { background: #fef3c7; color: var(--warn); }
.badge-reopened_for_correction { background: #fde2e2; color: var(--danger); }

/* Daily Reports. These were specified when the module was built and never written, so every
   status badge in the largest module rendered as a bare pill -- .badge sets no background or
   colour of its own, and only dr_draft was reaching a rule (drStatusTone maps it to draft /
   submitted_rm). Same colour pairs as every other module, so a status reads the same way
   whichever screen it is on. */
.badge-dr_draft { background: #eef0f4; color: #4b5563; }
.badge-dr_submitted { background: #fef3c7; color: var(--warn); }
.badge-dr_rm_verified { background: #dbeafe; color: var(--primary-dark); }
.badge-dr_om_approved { background: #dcfce7; color: var(--success); }
.badge-dr_approved_with_open_ca { background: #fef3c7; color: var(--warn); }
.badge-dr_returned_for_correction, .badge-dr_returned_by_om,
.badge-dr_rejected { background: #fde2e2; color: var(--danger); }
.badge-dr_not_required, .badge-dr_store_closed { background: #eef0f4; color: var(--text-muted); }

/* Packet roll-up, shown per store. in_progress takes the in-flight blue rather than a warning
   colour: work underway is not a problem. */
.badge-not_started { background: #eef0f4; color: #4b5563; }
.badge-in_progress { background: #dbeafe; color: var(--primary-dark); }
.badge-fully_approved { background: #dcfce7; color: var(--success); }
.badge-has_exceptions { background: #fde2e2; color: var(--danger); }
.badge-store_closed { background: #eef0f4; color: var(--text-muted); }

/* Repeated 13 times as an inline style before this existed. */
.empty-cell { text-align: center; color: var(--text-muted); padding: 20px; }

/* Daily Sales Report -- supporting documents. Every required document keeps a slot whether or
   not it has been attached, so a missing one is visible rather than merely absent. */
.doc-slots { display: grid; gap: 10px; margin: 12px 0 4px; }
.doc-slot { border: 1px solid var(--border); border-radius: 6px; padding: 12px 14px; }
.doc-slot.is-needed { border-color: var(--danger); }
.doc-slot.is-held { border-color: var(--success); }
.doc-slot-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.doc-slot-name { font-weight: 600; font-size: var(--fs-base); }
.doc-slot-file { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.doc-slot-upload { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.doc-slot-upload input[type="file"] { max-width: 260px; }

/* A photo belongs to the line it is of, so the control sits in the line. */
.dr-row-photo { white-space: nowrap; }
/* A multi-file input reads "Choose Files / No file chosen", which the old 190px cut in half --
   the control looked broken rather than narrow. Dropping the cap was not enough on its own:
   .items-table sets width:100% on every input, so this one collapsed to whatever width the rest
   of the cell happened to need, which was 42px. It sizes to its own content now, with a floor
   so the column has to make room for it. */
.dr-row-photo input[type="file"] {
  display: block; margin-top: 5px; width: auto; min-width: 200px; padding: 4px 0; border: 0;
  background: none; height: auto;
}
.dr-row-photo-error { display: block; max-width: 240px; white-space: normal; margin-top: 4px; }
/* The meat is a label on an assortment line, not a control. Kept on one line with the rest of
   the cell, and allowed to be as wide as the name it has to show. */
.dr-cell-product-name { display: block; padding: 6px 0; font-weight: 600; white-space: nowrap; }
.dr-photo-chip { display: inline-flex; align-items: center; gap: 4px; margin: 4px 4px 0 0;
                 padding: 1px 4px 1px 7px; border: 1px solid var(--border); border-radius: 999px;
                 font-size: var(--fs-xs); }
.dr-photo-remove { border: 0; background: none; cursor: pointer; color: var(--danger);
                   font-size: var(--fs-base); line-height: 1; padding: 0 3px; }
.dr-unclaimed-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 7px 0;
                    border-bottom: 1px solid var(--border); }
.dr-unclaimed-row:last-of-type { border-bottom: 0; }
.dr-unclaimed-row select { max-width: 260px; margin-left: auto; }

/* EOD Compliance */
.badge-COMPLETE { background: #dcfce7; color: var(--success); }
.badge-SALES_REPORT_PENDING, .badge-INVENTORY_COUNT_PENDING { background: #eef0f4; color: var(--text-muted); }
.badge-RM_SALES_APPROVAL_PENDING, .badge-INVENTORY_APPROVAL_PENDING { background: #fef3c7; color: var(--warn); }
.badge-RECOUNT_REQUIRED { background: #fde2e2; color: var(--danger); }
.badge-EXCEPTION_REQUIRES_ATTENTION { background: #fee2e2; color: var(--danger); }

/* Sales Exceptions */
.badge-outstanding { background: #fef3c7; color: var(--warn); }
.badge-resolved { background: #dcfce7; color: var(--success); }
.badge-warning { background: #fef3c7; color: var(--warn); }
.badge-requires_om_review { background: #fee2e2; color: var(--danger); }

/* Every control in a form field fills its cell, so a row of them lines up whatever each
   control's own intrinsic width happens to be. <input type="time"> is the reason: browsers
   size it around their native time picker, so it rendered noticeably wider than the selects
   and number boxes beside it. min-width:0 lets it shrink to the column like everything else. */
.field > input, .field > select, .field > textarea { width: 100%; min-width: 0; }

/* A range's two boxes hold a couple of digits, not a sentence. Left to the table's default
   they stretch across the column, putting the number an inch from its own heading. */
.items-table input.dr-th-min, .items-table input.dr-th-max { width: 7em; }

.dr-total-uom { font-weight: 400; color: var(--text-muted); margin-left: 3px; }

/* A product picker inside a report table. Left to the column's natural width it collapses to
   a few characters, which hides the very thing the person is choosing. */
select.dr-cell-product { min-width: 15em; }
@media (max-width: 768px) { select.dr-cell-product { min-width: 11em; } }
/* The same room once the report is filed and the picker becomes plain text. Without it the
   column is whatever the table has left over, so the identical sheet read narrower on the
   reconciliation with more columns -- the meat's name wrapping a word to a line on one report
   and sitting comfortably on the other. */
.dr-cell-product-col { min-width: 15em; }
@media (max-width: 768px) { .dr-cell-product-col { min-width: 11em; } }

/* The undertaking whoever submits a report is making. */
.dr-certification { margin-top: var(--sp-3); }
.dr-certification p { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); color: var(--text-muted); max-width: 90ch; }
.dr-cert-facts { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp-1) var(--sp-4);
                 margin: var(--sp-3) 0 0; font-size: var(--fs-sm); }
.dr-cert-facts dt { color: var(--text-muted); }
.dr-cert-facts dd { margin: 0; font-weight: 600; }

/* A reconciliation sheet runs to nine columns. It scrolls inside its own box rather than
   pushing the page sideways, so the rest of the form stays where it is. */
/* Wide tables scroll inside their panel rather than pushing the page sideways. The mobile
   breakpoint already does this for every table; this is for the widths in between -- a tablet
   is past 768px and still narrower than an eleven-column table. */
.table-scroll { overflow-x: auto; }
.table-scroll > table { min-width: 100%; }
.dr-table-scroll { overflow-x: auto; }
.dr-table-scroll > table { min-width: 100%; }
/* A figure has to be readable while it is being typed. Table layout will squeeze a column to
   whatever is left over, and a production sheet's weights run to five digits and a spinner --
   at which point the person entering 12,500 g can see "125". A floor on the entry boxes stops
   that; when the row then no longer fits, the table scrolls inside its own box, which is what
   the box above is for. */
.dr-table-scroll input.dr-cell[type="number"] { min-width: 7em; }

/* The reason a production row's figures depart from what the other columns imply. */
.dr-why-row td { background: #fffbeb; }
.dr-why-label { display: block; font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted); margin-bottom: var(--sp-1); }
.dr-why-label .req { color: var(--danger); }
input.dr-why { width: 100%; }
.dr-exception-list { margin: var(--sp-2) 0 var(--sp-1); padding-left: var(--sp-4); }
.dr-exception-list li { margin-bottom: var(--sp-2); }
.dr-exception-why { margin-top: 2px; font-size: var(--fs-sm); color: var(--text-muted); }

/* Template builder: a field's settings, folded away until asked for. */
.builder-section { border: 1px solid var(--border); border-radius: 6px; padding: var(--sp-3); margin-bottom: var(--sp-3); }
.builder-settings-row td { background: var(--bg); }
.builder-settings { padding: var(--sp-2) 0; }
.builder-summary { margin-left: var(--sp-2); }
.builder-columns { margin-top: var(--sp-3); }
.builder-columns .subsection-title { margin-bottom: var(--sp-2); }

/* A daily-report table column the system works out rather than asking anyone to type. */
.dr-calc { display: inline-block; min-width: 3ch; text-align: right; font-variant-numeric: tabular-nums;
           color: var(--text-muted); font-weight: 600; }
/* An entered figure that disagrees with what the other columns imply. Shown, never blocked --
   the scale and the arithmetic can legitimately differ, and that gap is the point. */
.dr-mismatch { border-color: var(--warn) !important; background: #fffbeb; }
input.dr-mismatch:focus { outline-color: var(--warn); }

/* Store Assortment's editable columns.
   These cells hold input controls, not displayed figures, so they are NOT .num. Right-aligning
   the cell pushes the box to the cell's right edge while the digits inside it stay left --
   which is what made the header row zig-zag left/right/left across the table. Left-aligning
   the whole group means every heading shares a left edge with the control it labels. */
.assort-input { width: 90px; }
.assort-select { width: 110px; }
/* Digits line up with each other within the column, without moving the box itself. */
.assort-input { text-align: right; }

/* A system-generated draft PR is awaiting its Restaurant Manager. */
.badge-system_draft { background: #e0e7ff; color: #3730a3; }

/* The "not an approved purchase order" notice on a system draft. Same shape as
   .warning-banner but informational rather than cautionary, so it reads as context instead of
   a problem. */
.auto-pr-banner {
  background: #eef2ff;
  color: #3730a3;
  border-color: #c7d2fe;
}

/* The hand-off chain an automated purchase request travels. Wraps on a narrow screen rather
   than scrolling, so a phone shows the whole chain stacked instead of half of it. */
.stage-track {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
  margin: 0 0 var(--sp-4);
}
.stage-step {
  font-size: var(--fs-sm);
  font-weight: 600;
  padding: var(--sp-1) 10px;
  border-radius: 999px;
  background: var(--bg);
  color: var(--text-muted);
  border: 1px solid var(--border);
}
.stage-step.stage-done { background: #dcfce7; color: var(--success); border-color: #bbf7d0; }
.stage-step.stage-current { background: #e0e7ff; color: #3730a3; border-color: #c7d2fe; }
.stage-step.stage-cancelled { background: #fee2e2; color: var(--danger); border-color: #fecaca; }
.stage-sep { color: var(--text-muted); font-size: var(--fs-sm); }

/* Command Center priority */
.badge-CRITICAL { background: #fee2e2; color: var(--danger); }
.badge-HIGH { background: #fef3c7; color: var(--warn); }
.badge-NORMAL { background: #eef0f4; color: var(--text-muted); }

.warning-banner {
  background: #fef3c7;
  color: #92400e;
  border: 1px solid #fcd34d;
  border-radius: var(--radius);
  padding: 10px 14px;
  font-size: var(--fs-base);
  margin-bottom: 14px;
}

/* ---------- overlay / detail ---------- */
.overlay { position: fixed; inset: 0; background: rgba(15,18,25,0.45); display: flex; align-items: flex-start; justify-content: center; padding: 32px 16px; overflow-y: auto; }
.overlay.hidden { display: none; }
.detail-panel { position: relative; background: var(--surface); width: 100%; max-width: 900px; border-radius: var(--radius); max-height: calc(100vh - 64px); overflow-y: auto; padding: 24px; }
.close-btn { position: absolute; top: 14px; right: 14px; background: none; border: none; font-size: var(--fs-icon); cursor: pointer; color: var(--text-muted); }
.detail-section { margin-bottom: 20px; }
.detail-section h4 { margin: 0 0 8px; font-size: var(--fs-base); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.detail-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; font-size: var(--fs-base); }
.detail-grid div div:first-child { color: var(--text-muted); font-size: var(--fs-sm); margin-bottom: 2px; }
.action-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.approval-actions textarea { width: 100%; min-width: 220px; margin-bottom: 10px; }

/* ---------- activity log ---------- */
.activity-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.activity-item { border-left: 3px solid var(--border); padding: 4px 0 4px 12px; font-size: var(--fs-sm); }
.activity-item .ts { color: var(--text-muted); font-size: var(--fs-xs); }

/* ---------- breakdown mini-tables ---------- */
.breakdown-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
.breakdown-col table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.breakdown-col td { padding: 4px 2px; border-bottom: 1px solid var(--border); }
.breakdown-col td:last-child { text-align: right; font-variant-numeric: tabular-nums; }

.email-preview {
  background: #fafbfc; border: 1px solid var(--border); border-radius: 6px;
  padding: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-sm); white-space: pre; overflow-x: auto; max-height: 340px; overflow-y: auto;
}

/* wrap: System Administration carries twelve sections, more than fit one row on a tablet. */
.tab-strip { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--border); }
.tab-strip button { border: none; background: none; padding: 10px 14px; font-size: var(--fs-base); cursor: pointer; color: var(--text-muted); border-bottom: 2px solid transparent; }
.tab-strip button.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }

.checkbox-row { display: flex; align-items: center; gap: 8px; }
.checkbox-row input[type="checkbox"] { width: auto; }

/* ---------- touch ergonomics ----------
   Keyed to pointer type, NOT viewport width. Only one breakpoint exists (768px), so every
   iPad above it -- 10th gen (820), Pro 11" (834), and ALL iPads in landscape (1024+) -- was
   getting the desktop treatment: 14px fields that make iOS Safari auto-zoom on focus, and
   16-37px tap targets. A 1024px iPad is still a thumb, so these rules follow the input
   device instead. `pointer: coarse` reports the PRIMARY pointer, so a trackpad laptop with a
   touchscreen stays on the desktop sizing. */
@media (pointer: coarse) {
  /* 16px literal, deliberately off-scale: below it, iOS Safari zooms the viewport when a
     field takes focus, yanking the layout around mid-entry. */
  /* Taller on touch as well: 44px is the Apple HIG minimum tap target. */
  input, select, textarea { padding: 10px 12px; font-size: 16px; }
  :root { --control-h: 44px; }
  /* 44px is the Apple HIG minimum (Material asks 48dp). Buttons centre their own label, so
     min-height alone is enough; both checkbox wrappers are already centred flex rows --
     .checkbox-row (period/compare) and .inline-checkbox-label ("Show inactive"). */
  .main-nav button, .btn, .tab-strip button { min-height: 44px; }
  .checkbox-row, .inline-checkbox-label { min-height: 44px; }
  /* "Forgot Password" is a bare 15px-tall text link and the only route to that action on the
     login screen -- the one standalone (non-.btn) link in the app that isn't inside an
     already-clickable table row. */
  .login-links a { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 12px; }
  /* A bare checkbox keeps the browser's ~13px intrinsic box whatever padding it is given, so
     the rules above do not reach it. Store Assortment is a per-SKU column of them -- the one
     screen where this is genuinely hard to hit on a phone. */
  input[type="checkbox"], input[type="radio"] { width: 22px; height: 22px; padding: 0; }
  /* The drawer's own collapse control sits at 32px; it is a real target on a touch screen. */
  .sidebar-collapse { min-height: 44px; }
}

/* Photo-verified checklist items. Filled on a phone, in a kitchen, one-handed -- so each item
   is a full-width card with one obvious action, not a row in a grid. Complete and incomplete
   have to be tellable apart at a glance across the whole list, which is what the left edge and
   the tick are for; colour alone would not survive a bright kitchen or a colour-blind reader. */
.dr-check {
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius);
  padding: var(--sp-3);
  margin-bottom: var(--sp-3);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.dr-check.is-done { border-left-color: var(--success); }
.dr-check.is-todo { border-left-color: var(--warn); }
.dr-check-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.dr-check-mark {
  flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--border-strong);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 700; color: #fff;
}
.dr-check.is-done .dr-check-mark { background: var(--success); border-color: var(--success); }
.dr-check-label { font-weight: 600; font-size: var(--fs-base); color: var(--text); flex: 1 1 12em; }
.dr-check-photo { display: inline-block; max-width: 220px; }
.dr-check-photo img {
  width: 100%; height: auto; max-height: 160px; object-fit: cover;
  border-radius: var(--radius); border: 1px solid var(--border); display: block;
}
/* The file input is the button: a bare one reads as a system control and says "No file chosen"
   next to it, which on a phone is noise beside the thing you actually tap. */
.dr-check-attach { position: relative; overflow: hidden; display: inline-flex; align-items: center; }
.dr-check-attach input[type="file"] {
  position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer;
}
.dr-check-caption { width: 100%; }
/* Sits with the item it belongs to rather than at the foot of the panel, and closes up
   entirely when there is nothing to say. */
.dr-check-error { margin-top: 6px; font-size: var(--fs-sm); }
/* The one line on Store Inventory Level that says when its figures are from. Larger than a
   section title because it is the reading the whole table depends on. */
.stock-as-of { margin: 0 0 4px; font-size: var(--fs-display); text-transform: none;
               letter-spacing: normal; }

/* Frozen column headings on long lists. A list is unreadable once its headings scroll away --
   a row of bare values tells you nothing about which column each one belongs to.

   The table becomes its own scrolling box rather than being wrapped in one. A sticky heading
   sticks to its nearest scrolling ancestor, so it needs one to exist; making the table itself
   that ancestor means no list has to be re-wrapped in markup, and the anonymous table box the
   browser generates for the row groups keeps every column aligned exactly as before.

   Which tables get this, and how tall they are, is decided in app.js -- it depends on where a
   table sits on the page, which CSS cannot ask about. */
.data-table.has-frozen-head { display: block; overflow: auto; }
.data-table.has-frozen-head thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--frozen-head-bg, var(--bg));
  /* border-collapse leaves the real border behind when the cell is stuck, so the rule under
     the headings is drawn inside the cell instead. */
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--border);
}
/* Headings have to match whatever the rows sit on, or they read as a grey band across a white
   panel. */
.panel .data-table.has-frozen-head,
.detail-panel .data-table.has-frozen-head { --frozen-head-bg: var(--surface); }

/* ---------- narrow screens (layout only) ---------- */
@media (max-width: 768px) {
  main#appContent { padding: 0 12px 40px; margin: 16px auto; }
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
  .breakdown-grid { grid-template-columns: 1fr; }
  .detail-grid { grid-template-columns: 1fr; }
  .btn { padding: 11px 16px; font-size: var(--fs-control); }
  .data-table, .items-table { display: block; overflow-x: auto; white-space: nowrap; }
  .main-nav { gap: 2px; }
  .main-nav button { padding: 8px 10px; font-size: var(--fs-sm); }
  /* 6 stat cards at a 200px minimum track fall to one column here, which is ~565px of
     scrolling before the tables start. 150px pairs them up in the ~351px available. */
  .card-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}

/* ---------- Dialogs & toasts ----------------------------------------------------------
   Replaces the browser's native alert/confirm/prompt. Those render with the origin as a
   prefix ("s-roms-v2-production.up.railway.app says..."), cannot be styled, block the whole
   tab, and on a tablet read as a browser warning rather than part of the app -- which matters
   for shop-floor users. Three tiers: inline .form-msg stays for anything tied to a field,
   toasts for action feedback with no form context, and .dialog-panel only for interactions
   that genuinely must block (destructive confirms, required input). */
.dialog-panel { position: relative; background: var(--surface); width: 100%; max-width: 460px;
  border-radius: var(--radius); padding: 20px; margin-top: 10vh; box-shadow: 0 10px 30px rgba(15,18,25,0.2); }
.dialog-title { margin: 0 0 8px; font-size: var(--fs-title); font-weight: 700; color: var(--text); }
.dialog-message { margin: 0 0 14px; font-size: var(--fs-base); color: var(--text-muted); line-height: 1.5; }
.dialog-field { margin-bottom: 12px; }
.dialog-field label { display: block; font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-muted); margin-bottom: 4px; }
.dialog-field input[type="text"], .dialog-field input[type="password"],
.dialog-field select, .dialog-field textarea { width: 100%; }
.dialog-field textarea { min-height: 72px; resize: vertical; }
.dialog-error { font-size: var(--fs-sm); color: var(--danger); margin: 0 0 10px; min-height: 1em; }
.dialog-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 6px; }

.toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 1000;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end; pointer-events: none; }
.toast { pointer-events: auto; background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-left: 4px solid var(--text-muted);
  border-radius: var(--radius); padding: 10px 14px; font-size: var(--fs-base);
  max-width: 380px; box-shadow: 0 6px 18px rgba(15,18,25,0.15);
  animation: toast-in 140ms ease-out; }
.toast.success { border-left-color: var(--success); }
.toast.error { border-left-color: var(--danger); }
.toast.warning { border-left-color: var(--warn); }
.toast.warn { border-left-color: var(--warn); }
.toast.leaving { opacity: 0; transform: translateY(4px); transition: opacity 160ms, transform 160ms; }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  .toast.leaving { transition: none; }
}
/* On a phone the bottom-right corner is awkward; span the width instead. */
@media (max-width: 768px) {
  .toast-container { left: 12px; right: 12px; bottom: 12px; align-items: stretch; }
  .toast { max-width: none; }
}

/* Overlay panels get tighter chrome on a phone/tablet, where the default 32px overlay padding
   plus 24px panel padding eats most of the usable width. Applies to both the master-data form
   modal and the product catalog picker. */
@media (max-width: 768px) {
  .overlay { padding: 12px 8px; }
  .detail-panel, .dialog-panel { padding: 16px; }
  .dialog-panel { margin-top: 4vh; }
}

/* Row action cells: .action-row wraps by design (it is used for page-level button groups where
   wrapping is right), but inside a table cell that stacks Edit above Deactivate and doubles the
   row height. Keep them side by side and let the cell claim the width it needs. */
.data-table td.row-actions { margin: 0; flex-wrap: nowrap; white-space: nowrap; width: 1%; }

/* --- Backup & Recovery: status facts, notices and the passphrase pair --------------------
   The passphrase fields were misaligned because .grid-2 stretches its cells to equal height
   and `.grid-2 > .field label { flex: 1 0 auto }` then grows the label of whichever field has
   less below it -- so Confirm's label swelled to fill the gap left by the other field's hint,
   pushing its input 50px down. These use their own layout rather than .grid-2, so the shared
   rule that other screens rely on is left alone. */

.badge-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }

/* Running text stays readable instead of stretching the full panel width. */
.measure { max-width: 68ch; }

.fact-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 32px;
  margin: 14px 0 16px;
}
.fact { min-width: 8rem; }
.fact-key {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 2px;
}
.fact-value { font-size: var(--fs-base); font-variant-numeric: tabular-nums; }
.fact-value .hint { margin-top: 1px; }

.notice-block {
  border-left: 3px solid var(--warn);
  background: var(--bg);
  padding: 10px 14px;
  margin: 0 0 16px;
  max-width: 68ch;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.notice-block p { margin: 0 0 6px; font-size: var(--fs-sm); }
.notice-block ul { margin: 0; padding-left: 18px; }
.notice-block li { font-size: var(--fs-sm); margin-bottom: 2px; }
.notice-block code { font-size: var(--fs-xs); }

.passphrase-form { margin-top: 4px; }
/* Fixed columns, top-aligned: both inputs sit on the same line whatever is written below them. */
.passphrase-pair {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 15rem);
  align-items: start;
  gap: 14px;
  margin-bottom: 6px;
}
.passphrase-pair .field { margin-bottom: 0; }
.passphrase-pair .field label { flex: 0 0 auto; }

@media (max-width: 768px) {
  .passphrase-pair { grid-template-columns: 1fr; }
}

/* The count on the Tasks button. Sits inside the button so it moves with it, and is quiet
   enough to be ignored when there is nothing urgent. */
.nav-count {
  display: inline-block;
  min-width: 1.15rem;
  margin-left: 6px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.35;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Read-only mode. Deliberately loud and at the very top of the page: someone about to lose work
   to a refused save should see it before they try, not after. */
.maintenance-banner {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--warn);
  color: #1b1205;
  padding: 8px 16px;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-align: center;
}

/* ==========================================================================================
   MODULE SHELL — a module's sections in a left sidebar, its content to the right.
   Used where a module has enough sections to be worth the width; modules with two or three
   keep their tab strip. Top navigation stays for modules, tabs stay for closely related views
   within a section, so the three levels never appear as three stacked rows.
   ========================================================================================== */

.module-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  gap: var(--sp-6);
  align-items: start;
}
.module-shell.is-collapsed { grid-template-columns: var(--sidebar-rail-w) minmax(0, 1fr); }

.module-sidebar {
  position: sticky;
  top: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-3) 0 var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  max-height: calc(100vh - var(--sp-8));
  overflow-y: auto;
}

.sidebar-title {
  font-size: var(--fs-title);
  font-weight: 700;
  padding: 0 var(--sp-4) var(--sp-2);
  margin: 0;
}
.sidebar-search { padding: 0 var(--sp-3) var(--sp-2); }
.sidebar-search input { width: 100%; font-size: var(--fs-base); }

.sidebar-group {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: var(--sp-3) var(--sp-4) var(--sp-1);
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  border: 0;
  background: none;
  text-align: left;
  font-size: var(--fs-base);
  color: var(--text);
  padding: var(--sp-2) var(--sp-4);
  cursor: pointer;
  border-left: 2px solid transparent;
}
.sidebar-link:hover { background: var(--surface-hover); }
.sidebar-link.active {
  background: var(--accent-soft);
  color: var(--primary);
  font-weight: 600;
  border-left-color: var(--primary);
}
.sidebar-link svg { flex: 0 0 auto; width: 16px; height: 16px; }
.sidebar-link .label { flex: 1 1 auto; min-width: 0; }

.sidebar-foot {
  margin-top: auto;
  padding: var(--sp-2) var(--sp-3) 0;
  border-top: 1px solid var(--border);
}
.sidebar-collapse {
  display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; border: 0; background: none; cursor: pointer;
  font-size: var(--fs-sm); color: var(--text-muted); padding: var(--sp-2);
}
.sidebar-collapse:hover { color: var(--text); }

/* Collapsed rail: icons only, labels announced by title/aria instead. */
.is-collapsed .module-sidebar { padding-left: 0; padding-right: 0; }
.is-collapsed .sidebar-title,
.is-collapsed .sidebar-search,
.is-collapsed .sidebar-group,
.is-collapsed .sidebar-link .label,
.is-collapsed .sidebar-collapse span { display: none; }
.is-collapsed .sidebar-link { justify-content: center; padding: var(--sp-3) 0; }
.is-collapsed .sidebar-collapse { justify-content: center; }

/* ==========================================================================================
   PAGE HEADER — title, one line of description, actions on the right. One component so the
   same three things do not get laid out five different ways.
   ========================================================================================== */

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-5);
}
.page-head-text { min-width: 0; }
.page-head h2 { margin: 0 0 2px; font-size: var(--fs-display); font-weight: 700; }
.page-head .page-desc { margin: 0; color: var(--text-muted); font-size: var(--fs-base); }
.page-head-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.dash-welcome { text-align: right; margin-bottom: var(--sp-2); }
/* One line of "what moved" under an approval row's identity block. Wraps rather than pushing
   the table wide, and stays visually secondary to the identity above it. */
.change-line { margin-top: var(--sp-1); line-height: 1.5; }

/* Searchable single-value picker, for a list too long to scroll as a <select>. Sits inside the
   master-data form modal, so the results panel is absolutely positioned rather than a second
   overlay -- the app has one modal panel and nesting them would clear the form underneath. */
.lookup { position: relative; }
/* An <input> does not stretch inside a block container the way a flex child does, so without
   this the picker sat at its default ~178px while every other field in the row was full width.
   Room on the right for the arrow, so long text never runs under it. */
.lookup-input { width: 100%; padding-right: 30px; }
.lookup-toggle {
  position: absolute; right: 1px; top: 1px; bottom: 1px; width: 28px;
  display: flex; align-items: center; justify-content: center;
  border: none; background: none; color: var(--text-muted); cursor: pointer;
  border-radius: 0 6px 6px 0;
}
.lookup-toggle:hover { color: var(--text); }
.lookup-results {
  position: absolute; z-index: 10; left: 0; right: 0; top: 100%; margin-top: 2px;
  max-height: 260px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 6px; box-shadow: var(--elev-2);
}
.lookup-results[hidden] { display: none; }
.lookup-item {
  display: block; width: 100%; text-align: left; border: none; background: none;
  padding: var(--sp-2) var(--sp-3); font-size: var(--fs-base); cursor: pointer; color: var(--text);
}
.lookup-item:hover, .lookup-item.active { background: var(--surface-hover); }
.lookup-note { padding: var(--sp-2) var(--sp-3); }

/* ==========================================================================================
   OVERFLOW MENU — one visible action per row, the rest behind a single control, so a table
   does not carry four buttons on every line.
   ========================================================================================== */

.overflow { position: relative; display: inline-block; }
.overflow-trigger {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius);
  cursor: pointer;
  padding: 3px 8px;
  color: var(--text-muted);
  line-height: 1.1;
}
.overflow-trigger:hover { background: var(--surface-hover); color: var(--text); }
.overflow-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 40;
  min-width: 190px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--elev-2);
  padding: var(--sp-1);
  display: flex;
  flex-direction: column;
}
/* The browser's own `display: none` for [hidden] loses to the class rule above, so every menu
   rendered open while its DOM state said closed. Asserting on the attribute passed; the page was
   visibly wrong. */
.overflow-menu[hidden] { display: none; }
.overflow-menu button {
  border: 0; background: none; text-align: left; cursor: pointer;
  font-size: var(--fs-base); color: var(--text);
  padding: var(--sp-2) var(--sp-3); border-radius: 4px; width: 100%;
}
.overflow-menu button:hover { background: var(--surface-hover); }
.overflow-menu button.destructive { color: var(--danger); }
.overflow-menu button.destructive:hover { background: #fdeceb; }
.overflow-sep { height: 1px; background: var(--border); margin: var(--sp-1) 0; }

/* Tablet: the sidebar becomes a rail so content keeps its width. */
@media (max-width: 1024px) {
  .module-shell { grid-template-columns: var(--sidebar-rail-w) minmax(0, 1fr); gap: var(--sp-4); }
  .module-shell .sidebar-title,
  .module-shell .sidebar-search,
  .module-shell .sidebar-group,
  .module-shell .sidebar-link .label,
  .module-shell .sidebar-collapse span { display: none; }
  .module-shell .sidebar-link { justify-content: center; padding: var(--sp-3) 0; }
}

/* Mobile: out of the flow entirely, opened as a drawer from a button above the content. */
@media (max-width: 768px) {
  .module-shell { display: block; }
  /* Off-canvas via transform so opening the drawer composites instead of relaying out. */
  .module-sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 90;
    width: min(84vw, var(--sidebar-w));
    max-height: none; border-radius: 0;
    transform: translateX(-100%);
    transition: transform 0.18s ease; box-shadow: var(--elev-2);
  }
  .module-shell.drawer-open .module-sidebar { transform: none; }
  .module-shell .sidebar-title,
  .module-shell .sidebar-search,
  .module-shell .sidebar-group,
  .module-shell .sidebar-link .label { display: revert; }
  .module-shell .sidebar-link { justify-content: flex-start; padding: var(--sp-3) var(--sp-4); }
  .sidebar-scrim {
    position: fixed; inset: 0; z-index: 85; background: rgba(16, 24, 40, 0.4);
  }
  .sidebar-toggle { display: inline-flex !important; }
}
.sidebar-toggle { display: none; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-4); }
/* Sized here rather than only inside the mobile block below. The control is hidden at desktop,
   so this changes nothing today -- but a size that exists only inside a media query means the
   element falls back to the browser's own button default (13.3333px) anywhere the query does
   not match, which is the drift the type tokens exist to stop. The mobile block still owns the
   layout; this owns the type. */
.sidebar-toggle-module { font-weight: 700; font-size: var(--fs-base); }
.sidebar-toggle-section { font-size: var(--fs-sm); color: var(--text-muted); }

@media (prefers-reduced-motion: reduce) {
  .module-sidebar { transition: none; }
}

/* ==========================================================================================
   MOBILE PRESENTATION (under 768px)
   Presentation only. Every rule below re-arranges markup the desktop already renders -- there
   is no mobile-only navigation list, no mobile-only table, no second source of data. Desktop
   and tablet widths are untouched: everything here is inside a max-width query, and the two
   elements added to the shell (the hamburger and the identity initials) are display:none
   above the breakpoint.
   ========================================================================================== */

/* Hidden on desktop; the media query below reveals them. */
.nav-toggle { display: none; }
.brand-short { display: none; }
.user-initials { display: none; }
.user-menu-identity { display: none; }
.nav-scrim { display: none; }

@media (max-width: 768px) {
  /* ---- header ------------------------------------------------------------------------ */
  .topbar { padding: 8px 12px; gap: 0; position: sticky; top: 0; z-index: 70; }
  /* .topbar is a stacking context, so .main-nav's z-index is resolved inside it -- the drawer
     cannot rise above the scrim on its own. Lift the whole bar over the scrim while the drawer
     is open, or the scrim paints over the menu and swallows every tap meant for it. */
  body.nav-open .topbar { z-index: 80; }
  .topbar-row { flex-wrap: nowrap; gap: 8px; }

  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center; position: relative;
    width: 44px; height: 44px; flex: 0 0 44px;          /* 44px touch target */
    border: none; background: none; color: var(--text); border-radius: 8px; cursor: pointer;
  }
  .nav-toggle:hover { background: var(--surface-hover); }
  /* The Tasks count, mirrored onto the hamburger because the nav carrying it is off-canvas. */
  .nav-toggle-count {
    position: absolute; top: 4px; right: 2px; min-width: 17px; padding: 0 4px;
    border-radius: 999px; background: var(--danger); color: #fff;
    font-size: var(--fs-xs); font-weight: 600; line-height: 17px; text-align: center;
    font-variant-numeric: tabular-nums;
  }
  .nav-toggle-count[hidden] { display: none; }

  /* The full product name does not fit beside a menu button and an avatar; the short form is
     the same identity, and the long one stays in the document for assistive tech. */
  .brand { flex: 1 1 auto; min-width: 0; gap: 8px; }
  .brand .brand-subtitle {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
  .brand-short {
    display: inline-block; font-weight: 700; font-size: var(--fs-title);
    letter-spacing: 0.02em; color: var(--text);
  }

  /* Name + role become initials; the full text moves into the menu they open. */
  .user-box { gap: 4px; flex: 0 0 auto; }
  .user-info { display: none; }
  .user-info-btn { padding: 0; }
  .user-initials {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: 999px;
    background: var(--primary); color: #fff; font-size: var(--fs-sm); font-weight: 700;
  }
  .user-menu-identity {
    display: block; padding: var(--sp-3); border-bottom: 1px solid var(--border);
    font-size: var(--fs-sm); color: var(--text-muted); line-height: 1.4;
  }
  .user-menu-identity b { color: var(--text); }
  .user-menu-dropdown { min-width: 210px; }
  .user-menu-item { min-height: 44px; }
  /* Log Out lives in the menu here; the bar's copy is the desktop one. Same handler. */
  #logoutBtn { display: none; }

  /* ---- navigation drawer -------------------------------------------------------------- */
  .main-nav {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 80;
    width: min(82vw, 300px);
    flex-direction: column; flex-wrap: nowrap; gap: 2px;
    padding: var(--sp-4) var(--sp-3); overflow-y: auto;
    background: var(--surface); box-shadow: var(--elev-2);
    transform: translateX(-100%); transition: transform 0.18s ease;
  }
  body.nav-open .main-nav { transform: none; }
  .main-nav button {
    width: 100%; text-align: left; min-height: 44px;
    padding: var(--sp-3) var(--sp-4); font-size: var(--fs-control); border-radius: 8px;
    display: flex; align-items: center;
  }
  .main-nav button .nav-count { margin-left: auto; }
  .nav-scrim { display: block; position: fixed; inset: 0; z-index: 75; background: rgba(16, 24, 40, 0.4); }
  .nav-scrim[hidden] { display: none; }

  /* ---- module section selector -------------------------------------------------------- */
  /* The same control that already opened the section drawer, now reading as a selector that
     names the module and the section you are in. */
  .sidebar-toggle {
    display: flex !important; align-items: center; justify-content: space-between;
    width: 100%; min-height: 52px; gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-4); margin-bottom: var(--sp-4);
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    color: var(--text); cursor: pointer; text-align: left;
  }
  .sidebar-toggle-text { display: flex; flex-direction: column; min-width: 0; }
  .sidebar-toggle-section {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* ---- page furniture ----------------------------------------------------------------- */
  .page-head { flex-direction: column; align-items: stretch; gap: var(--sp-3); }
  .page-head-actions { flex-wrap: wrap; }
  .page-head-actions .btn { min-height: 44px; }
  /* Toolbars stack rather than squeezing every control onto one line. */
  .list-toolbar, .panel-header-row { flex-wrap: wrap; gap: var(--sp-2); }
  /* The header row is a title/control pair on desktop and uses space-between; where the title cell
     is empty that leaves the control marooned on the right. */
  .panel-header-row { justify-content: flex-start; }
  .panel-header-row > div:empty { display: none; }
  .list-toolbar > input[type="text"],
  .list-toolbar > input[type="search"] { flex: 1 1 100%; max-width: none !important; }
  .list-toolbar > span { margin-left: 0 !important; }
  .action-row .btn { min-height: 44px; }

  /* ---- record cards ------------------------------------------------------------------- */
  /* Opt-in per table (.table-cards). Tables not marked keep the existing horizontal scroll,
     so nothing changes for them. Every column stays in the DOM and every value is shown --
     the header row is what moves, into each cell's own label. */
  .data-table.table-cards { display: block; overflow: visible; white-space: normal; }
  .data-table.table-cards thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
  }
  .data-table.table-cards tbody { display: block; }
  .data-table.table-cards tr {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2) var(--sp-3);
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: var(--sp-4); margin-bottom: var(--sp-3); background: var(--surface);
  }
  .data-table.table-cards tr:hover { background: var(--surface); }
  .data-table.table-cards td {
    display: flex; flex-direction: column; gap: 2px;
    border: none; padding: 0; min-width: 0; white-space: normal; overflow-wrap: anywhere;
  }
  .data-table.table-cards td::before {
    content: attr(data-label);
    font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.03em;
    text-transform: uppercase; color: var(--text-muted);
  }
  /* Name and username read as the card's heading, matching the order of the first two
     columns; their labels are already carried by the heading itself. */
  .data-table.table-cards td.cell-title {
    grid-column: 1 / -1; font-size: var(--fs-title); font-weight: 700;
  }
  .data-table.table-cards td.cell-sub {
    grid-column: 1 / -1; margin-top: -6px; color: var(--text-muted);
  }
  /* A badge is inline-block on desktop; inside the card's flex column it would stretch to the
     full track, so pin it to its own width. */
  .data-table.table-cards td .badge { align-self: flex-start; }
  .data-table.table-cards td.cell-title::before,
  .data-table.table-cards td.cell-sub::before { content: none; }
  /* Actions span the card and sit at its foot, Edit still first and the overflow beside it. */
  .data-table.table-cards td.cell-actions {
    grid-column: 1 / -1; flex-direction: row; align-items: center; gap: var(--sp-2);
    width: auto; text-align: left !important;
    padding-top: var(--sp-3); margin-top: var(--sp-1); border-top: 1px solid var(--border);
  }
  .data-table.table-cards td.cell-actions::before { content: none; }
  .data-table.table-cards td.cell-actions .btn { min-height: 44px; }
  /* The overflow trigger is a compact glyph button on desktop; on touch it needs a real
     target. Only its box grows -- the icon, menu and actions inside are unchanged. */
  .data-table.table-cards td.cell-actions .overflow-trigger {
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  /* A row that is one full-width cell is a week grouping, an empty state or a total -- not a
     record. It keeps its own line rather than becoming a card. */
  .data-table.table-cards tr.card-full-row,
  .data-table.table-cards tr.empty-row {
    display: block; border: none; background: none; padding: 0 var(--sp-1);
    margin: 0 0 var(--sp-2);
  }
  .data-table.table-cards tr.card-full-row td,
  .data-table.table-cards tr.empty-row td { display: block; text-align: left; }
  .data-table.table-cards tr.card-full-row td::before,
  .data-table.table-cards tr.empty-row td::before { content: none; }
  /* Week group headings keep the weight they have on desktop. */
  .data-table.table-cards tr.week-group-header { margin-top: var(--sp-4); }
  .data-table.table-cards tr.week-group-header td { padding: var(--sp-2) 0; border-bottom: 1px solid var(--border); }
  /* A cell that spans columns inside a card (a note, a total) takes the full width. */
  .data-table.table-cards td.cell-span { grid-column: 1 / -1; }
  .data-table.table-cards td.cell-span::before { content: none; }
  /* Totals rows sit under the cards, not as one. */
  .data-table.table-cards tfoot { display: block; }
  .data-table.table-cards tfoot tr { display: flex; justify-content: space-between; gap: var(--sp-3);
    border: none; background: none; padding: var(--sp-3) var(--sp-1); margin: 0; font-weight: 600; }
  .data-table.table-cards tfoot td { display: block; }
  .data-table.table-cards tfoot td::before { content: none; }
  /* Rows that navigate keep saying so. */
  .data-table.table-cards tbody tr[data-id],
  .data-table.table-cards tbody tr[data-hash] { cursor: pointer; }
  /* Numeric columns lose their right alignment inside a card, where the label sits above. */
  .data-table.table-cards td.num { text-align: left; }

  /* ---- dialogs and the record modal --------------------------------------------------- */
  /* Full-height sheets rather than a small centred box, so a long form is usable. Still the
     same dialog element and the same close/confirm behaviour. */
  .overlay { padding: 0; align-items: flex-end; }
  .detail-panel, .dialog-panel {
    max-width: none; width: 100%; margin: 0; max-height: 92vh;
    border-radius: var(--radius) var(--radius) 0 0;
  }
  .form-actions, .dialog-actions { flex-wrap: wrap; }
  .form-actions .btn, .dialog-actions .btn { flex: 1 1 auto; min-height: 44px; }
}

/* Keyboard focus must stay visible on every control the mobile layout restyles. */
.nav-toggle:focus-visible, .sidebar-toggle:focus-visible,
.user-info-btn:focus-visible, .user-menu-item:focus-visible {
  outline: 2px solid var(--primary); outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .main-nav { transition: none; }
}

/* A matrix that is long in both directions: the permission names run off the top, the role
   columns off the side, and a checkbox in the middle belongs to neither once you have scrolled.
   Both headings are pinned so a tick always has a row and a column you can read.

   The scrolling has to happen in this container rather than the page, or `position: sticky`
   has nothing to stick within -- the same reason the frozen list headers needed a height cap. */
.matrix-scroll { max-height: 70vh; overflow: auto; }

.matrix-frozen thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
}

/* The first column, pinned sideways. Row headers and the cells beneath them both, so the
   permission name travels with its ticks. */
.matrix-frozen thead th:first-child,
.matrix-frozen tbody td:first-child {
  position: sticky;
  left: 0;
  background: var(--surface);
}

/* Where the two meet. Above both, or the corner cell scrolls under one of them. */
.matrix-frozen thead th:first-child { z-index: 3; }

/* A category divider spans the whole width, so pinning its single cell to the left would leave
   the band sliding out from under its own label. */
.matrix-frozen tbody tr td[colspan] { position: static; }

/* Sticky cells sit above the rows they scroll over, so they need their own edges: without a
   border of their own the table's ruling scrolls away and the pinned column floats. */
.matrix-frozen tbody td:first-child { border-right: 1px solid var(--border); }
.matrix-frozen thead th { border-bottom: 1px solid var(--border); }

/* The idle sign-out countdown. Fixed rather than in the layout so it cannot reflow a form
   somebody is mid-way through filling, and above the dialog layer so it is still visible if a
   dialog is open when the clock runs down. */
.idle-warning {
  position: fixed; top: 12px; left: 50%; transform: translateX(-50%); z-index: 1000;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px; border-radius: var(--radius);
  background: #fef3c7; color: var(--warn); border: 1px solid var(--warn);
  font-size: var(--fs-base); font-weight: 600;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
  max-width: calc(100vw - 24px);
}
.idle-warning .btn { flex: none; }

/* Report 09: a flagged product carries a warn rail so it can be found in a long table. */
.items-table tr.recon-flagged td:first-child { box-shadow: inset 3px 0 0 var(--warn); }

/* Quantity boxes on transfer receiving sit under right-aligned headings, so they align right too
   and fill their column instead of a fixed width that left them adrift under the heading. */
.items-table td.num input.trf-qty { width: 100%; max-width: 120px; text-align: right; margin-left: auto; display: block; }

/* Report 05 discount transactions: each photo cell's controls sit in one row of equal-height
   buttons, and Remove keeps to its own narrow column instead of stretching. */
.spwd-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.spwd-actions .btn { white-space: nowrap; }
.spwd-actions .badge { align-self: center; }
.data-table td.spwd-remove-cell { width: 1%; white-space: nowrap; }

/* Daily Review: the store whose lines are shown below the table. */
.data-table tr.is-selected td { background: var(--accent-soft); }

/* --- Settings forms laid out in sections (Audit Controls) ---------------------------------
   Not .grid-3: that grid stretches every cell to the tallest and grows each label to fill, so a
   field with anything written under its input pushes its neighbours' inputs down. Here fields are
   top-aligned in fixed-width columns with single-line labels, and the explanation for a section
   sits above its fields, so every input in a row lands on the same line. */
.settings-section { padding: var(--sp-5) 0; border-top: 1px solid var(--border); }
.settings-section:first-child { padding-top: 0; border-top: 0; }
.settings-section > .hint { max-width: 72ch; margin: 0 0 var(--sp-4); }
.settings-fields {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 15rem));
  gap: var(--sp-4) var(--sp-5);
  align-items: start;
}
.settings-fields .field label { flex: 0 0 auto; }
.settings-fields .field--wide { grid-column: auto; }
.settings-notes { margin: var(--sp-3) 0 0; padding-left: 18px; max-width: 72ch; }
.settings-notes li { font-size: var(--fs-sm); color: var(--text-muted); margin-bottom: 2px; }
.settings-actions { margin-top: var(--sp-2); }
.deadline-cell { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.deadline-input { width: 5.5rem; }
@media (max-width: 640px) {
  .settings-fields { grid-template-columns: 1fr; }
}

/* Finding page: several possible next steps are separated by a rule, not boxed separately. */
.finding-actions .field { max-width: 40rem; }
.finding-actions-rule { border: 0; border-top: 1px solid var(--border); margin: var(--sp-4) 0; }

/* Daily Sales Monitoring and the 2pm/6pm report comparison */
.delta-up { color: var(--success); font-weight: 600; }
.delta-down { color: var(--danger); font-weight: 600; }
.snap-table { table-layout: fixed; width: 100%; }
.snap-table td .hint { display: block; font-size: var(--fs-xs); white-space: nowrap; }
/* Daily Sales Monitoring shows every store in full: no box of its own to scroll inside -- the
   section grows to fit and the page scrolls. Figures wrap rather than push the table wider. */
.snap-full { overflow: visible; }
.snap-full .snap-table td { overflow-wrap: anywhere; }
.snap-full .snap-table td .hint { white-space: normal; }
.snap-table th { white-space: normal; }
.snap-table tr.snap-subtotal td { background: var(--bg); }
.snap-table tr.snap-total td { background: var(--bg); border-top: 2px solid var(--border); }
.snap-slot + .snap-slot { margin-top: var(--sp-4); }
.snap-table .snap-note { display: block; font-size: var(--fs-xs); }
/* Title and store count read as one line: sat on the same baseline, not centred on each other. */
.snap-head { align-items: baseline; margin-bottom: var(--sp-2); }
.snap-head .snap-count { margin-left: auto; }
/* Beats the phone rule that pins every toolbar span left: the count belongs at the right edge
   here on every screen, as it is on desktop. */
.list-toolbar.snap-head > .snap-count { margin-left: auto !important; }
/* On a phone the page keeps its table (no cards); each section scrolls sideways on its own,
   with the store column pinned so a row never loses the store it belongs to. No height is
   set, so a section still never scrolls up and down inside itself. */
@media (max-width: 768px) {
  .snap-full { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .snap-full .snap-table { display: table; width: auto; min-width: 680px; table-layout: fixed;
                           white-space: normal; overflow: visible; }
  .snap-full .snap-table th:first-child, .snap-full .snap-table td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--surface);
    box-shadow: inset -1px 0 0 var(--border); }
  .snap-full .snap-table tr.snap-subtotal td:first-child,
  .snap-full .snap-table tr.snap-total td:first-child { background: var(--bg); }
}

/* Daily report fields start at the top of their row. The shared grid rule grows every label so
   wrapped labels line up at the bottom -- but a field with a hint or a photo line under it then
   swells its neighbour's label and pushes that value down beside the hint (Report 04's Total Net
   Sales floated mid-row next to the gross figure's photo line). */
.dr-fields { align-items: start; }
.grid-2.dr-fields > .field label, .grid-3.dr-fields > .field label { flex: 0 0 auto; }

/* A field's own photos: each one listed, and the picker labelled for what it will do. */
.dr-photo-chips { display: flex; flex-wrap: wrap; }
.dr-photo-pick { display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-sm);
                 font-weight: 600; color: var(--text-muted); }

/* Undelivered Items */
.und-head { align-items: baseline; margin-bottom: var(--sp-2); }
.list-toolbar.und-head > .und-count { margin-left: auto !important; }
.badge-closed_short { background: #fef3c7; color: var(--warn); }
.und-cards { grid-template-columns: repeat(5, minmax(0, 1fr)); }
#undBody td.nowrap, #undBody td.nowrap .btn { white-space: nowrap; }
#undBody .und-uom { display: block; font-size: var(--fs-xs); }
@media (max-width: 768px) {
  .und-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .und-cards > :last-child { grid-column: span 2; }
}

/* Weekly Final Approval -- one block per store, decided on its own */
.badge-final_rejected { background: #fde2e2; color: var(--danger); }
.wk-brand { margin-top: var(--sp-4); }
.wk-store { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); margin-bottom: var(--sp-2); }
.wk-store-head { display: grid; grid-template-columns: minmax(0, 1fr) 230px 400px; gap: var(--sp-3); align-items: center; padding: var(--sp-2) var(--sp-3); }
.wk-store-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wk-store-id .hint { overflow-wrap: anywhere; }
.wk-store-figs { display: flex; align-items: baseline; justify-content: flex-end; gap: var(--sp-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.wk-rejected-amt { color: var(--text-faint); text-decoration: line-through; }
.wk-store-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px; }
.wk-store-actions .btn { white-space: nowrap; }
.wk-outcome { flex: 1 1 200px; text-align: right; }
.wk-decided { background: var(--surface-sunken); }
.wk-store-body { border-top: 1px solid var(--border); padding: var(--sp-2) var(--sp-3) var(--sp-3); }
.wk-suppliers { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--sp-2); }
.wk-chip { font-size: var(--fs-sm); background: var(--surface-sunken); border: 1px solid var(--border); border-radius: 999px; padding: 2px 10px; }
.wk-sub { display: block; font-size: var(--fs-xs); }
.data-table tr.wk-total-row td { border-top: 2px solid var(--border-strong); background: var(--surface-sunken); }
.wk-matrix td, .wk-matrix th { white-space: nowrap; }
@media (max-width: 1180px) {
  .wk-store-head { grid-template-columns: minmax(0, 1fr) 200px; }
  .wk-store-actions { grid-column: 1 / -1; }
}
@media (max-width: 768px) {
  .wk-store-head { grid-template-columns: minmax(0, 1fr) auto; }
  .wk-store-actions { grid-column: 1 / -1; justify-content: flex-start; }
}
.wk-store, .wk-store-body { min-width: 0; }
.wk-lines .wk-qty-input { width: 76px; }
.wk-lines .wk-reason-input { min-width: 120px; }

/* Supplier Performance */
.sp-table td .sp-sub, .sp-detail .sp-sub { display: block; font-size: var(--fs-xs); }
.sp-delta { display: block; font-size: var(--fs-xs); color: var(--text-faint); white-space: nowrap; }
.sp-good { color: var(--success); }
.sp-bad { color: var(--danger); }
.data-table tr.sp-detail > td { background: var(--surface-sunken); padding: var(--sp-3); }
.sp-detail-body { display: flex; flex-direction: column; gap: var(--sp-2); }
.sp-flags { display: flex; flex-wrap: wrap; gap: 4px; }
.sp-nowrap { white-space: nowrap; }

/* Daily report tables: "View full table" opens the whole table in one screen, for a screenshot */
.dr-table-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.dr-full-overlay { z-index: 1000; padding: 12px; }
.dr-full-panel { background: var(--surface); border-radius: var(--radius); padding: var(--sp-3); width: max-content; max-width: 100%; overflow-x: auto; box-shadow: var(--elev-2); }
.dr-full-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-2); }
.dr-full-panel table { width: auto; font-size: var(--fs-sm); }
.dr-full-panel th, .dr-full-panel td { padding: 4px 8px; }
.dr-full-panel td { white-space: nowrap; }
.dr-full-panel tr.dr-why-row td { white-space: normal; }
@media print { .dr-full-close { display: none; } }
.table-full-bar { display: flex; justify-content: flex-end; margin-bottom: 6px; }
.table-full-bar[hidden] { display: none; }
.assort-store-row { display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap; }
.assort-store-row .field { margin-bottom: 0; flex: 1 1 260px; }

/* Application Settings: every tab on one line; the strip scrolls sideways where it cannot fit */
#globalTabs { flex-wrap: nowrap; overflow-x: auto; }
#globalTabs button { flex: none; white-space: nowrap; }
/* Settings as a list: explanation left, control right, so every row lines up */
.settings-list { display: flex; flex-direction: column; gap: var(--sp-4); margin: var(--sp-3) 0 var(--sp-4); }
.settings-group .subsection-title { margin-bottom: 0; }
.settings-row { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: var(--sp-6); align-items: start;
  padding: var(--sp-3) 0; border-bottom: 1px solid var(--border); }
.settings-group .settings-row:last-child { border-bottom: none; }
.settings-text label { display: block; font-weight: 600; font-size: var(--fs-base); color: var(--text); margin-bottom: 2px; }
.settings-text .hint { margin: 0; max-width: 62ch; }
.settings-control select, .settings-control input:not([type="checkbox"]) { width: 100%; }
.settings-toggle { margin: 0; min-height: var(--control-h); align-items: center; }
@media (max-width: 768px) {
  .settings-row { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
}
/* Daily Sales Monitoring: the ALL STORES totals stand out from the stores above them */
.snap-table tr.snap-total td { font-weight: 700; }
.snap-table tr.snap-total td .hint { font-weight: 400; }
