/* ═══════════════════════════════════════════════════════════════════════════
   Gatuka admin portal — layer over Organic.
   ═══════════════════════════════════════════════════════════════════════════

   Rules this file obeys, from design.md §2 / §7 and D47:

   1. No raw colour. Every colour is a `var(--color-*)` from the vendored
      styles.css, which is the only file allowed to hold a hex.
   2. Component classes only. The prototype writes its geometry inline; the
      measurements below are transcribed from it verbatim (rail 252px, item
      13.5px/600, group label 10.5px/.1em, …) so the built admin matches the
      approved screens rather than approximating them.
   3. Organic's own `.btn` / `.card` / `.table` / `.tag` / `.seg` / `.dialog`
      do the work wherever they fit. What is here is the shell and the few
      admin-density patterns Organic has no class for.

   Source screens: mockup/AdminNav.dc.html, mockup/AdminDashboard.dc.html.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --rail-width: 252px;
  /* Urgent / negative — overdue, short, a failed save, a negative figure, a
     destructive button. Organic gives brand and urgency one hue (terracotta), so
     here it is the accent; a palette that splits them (TailAdmin's red) only has
     to redefine these. Brand uses stay on `--color-accent`. */
  --color-alert: var(--color-accent);
  --color-alert-100: var(--color-accent-100);
  --color-alert-200: var(--color-accent-200);
  --color-alert-700: var(--color-accent-700);
  --color-alert-800: var(--color-accent-800);
  --page-gutter: 36px;
  --admin-font-size: 14px;
}

/* ── ground ──────────────────────────────────────────────────────────────── */

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

body.admin {
  margin: 0;
  min-height: 100vh;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--admin-font-size);
}

/* Bangla needs its own leading — never reuse the English value (design.md §2.2). */
html[lang='bn'] body { line-height: 1.9; }

/* Every figure in the admin is tabular: columns line up, digits don't jitter. */
.num, .table td, .table th, .money, .stat-value, .kpi-value {
  font-variant-numeric: tabular-nums;
}

/* Money is Arabic-numeral in both locales and never wraps mid-amount. */
.money { white-space: nowrap; }

.shell { display: flex; min-height: 100vh; }
.shell-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* Mobile chrome. Hidden on the desktop layout, where the rail is always present. */
.topbar { display: none; }
.nav-backdrop { display: none; }

/* Anything that can outgrow a narrow screen scrolls inside itself rather than
   pushing the page sideways — Organic's `.seg` clips with overflow:hidden, so
   without this the last period option is unreachable at 360px. */
.scroll-x { overflow-x: auto; max-width: 100%; scrollbar-width: thin; }
.scroll-x > .seg { flex: none; }

/* ── left rail ───────────────────────────────────────────────────────────── */

.rail {
  width: var(--rail-width);
  flex: none;
  /* Pinned to the viewport, not to the page. `min-height` let the rail stretch
     to the *content* height as a flex child of `.shell`, so on a long list it
     grew past the fold, `.rail-scroll` never needed to scroll, and the footer —
     the language toggle and sign-out — sat at the bottom of a rail taller than
     the screen. A fixed height is what makes `.rail-scroll`'s `flex: 1` mean
     anything. `align-self` stops the flex stretch reintroducing it. */
  height: 100vh;
  height: 100dvh;
  position: sticky;
  top: 0;
  align-self: flex-start;
  background: var(--color-surface);
  display: flex;
  flex-direction: column;
  font-family: var(--font-body);
}

.rail-brand {
  padding: 26px 22px 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: relative;
}
/* Only the drawer needs a close affordance; the static rail has nothing to close. */
.rail-close { display: none; position: absolute; top: 20px; right: 16px; }
.rail-brand-row { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.rail-mark {
  width: 26px; height: 26px; flex: none;
  border-radius: 50%;
  background: var(--color-accent);
}
.rail-wordmark {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 20px;
  color: var(--color-text);
}
.rail-sublabel {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-neutral-600);
  padding-left: 36px;
}

.rail-scroll {
  flex: 1;
  overflow: auto;
  padding: 10px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rail-group-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 14px 12px 6px;
  cursor: pointer;
  background: none;
  border: 0;
  width: 100%;
  text-align: inherit;
  font: inherit;
  color: inherit;
}
.rail-group-label {
  flex: 1;
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-neutral-500);
}
.rail-group-chevron {
  color: var(--color-neutral-500);
  transition: transform 0.15s ease-out;
}
.rail-group-header[aria-expanded='true'] .rail-group-chevron { transform: rotate(180deg); }

.rail-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 999px;
  text-decoration: none;
  color: var(--color-text);
  background: transparent;
}
.rail-item:hover { background: var(--color-accent-100); }
.rail-item[aria-current='page'] {
  background: var(--color-accent-100);
  color: var(--color-accent-800);
}
.rail-item-label { font: 600 13.5px var(--font-body); flex: 1; }
.rail-badge {
  font: 700 10.5px var(--font-body);
  background: var(--color-accent);
  color: var(--color-bg);
  border-radius: 999px;
  padding: 2px 8px;
}

.rail-footer {
  padding: 16px 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Language toggle — a full-width `.seg` whose options are submit buttons, so
   the choice persists server-side to the user profile rather than to
   localStorage the way the prototype does (D48). */
.lang-toggle { width: 100%; }
.lang-toggle button {
  flex: 1;
  text-align: center;
  padding: 7px 0;
  cursor: pointer;
  font: 600 12px var(--font-body);
  background: transparent;
  color: var(--color-neutral-600);
  border: 0;
}
.lang-toggle button[aria-pressed='true'] {
  background: var(--color-accent);
  color: var(--color-bg);
}

.rail-user { display: flex; align-items: center; gap: 10px; }
.avatar {
  width: 36px; height: 36px; flex: none;
  border-radius: 50%;
  background: var(--color-accent-2-200);
  color: var(--color-accent-2-800);
  display: flex; align-items: center; justify-content: center;
  font: 600 13px var(--font-body);
}
.rail-user-name {
  font: 600 13px var(--font-body);
  color: var(--color-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rail-user-role { font: 400 11.5px var(--font-body); color: var(--color-neutral-600); }

/* ── page header ─────────────────────────────────────────────────────────── */

.page-header {
  padding: 28px var(--page-gutter) 8px;
  display: flex;
  align-items: center;
  gap: 16px;
}
.page-title {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 27px;
  line-height: 1.15;
  color: var(--color-text);
  margin: 0;
}
.page-subtitle {
  font: 400 14px var(--font-body);
  color: var(--color-neutral-700);
  margin: 3px 0 0;
}
.page-header-spacer { flex: 1; }

.pill-static {
  display: flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 18px;
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font: 600 13px var(--font-body);
  border: 0;
}
a.pill-static, button.pill-static { cursor: pointer; text-decoration: none; }

.page-body {
  flex: 1;
  overflow: auto;
  padding: 20px var(--page-gutter) var(--page-gutter);
  display: flex;
  flex-direction: column;
  gap: 26px;
}

/* A form that wraps a whole screen — tabs, panels, a save bar — sits between
   `.page-body` and the blocks it would otherwise space, and a form is a block
   with no gap. Carry the page rhythm through it so the screen reads the same
   whether or not its content happens to be one form. Only a bare form: one
   with a class or a style (a filter `.toolbar`, a `.filter-row`) lays itself out
   as a row, and this must never outrank that. */
.page-body > form:not([class], [style]) { display: flex; flex-direction: column; gap: 26px; }

/* The same one level down: `.card` is a flex column, but a bare form inside it is
   a block, so the card's gap never reaches the title, the fields and the action
   row the form holds. A lone button keeps its own width rather than stretching. */
.card > form:not([class], [style]) { display: flex; flex-direction: column; gap: 16px; }
:is(.page-body, .card) > form:not([class], [style]) > .btn { align-self: flex-start; }

/* Layout for anything Alpine shows and hides. `x-show` restores a shown element
   by *deleting* its inline `display`, so `style="display:flex;gap:16px"` comes
   back as a block and the gap silently stops applying — cards end up touching.
   A class survives the toggle; the inline style does not. */
.stack { display: flex; flex-direction: column; gap: 16px; }
.stack-panel { max-width: 980px; }
.inline-row { display: flex; align-items: center; gap: 10px; }
.inline-row-spread { justify-content: space-between; gap: 8px; }

/* ── toolbar: period segment + alert chips ───────────────────────────────── */

.toolbar { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.seg-link {
  padding: 8px 16px;
  cursor: pointer;
  white-space: nowrap;
  font: 600 12.5px var(--font-body);
  color: var(--color-text);
  background: transparent;
  text-decoration: none;
  border: 0;
}
.seg-link[aria-current='true'] { background: var(--color-accent); color: var(--color-bg); }

.chip-row { display: flex; gap: 10px; flex-wrap: wrap; }
.alert-chip {
  display: flex; align-items: center; gap: 6px;
  cursor: pointer; text-decoration: none;
  font: 600 12.5px var(--font-body);
  color: var(--color-alert-800);
  background: var(--color-alert-100);
  border-radius: 999px;
  padding: 7px 14px;
  border: 0;
}

/* ── stat cards ──────────────────────────────────────────────────────────── */

.stat-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
/* Column count is a class, never an inline `grid-template-columns`: an inline
   style outranks every media query below, which is how a five-card strip ended up
   running off the side of a 360px screen. Declared only at the widest breakpoint
   so the responsive rules further down stay in charge. */
@media (min-width: 1101px) {
  .stat-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
  .stat-grid.cols-5 { grid-template-columns: repeat(5, 1fr); }
}
.stat-label {
  font: 600 11px var(--font-body);
  letter-spacing: 0.02em;
  color: var(--color-neutral-700);
}
.stat-value {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 23px;
  line-height: 1;
  color: var(--color-text);
}
.stat-delta { font: 600 11.5px var(--font-body); }
/* Two hues only: sage = positive, terracotta = negative/urgent (design.md §7). */
.delta-up { color: var(--color-accent-2-700); }
.delta-down, .delta-flag { color: var(--color-alert-800); }
.delta-flat { color: var(--color-neutral-600); }

/* ── panels ──────────────────────────────────────────────────────────────── */

.panel-row { display: flex; gap: 16px; align-items: stretch; }
/* A flex item defaults to `min-width: auto`, which means it refuses to shrink
   below its own min-content width. The bar chart's min-content is its 25 gaps
   plus its labels, so the chart card computed wider than the row that held it
   and crushed the panel beside it — "Orders by status" came out 102px wide,
   one word per line. `min-width: 0` is what lets the declared 2:1 flex ratio
   actually hold; the chart then scrolls inside itself if it still needs to. */
.panel-row > * { min-width: 0; }
.panel-title {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 18px;
  color: var(--color-text);
}
.panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.legend { display: flex; gap: 16px; }
.legend-item { display: flex; align-items: center; gap: 6px; font: 500 11.5px var(--font-body); color: var(--color-neutral-700); }
.legend-dot { width: 9px; height: 9px; border-radius: 50%; }

.meter { height: 8px; border-radius: 999px; background: var(--color-neutral-200); overflow: hidden; }
/* Untouched is the neutral ramp by default, so a caller that never reaches for
   a modifier (most meters have no "done" concept) still paints a visible bar.
   `.meter-warn` / `.meter-done` are the purchase orders list's own three-state
   ramp (`mockup/AdminPurchaseOrder.dc.html`'s `barColor`) — complete is sage,
   started is terracotta. */
.meter > span { display: block; height: 100%; border-radius: 999px; background: var(--color-neutral-300); }
.meter > span.meter-warn { background: var(--color-alert); }
.meter > span.meter-done { background: var(--color-accent-2-600); }

.list-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--color-divider);
}
.list-row:last-child { border-bottom: 0; }
.list-rank { font: 700 12px var(--font-body); color: var(--color-neutral-400); width: 16px; }
.list-thumb {
  width: 32px; height: 32px; flex: none;
  border-radius: 50%;
  background: var(--color-accent-100);
  color: var(--color-accent-800);
  display: flex; align-items: center; justify-content: center;
  font: 700 10px var(--font-body);
}
.list-primary {
  font: 600 13px var(--font-body); color: var(--color-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.list-secondary { font: 400 11px var(--font-body); color: var(--color-neutral-600); }
.list-value { font: 700 13px var(--font-body); color: var(--color-text); }
.list-grow { flex: 1; min-width: 0; }

.timeline-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; margin-top: 6px; }

/* ── dashboard: the sales chart (E4, M6) ─────────────────────────────────────
   Bar heights are the one inline style on this page, because they are data. The
   layout is all classes: an inline `grid-template-columns` once outranked every
   media query below and ran a stat strip off the side of a 360px screen. */

/* The chart grows into whatever height the card has rather than sitting 180px
   tall inside it. The row is `align-items: stretch`, so the taller neighbour —
   nine status meters — used to leave 250px of dead space under the bars. */
.bar-chart {
  flex: 1; min-height: 200px;
  display: flex; align-items: flex-end; gap: 16px;
  /* A month is 31 slots; at 16px each the gaps alone are half a panel. The gap
     is the compressible part — the bars and their labels are the content — so
     it shrinks with available width, and anything still too wide scrolls here
     rather than widening the page (the same rule tables follow). */
  gap: clamp(3px, 1.1vw, 16px);
  overflow-x: auto;
}
.bar-slot {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  height: 100%; justify-content: flex-end;
}
.bar-pair {
  flex: 1; width: 100%;
  display: flex; gap: 4px; align-items: flex-end; justify-content: center;
}
.bar { width: 42%; border-radius: 6px 6px 0 0; }
/* A zero-height bar is invisible, which reads as "no bar drawn" rather than
   "no sales". 2px of it stays. */
.bar-prev { background: var(--color-neutral-300); min-height: 2px; }
.bar-cur { background: var(--color-accent); min-height: 2px; }
/* In flow under its own bars, not absolutely positioned: an absolute label does
   not participate in layout, so a thirteen-bar month overflowed its slot and
   collided with its neighbour — first in Bengali, which runs 15–20% wider. */
.bar-label {
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: clip;
  font: 600 10.5px var(--font-body); color: var(--color-neutral-500);
}
@media (max-width: 700px) {
  /* Below this the bars are thinner than their gaps. */
  .bar-chart { gap: 7px; }
}

/* ── dashboard: rows that are links ──────────────────────────────────────── */

/* Every figure links to the screen it was counted from (M6). These undo the
   anchor's own colour and underline so a linked card is visually a card. */
.card-link, .meter-row, .activity-row {
  text-decoration: none; color: inherit;
}
.card-link:hover, .meter-row:hover, .activity-row:hover { background: var(--color-neutral-100); }
.card-link:hover { background: var(--color-surface); box-shadow: var(--shadow-md); }

.meter-row { display: block; padding: 6px 0; border-radius: var(--radius-sm); }
.meter-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.meter-label { font: 500 12.5px var(--font-body); }
.meter-count { font: 600 12.5px var(--font-body); }

.activity-row { align-items: flex-start; }
.activity-line { font: 400 13px var(--font-body); line-height: 1.4; }

/* ── empty state ─────────────────────────────────────────────────────────── */

.empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-4);
  text-align: center;
  color: var(--color-neutral-600);
}
.empty-title {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 20px;
  color: var(--color-text);
}
.empty-body { font-size: 13px; max-width: 46ch; }

/* ── tables ──────────────────────────────────────────────────────────────── */

.table-wrap { overflow-x: auto; }
.table thead th { position: sticky; top: 0; z-index: 1; background: var(--color-surface); }
.table tbody tr:hover { background: var(--color-accent-100); }
.table-foot {
  display: flex; align-items: center;
  padding: 14px 20px;
  border-top: 1px solid var(--color-divider);
}
/* A selected row, held by the same Alpine state the bulk bar reads. */
.row-picked { background: var(--color-accent-100); }
/* An order number is one token — wrapping `GK-2608-00001` over three lines on a
   phone makes the whole row unreadable. The table scrolls sideways instead. */
.order-number {
  font: 700 13px var(--font-body);
  color: var(--color-accent);
  white-space: nowrap;
}
/* It became a link when the order-detail screen landed (E3.7) — the accent colour was
   already promising one. */
a.order-number { text-decoration: none; }
a.order-number:hover { text-decoration: underline; }
.row-thumb {
  width: 28px; height: 28px; flex: none;
  border-radius: 50%;
  background: var(--color-accent-100);
  color: var(--color-accent-800);
  display: flex; align-items: center; justify-content: center;
  font: 700 10px var(--font-body);
}

/* ── list toolbars: filter row, popovers, saved views ────────────────────── */

.filter-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Every filter drawer and date picker on a list screen. `z-index` clears the
   sticky table header (which carries its own) but stays under a dialog. */
.popover {
  position: absolute; top: 44px; left: 0;
  width: 300px; max-width: calc(100vw - 2 * var(--page-gutter));
  z-index: 40;
}
/* `.pill-static` sits on `--color-surface`, which *is* the card background — so a
   chip inside a popover needs the next step down the ramp to read as a control. */
.popover .pill-static { background: var(--color-neutral-100); }

.saved-view {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--color-neutral-100);
  border-radius: 999px;
  padding: 5px 8px 5px 13px;
  font: 600 12px var(--font-body);
}
.saved-view.is-current { background: var(--color-accent-100); color: var(--color-accent-800); }
.saved-view a { color: inherit; text-decoration: none; }
.saved-view form { display: inline-flex; }
.saved-view-shared { display: inline-flex; color: var(--color-neutral-500); }
.saved-view-remove {
  display: inline-flex;
  border: 0; background: transparent; padding: 2px;
  color: var(--color-neutral-500); cursor: pointer;
}
.saved-view-remove:hover { color: var(--color-alert-800); }

/* ── status pills ────────────────────────────────────────────────────────── */

/* Four states across nine statuses, driven by `OrderStatus.color`
   (orders/services/presentation.py). Two hues only: terracotta for negative,
   sage for complete, neutral for everything in flow (design.md §7). */
.pill-status {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 6px 3px 9px;
  border-radius: 999px;
}
.pill-dot, .tab-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.tab-dot { width: 7px; height: 7px; }

.pill-flow { background: var(--color-neutral-200); color: var(--color-neutral-700); }
.pill-flow > .pill-dot, .tab-dot.pill-flow { background: var(--color-neutral-500); }
.pill-done { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.pill-done > .pill-dot, .tab-dot.pill-done { background: var(--color-accent-2-600); }
.pill-warn { background: var(--color-alert-100); color: var(--color-alert-800); }
.pill-warn > .pill-dot, .tab-dot.pill-warn { background: var(--color-alert-700); }
.pill-void { background: var(--color-neutral-100); color: var(--color-neutral-500); }
.pill-void > .pill-dot, .tab-dot.pill-void { background: var(--color-neutral-300); }
.tab-dot.pill-brand { background: var(--color-accent); }

/* The inline status dropdown, drawn as the pill's own label rather than as a
   select — the prototype's row has no visible control chrome. */
.pill-select {
  appearance: none; -webkit-appearance: none;
  border: 0; background: transparent; outline: none;
  padding: 2px; cursor: pointer;
  font: 700 11.5px var(--font-body);
  color: inherit;
}
.pill-select:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }

/* ── bulk selection bar ──────────────────────────────────────────────────── */

/* Inverted, as the prototype draws it: a dark pill that appears over the table
   the moment anything is selected. */
.bulk-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 18px;
  margin-bottom: 12px;
  background: var(--color-text);
  border-radius: 28px;
  color: var(--color-bg);
}
/* Organic's `.input` fills its row; inside the bar it is one control among six,
   so it sizes to its content instead of pushing everything onto a second line. */
.bulk-bar .bulk-input {
  width: auto; flex: none; min-width: 170px;
  height: 32px; padding: 0 14px;
  background: color-mix(in srgb, var(--color-bg) 16%, transparent);
  color: var(--color-bg);
  border: 0;
  font: 600 12px var(--font-body);
}
.bulk-bar .bulk-input option { color: var(--color-text); }
.bulk-bar .bulk-btn {
  height: 32px; padding: 0 14px;
  background: color-mix(in srgb, var(--color-bg) 16%, transparent);
  color: var(--color-bg);
  font: 600 12px var(--font-body);
}
.bulk-bar .bulk-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.bulk-check { color: var(--color-bg); font-size: 12px; }
.bulk-clear {
  border: 0; background: transparent; cursor: pointer;
  font: 700 12px var(--font-body);
  color: color-mix(in srgb, var(--color-bg) 75%, transparent);
}

/* ── row affordances: the customer link and the three quick actions ──────── */

/* A button that reads as text, so the customer cell can open the quick view
   without looking like a form control in a table of names. */
.row-link {
  border: 0; background: transparent; padding: 0;
  font: 600 13px var(--font-body);
  color: var(--color-text);
  cursor: pointer; text-align: inherit;
}
.row-link:hover { color: var(--color-accent); text-decoration: underline; }

.row-actions { display: flex; align-items: center; gap: 2px; color: var(--color-neutral-500); }
.row-action {
  display: inline-flex; padding: 5px;
  border: 0; background: transparent; border-radius: 8px;
  color: inherit; cursor: pointer; text-decoration: none;
}
.row-action:hover { background: var(--color-surface); color: var(--color-accent-800); }

/* ── order modals (E3.3) ─────────────────────────────────────────────────── */

.dialog-md { width: min(520px, 100%); }
.dialog-lg { width: min(600px, 100%); }

/* The little label/value stacks the prototype uses above a modal's content. */
.dialog-facts { display: flex; gap: 20px; flex-wrap: wrap; }
.dialog-facts > div { flex: 1; min-width: 130px; display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
.fact-label {
  font: 700 10px var(--font-body);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-neutral-500);
}
.fact-value { font: 700 14px var(--font-body); color: var(--color-text); }
.fact-muted { font: 400 11.5px var(--font-body); color: var(--color-neutral-500); }
.fact-link {
  display: inline-flex; align-items: center; gap: 4px;
  font: 600 12px var(--font-body);
  color: var(--color-accent); text-decoration: none;
}

/* Lines and totals, drawn as the prototype's inset block rather than a table —
   three columns of two rows is not tabular data. */
.line-table { border-radius: var(--radius-lg); overflow: hidden; background: var(--color-neutral-100); }
.line-head, .line-row { display: flex; align-items: center; gap: 10px; padding: 10px 16px; }
.line-head {
  font: 800 10px var(--font-body);
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--color-neutral-600);
}
.line-row { background: var(--color-bg); font: 600 12.5px var(--font-body); border-top: 1px solid var(--color-divider); }
.line-sku { display: block; font: 400 10.5px var(--font-body); color: var(--color-neutral-500); }
.line-totals {
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px 16px;
  font: 500 12px var(--font-body);
  color: var(--color-neutral-700);
}
.line-totals > div { display: flex; justify-content: space-between; gap: 12px; }
.line-grand {
  padding-top: 8px; border-top: 1px solid var(--color-divider);
  font: 800 13px var(--font-body); color: var(--color-text);
}

.note-block {
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px 14px;
  background: var(--color-accent-100);
  border-radius: var(--radius-md);
}
.note-block p { margin: 0; font: 500 12.5px/1.5 var(--font-body); color: var(--color-text); }

/* Quick view */
.quick-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding-bottom: 14px; border-bottom: 1px solid var(--color-divider);
}
.quick-avatar {
  width: 46px; height: 46px; flex: none; border-radius: 50%;
  background: var(--color-accent-100); color: var(--color-accent-800);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 16px;
}
.quick-name {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 17px; color: var(--color-text);
}
.quick-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.quick-stats > div {
  display: flex; flex-direction: column; gap: 4px;
  background: var(--color-neutral-100);
  border-radius: var(--radius-md);
  padding: 13px 15px;
}
.quick-figure {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 18px; line-height: 1; color: var(--color-text);
}
.quick-total {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 16px;
  background: var(--color-neutral-100);
  border-radius: var(--radius-md);
  font: 700 13px var(--font-body);
}

/* History trail. Two tones: what happened is sage, what is still expected is the
   neutral ramp (design.md §7 — sage is completion, and nothing else here is). */
.timeline { display: flex; flex-direction: column; }
.timeline-row { display: flex; gap: 14px; }
.timeline-rail { display: flex; flex-direction: column; align-items: center; }
.timeline-mark {
  width: 13px; height: 13px; flex: none; border-radius: 50%;
  background: var(--color-neutral-300);
}
.timeline-mark.is-done { background: var(--color-accent-2-600); }
.timeline-line { width: 2px; flex: 1; min-height: 18px; background: var(--color-divider); }
.timeline-line.is-done { background: var(--color-accent-2-600); }
.timeline-body { padding-bottom: 16px; min-width: 0; }
.timeline-label {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font: 700 13px var(--font-body); color: var(--color-text);
}
.timeline-label.is-future { font-weight: 600; color: var(--color-neutral-400); }
.timeline-comment { font: 400 12px/1.5 var(--font-body); color: var(--color-neutral-700); margin-top: 2px; }
.timeline-meta { font: 400 11.5px var(--font-body); color: var(--color-neutral-500); }

.update-form {
  display: flex; flex-direction: column; gap: 8px;
  padding-top: 14px; border-top: 1px solid var(--color-divider);
}

/* ── edit order (E3.4) ───────────────────────────────────────────────────── */

.back-link {
  display: flex; align-items: center; gap: 7px;
  color: var(--color-neutral-600); text-decoration: none;
  font: 600 12.5px var(--font-body);
}
.back-link:hover { color: var(--color-accent); }

/* A soft-tinted button for an action that is destructive but not yet confirmed —
   the prototype's `Cancel order`. The confirming button in the dialog is outlined
   instead, because terracotta is also the brand hue (design.md §2.1). */
.btn-danger-soft {
  height: 38px; padding: 0 16px;
  background: var(--color-alert-100);
  color: var(--color-alert-800);
  border: 0; border-radius: 999px;
  font: 600 13px var(--font-body); cursor: pointer;
}
.btn-danger-outline {
  border: 1.5px solid var(--color-alert-800);
  color: var(--color-alert-800);
}

/* The horizontal trail across the top of the edit screen — the same data the
   history modal draws vertically. */
.status-strip { flex-direction: row; align-items: center; gap: 22px; flex-wrap: wrap; padding: 14px 20px; }
.status-step { display: flex; align-items: center; gap: 7px; font: 700 12px var(--font-body); }
.status-step.is-future { font-weight: 600; color: var(--color-neutral-400); }

.edit-grid { display: flex; gap: 16px; align-items: flex-start; }
.edit-main { flex: 2; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.edit-summary {
  flex: 1; min-width: 270px;
  position: sticky; top: 0;
  gap: 10px;
}
.edit-address {
  font: 400 12px/1.5 var(--font-body);
  color: var(--color-neutral-700);
  border-top: 1px solid var(--color-divider);
  padding-top: 10px;
}

/* Label/value rows — fulfilment card, summary lines. */
.kv-list { display: flex; flex-direction: column; gap: 8px; }
.kv-list > div { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.kv-list > div > span:first-child { font: 400 12px var(--font-body); color: var(--color-neutral-600); }
.kv-list > div > span:last-child { font: 600 12.5px var(--font-body); color: var(--color-text); }

.summary-total {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  border-top: 1px solid var(--color-divider);
  padding-top: 11px;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 16px;
  color: var(--color-text);
}
.summary-total.is-collect { color: var(--color-alert-800); }
.adjust-fields { display: flex; flex-direction: column; gap: 6px; }
.edit-summary .alert-chip { align-self: flex-start; cursor: pointer; }
.summary-note { font: 400 11px var(--font-body); color: var(--color-accent-2-700); }

/* Quantity stepper, transcribed from the prototype's 96×34 pill. */
.stepper {
  margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  width: 100px; height: 34px; padding: 0 10px;
  background: var(--color-neutral-100);
  border-radius: 999px;
}
.stepper button {
  border: 0; background: transparent; cursor: pointer;
  color: var(--color-neutral-600);
  font: 700 16px var(--font-body);
  line-height: 1; padding: 0 2px;
}
.stepper button:disabled { opacity: 0.4; cursor: not-allowed; }
/* The quantity itself is a real input — typeable, and the thing that posts. Its
   chrome is stripped so the pill reads as one control. */
.stepper-input {
  width: 44px;
  border: 0; background: transparent; outline: none;
  text-align: center;
  font: 700 13px var(--font-body); color: var(--color-text);
  appearance: textfield; -moz-appearance: textfield;
}
.stepper-input::-webkit-outer-spin-button,
.stepper-input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.stepper-input:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; border-radius: 6px; }
.stepper.is-off { opacity: 0.45; }

/* A row marked for removal stays visible, struck through, until Save — so the
   staff member can see what they are about to do and undo it. */
.row-removed { opacity: 0.5; }
.row-removed td { text-decoration: line-through; }

/* ── manual order (E3.5) and the shared product picker ───────────────────── */

.manual-header { align-items: flex-start; }
/* The prototype centres a 900px column on this screen — it is a form, not a
   dashboard, and a full-width one is harder to read down. */
.manual-form { display: flex; flex-direction: column; gap: 16px; max-width: 900px; margin-inline: auto; width: 100%; }
.manual-summary { width: 300px; flex: none; gap: 10px; align-self: flex-start; position: sticky; top: 0; }
.field-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

/* Order type and channel, as the prototype's 48px tiles. A radio inside a tile,
   not a bare radio: the whole tile is the target, which matters on a phone. */
.choice-row { display: flex; gap: 8px; flex-wrap: wrap; }
.choice-tile {
  flex: 1; min-width: 120px;
  display: flex; align-items: center; gap: 9px;
  min-height: 48px; padding: 0 15px;
  background: var(--color-neutral-100);
  border-radius: var(--radius-lg);
  font: 700 12.5px var(--font-body);
  cursor: pointer;
}
.choice-tile:has(input:checked) { background: var(--color-accent); color: var(--color-bg); }
.choice-tile input { accent-color: var(--color-bg); }

.picker { display: flex; gap: 10px; flex-wrap: wrap; }
.picker-results { display: flex; flex-direction: column; gap: 2px; }
.picker-row {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 10px;
  border: 0; background: transparent; border-radius: var(--radius-md);
  cursor: pointer; text-align: left; width: 100%;
  color: inherit; font: inherit;
}
.picker-row:hover { background: var(--color-accent-100); }

.line-list { display: flex; flex-direction: column; }
.manual-line {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--color-divider);
}

/* Cancellation reasons, as tappable chips (M24). */
.reason-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.reason-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px;
  background: var(--color-neutral-100);
  border-radius: 999px;
  font: 600 12px var(--font-body);
  cursor: pointer;
}
.reason-chip:has(input:checked) {
  background: var(--color-accent-100);
  color: var(--color-accent-800);
}
.reason-chip input { accent-color: var(--color-accent); }

@media (max-width: 1100px) {
  .edit-grid { flex-direction: column; }
  .edit-summary { position: static; width: 100%; }
}

/* The affirmative twin of `.form-error`, for an HTMX swap's own message. */
.flash-ok {
  font-size: 12.5px;
  color: var(--color-accent-2-800);
  background: var(--color-accent-2-100);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}

/* ── order detail (E3.7) ─────────────────────────────────────────────────── */

/* The header stacks: back link, then the title row, then whichever inline panel the
   status menu opened. `.page-header` is a row, so this overrides its direction rather
   than adding a second header component. */
.detail-header { flex-direction: column; align-items: stretch; gap: 10px; }
.detail-titlerow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* The status pill, as a button. Same pill, one affordance added — the thing you click
   to change the status is the status. */
.pill-button { border: 0; cursor: pointer; font: inherit; }

/* An inline panel under the header: the status menu, the return confirmation. Sits on
   `--color-bg` because the header itself is `--color-surface`. */
.detail-panel {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
}
/* Terracotta tint for the panel that names a consequence (design.md §7). */
.detail-panel.is-warn { background: var(--color-alert-100); }
.detail-note { margin: 0; font: 500 12px/1.55 var(--font-body); color: var(--color-neutral-700); }
.detail-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.status-chips { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.status-chip {
  display: inline-flex; align-items: center; gap: 7px;
  height: 32px; padding: 0 13px;
  background: var(--color-surface);
  border: 0; border-radius: 999px;
  font: 700 12px var(--font-body); color: var(--color-text);
  cursor: pointer;
}
.status-chip:hover { background: var(--color-neutral-200); }
.status-chip.is-current { background: var(--color-neutral-200); cursor: default; }
/* The dot is a shape, not a hue: the three chips that do something other than move
   the order are separated by their trailing ellipsis and their panel, never by colour
   alone (design.md §2.1). */
.status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--color-neutral-400); }
.status-dot.is-warn { background: var(--color-alert); }
.status-dot.is-void { background: var(--color-neutral-300); }

.detail-grid { display: flex; gap: 18px; align-items: flex-start; }
.detail-side { width: 380px; flex: none; display: flex; flex-direction: column; gap: 16px; }

/* ── receiving screen's two-column layout (P2.6) ─────────────────────────── */

/* Same shape as `.detail-grid`/`.detail-side` above, and the same `min-width:
   0` `.activity-card` and `.ship-fields > *` already carry below: a flex item
   with no explicit `width` floors at its content's own minimum size by
   default, and the line-items table's fixed-pixel columns make that floor
   wide. That floor is harmless while `.panel-row` lays its children out in a
   row (`flex: 2` resolves a real width there), but once the row drops to
   `flex-direction: column` below 1100px, width stops being resolved by
   `flex-grow` at all — `align-items: flex-start` sizes the item to fit its
   content instead, wider than the viewport, and `.table-wrap`'s own
   `overflow-x: auto` never gets the chance to scroll it because the ancestor
   grew to avoid needing to. The 1100px override below removes the floor
   rather than the column itself, so the desktop two-column width is
   untouched. */
.receive-main { flex: 2; min-width: 340px; display: flex; flex-direction: column; gap: 16px; }
.receive-side { width: 280px; flex: none; display: flex; flex-direction: column; gap: 16px; }

/* E8's ship form, rendered inert. `disabled` on the fieldset does the work; the
   opacity is what makes it read as not-yet rather than broken. */
.ship-form {
  display: flex; flex-direction: column; gap: 7px;
  border: 0; padding: 12px 0 0; margin: 0;
  border-top: 1px solid var(--color-divider);
  opacity: 0.65;
}
.ship-fields { display: flex; gap: 10px; }
.ship-fields > * { flex: 1; min-width: 0; }

/* ── order activity feed (E3.7, M27) ─────────────────────────────────────── */

.activity-card { flex: 1; min-width: 0; padding: 0; overflow: hidden; }
.activity-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 15px 20px;
}

.quick-log {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 12px 20px;
  border-top: 1px solid var(--color-divider);
}
.log-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 13px;
  background: var(--color-neutral-100);
  border: 0; border-radius: 999px;
  font: 700 11.5px var(--font-body); color: var(--color-text);
  cursor: pointer;
}
/* Two hues, both meaning something (design.md §7): sage is the positive outcome — the
   one the whole call is aiming at — and terracotta is the one that stops the calls.
   Everything else stays on the neutral ramp and is separated by its label. */
.log-chip.is-sage { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.log-chip.is-warn { background: var(--color-alert-100); color: var(--color-alert-800); }
.log-chip:disabled { opacity: 0.45; cursor: not-allowed; }

/* An outcome's own panel, opened inside the strip rather than in a dialog: the client's
   team is working a call list, and a modal per outcome is a tap they will skip
   (design.md §7.2). */
.log-panel {
  flex-basis: 100%;
  display: flex; flex-direction: column; gap: 9px;
  margin-top: 4px; padding-top: 12px;
  border-top: 1px solid var(--color-divider);
}
.log-panel.is-warn { border-top-color: var(--color-alert-200); }
.log-panel-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font: 600 12.5px var(--font-body); }
.log-fields { display: flex; gap: 12px; flex-wrap: wrap; }
.log-fields > .field { min-width: 120px; }
/* The date input inside a chip row: same pill, native picker. */
.log-date {
  display: inline-flex; align-items: center; gap: 7px;
  height: 32px; padding: 0 13px;
  background: var(--color-neutral-100);
  border-radius: 999px;
  font: 700 12px var(--font-body); color: var(--color-text);
}
.log-date input { border: 0; background: transparent; font: 600 12px var(--font-body); color: var(--color-text); }

/* The affirmative button, for the one action that is the goal of the call. */
.btn-sage { background: var(--color-accent-2-700); color: var(--color-bg); border: 0; }

.feed { padding: 8px 20px 20px; }
.feed-day {
  font: 800 10px var(--font-body);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-neutral-500);
  padding: 16px 0 6px;
}
.feed-row { display: flex; gap: 12px; }
.feed-rail { width: 32px; flex: none; display: flex; flex-direction: column; align-items: center; }
.feed-icon {
  width: 32px; height: 32px; flex: none; border-radius: 50%;
  background: var(--color-neutral-100); color: var(--color-neutral-700);
  display: flex; align-items: center; justify-content: center;
}
.feed-line { flex: 1; width: 2px; background: var(--color-divider); margin: 3px 0; }
.feed-body { flex: 1; min-width: 0; padding-bottom: 16px; }
.feed-title {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font: 700 13px var(--font-body); color: var(--color-text);
}
.feed-arrow { display: inline-flex; align-items: center; gap: 6px; color: var(--color-neutral-500); }
.feed-title .tag { font-size: 10px; padding: 2px 9px; }
.feed-note {
  font: 500 12px/1.55 var(--font-body); color: var(--color-neutral-700);
  background: var(--color-neutral-100);
  border-radius: var(--radius-md);
  padding: 9px 12px;
  margin-top: 5px;
}
.feed-facts { font: 500 11.5px var(--font-body); color: var(--color-neutral-600); margin-top: 4px; }
.feed-meta { font: 400 11px var(--font-body); color: var(--color-neutral-500); margin-top: 4px; }

/* ── receiving a return (E11.6) ───────────────────────────────────────────── */

/* The inspect-and-restock table, inside the header's return panel. Rows on
   `--color-surface` so the panel's terracotta tint reads as the frame around them. */
.return-table { display: flex; flex-direction: column; gap: 9px; }
.return-head {
  display: flex; align-items: center; gap: 10px;
  padding: 0 14px;
  font: 800 9.5px var(--font-body);
  letter-spacing: 0.05em;
  color: var(--color-neutral-600);
}
.return-row { background: var(--color-surface); border-radius: var(--radius-md); }
.return-line {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px;
  font: 600 12px var(--font-body);
}
.return-reasons {
  display: flex; flex-wrap: wrap; gap: 5px;
  padding: 0 14px 11px;
}
/* The stepper inside a return row is narrower than the edit screen's 100px pill and
   sits inline rather than centred in a table cell. */
.return-line .stepper { margin: 0; }

/* ── COD reconciliation popover (E11.5) ──────────────────────────────────── */

/* Anchored under the `Reconcile` button on the orders list. Wider than the filter
   popovers because it carries a column list and four figures. */
.reconcile-panel {
  width: min(420px, calc(100vw - 36px));
  padding: 0;
  display: flex; flex-direction: column;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.reconcile-head { padding: 16px 18px 12px; }
.reconcile-body { display: flex; flex-direction: column; gap: 12px; padding: 0 18px 14px; }
.reconcile-cols { display: flex; flex-direction: column; gap: 7px; }
.reconcile-foot { padding: 14px 18px; border-top: 1px solid var(--color-divider); }

/* ── work queues (E11.4) ─────────────────────────────────────────────────── */

.queue-rows { display: flex; flex-direction: column; gap: 12px; }
.queue-row { gap: 0; padding: 16px 20px; }
/* An overdue row is tinted as a whole, not only its chip: the prototype does this, and
   it is what makes a screenful scannable at arm's length. */
.queue-row.is-overdue { background: color-mix(in srgb, var(--color-alert-100) 60%, var(--color-surface)); }
.queue-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.queue-age { width: 96px; flex: none; display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; }
.queue-who { flex: 1; min-width: 220px; }
.queue-attempts { display: flex; flex-direction: column; gap: 2px; min-width: 150px; }

/* Three tones, and they mean SLA state rather than decoration (design.md §7.1): within
   is quiet, approaching is tinted, overdue is filled — the only filled chip on the
   screen, so it reads as the alarm it is. */
.age-chip {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 5px 11px; border-radius: 999px;
  font: 700 12px var(--font-body);
  white-space: nowrap;
}
.age-ok { background: var(--color-surface); color: var(--color-text); box-shadow: inset 0 0 0 1px var(--color-divider); }
.age-soon { background: var(--color-accent-200); color: var(--color-accent-800); }
.age-overdue { background: var(--color-alert); color: var(--color-bg); }

/* The tab badge. Inside `.seg-link`, so it inherits the active state's colours. */
.queue-count { font-weight: 800; opacity: 0.75; }

/* ── Customer 360 (E11.3) ────────────────────────────────────────────────── */

/* Wider than the order screen's 380px column: this side carries four stat cards two
   abreast, and 320px is what the prototype gives them. */
.profile-side { width: 340px; flex: none; display: flex; flex-direction: column; gap: 16px; }
.stat-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.profile-stat { padding: 13px 15px; gap: 3px; }

/* The one card allowed urgency colouring (design.md §7.3). Everything else on the
   profile is neutral, which is what lets this one mean something. */
.next-action { gap: 4px; }
.next-action.is-urgent { background: var(--color-alert-100); }
.next-action.is-urgent .fact-label { color: var(--color-alert-800); }

.child-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--color-divider);
}
.child-row:last-of-type { border-bottom: 0; }
.child-thumb {
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  background: var(--color-accent-2-100); color: var(--color-accent-2-800);
  display: flex; align-items: center; justify-content: center;
  font: 700 11px var(--font-body);
}

/* ── invoice (E3.6, M20) ─────────────────────────────────────────────────── */

/* A print view, so it is sized in millimetres and the chrome around it is marked
   `.screen-only`. There is no VAT row and no BIN on the letterhead (M8) — see
   `orders/services/invoice.py` for why that is a rule and not an omission. */
.invoice {
  width: 100%;
  max-width: 190mm;
  margin-inline: auto;
  background: var(--color-bg);
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.invoice-bar { display: flex; align-items: center; gap: 12px; max-width: 190mm; margin-inline: auto; width: 100%; }

.invoice-head {
  display: flex; align-items: flex-start; gap: 20px; flex-wrap: wrap;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--color-divider);
}
.invoice-brand { display: flex; align-items: center; gap: 9px; }
.invoice-mark {
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  background: var(--color-accent); color: var(--color-bg);
  display: flex; align-items: center; justify-content: center;
  font: 700 12px var(--font-body);
}
.invoice-store {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 18px;
  color: var(--color-text);
}
.invoice-meta { font: 400 11px/1.5 var(--font-body); color: var(--color-neutral-600); margin: 3px 0 0; }
.invoice-ident { margin-left: auto; text-align: right; display: flex; flex-direction: column; gap: 2px; }
.invoice-label {
  font: 700 11px var(--font-body);
  letter-spacing: 0.1em;
  color: var(--color-neutral-600);
}
.invoice-number { font: 700 14px var(--font-body); color: var(--color-text); }

.invoice-parties { display: flex; gap: 24px; flex-wrap: wrap; }
.invoice-parties > div { flex: 1; min-width: 150px; display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }

.invoice-lines { width: 100%; border-collapse: collapse; }
.invoice-lines th {
  text-align: left;
  padding: 9px 10px;
  background: var(--color-neutral-100);
  font: 800 10px var(--font-body);
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--color-neutral-600);
}
.invoice-lines td {
  padding: 10px;
  border-bottom: 1px solid var(--color-divider);
  font: 600 12.5px var(--font-body);
  color: var(--color-text);
  vertical-align: top;
}

.invoice-totals {
  display: flex; flex-direction: column; gap: 7px;
  margin-left: auto;
  width: min(320px, 100%);
  font: 500 12.5px var(--font-body);
  color: var(--color-neutral-700);
}
.invoice-totals > div { display: flex; justify-content: space-between; gap: 12px; }
.invoice-grand {
  padding-top: 9px;
  border-top: 1px solid var(--color-divider);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 15px;
  color: var(--color-text);
}
.invoice-grand.is-due { color: var(--color-alert-800); }

.invoice-note {
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px 14px;
  background: var(--color-neutral-100);
  border-radius: var(--radius-md);
}
.invoice-note p { margin: 0; font: 500 12px/1.5 var(--font-body); color: var(--color-text); }
.invoice-foot {
  padding-top: 14px;
  border-top: 1px solid var(--color-divider);
  font: 400 11px var(--font-body);
  color: var(--color-neutral-600);
}

/* ── P4 reports library (D86) ────────────────────────────────────────────────
   A sibling of A7's report chrome: the same print header, the same parameter
   bar shape. Charts are the dashboard's bars and meters; every chart's table
   sits directly beneath it (ruling d) and is what prints (ruling f). */

.report-tabs { display: flex; flex-direction: column; gap: 10px; }
.report-subtabs .seg-link { padding: 6px 13px; font-size: 12px; }
.rpt-bar { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.rpt-presets { display: flex; gap: 6px; flex-wrap: wrap; }
.rpt-range-form { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.rpt-range-form .field { margin: 0; }

.export-menu { position: relative; flex: none; }
.export-menu > .pill-static { height: 38px; gap: 8px; cursor: pointer; border: 0; }
.export-popover {
  position: absolute; top: 46px; right: 0; z-index: 40;
  width: 250px; max-width: calc(100vw - 2 * var(--page-gutter));
  padding: 6px; gap: 2px;
}
.export-item {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  width: 100%; padding: 10px 12px; border: 0; border-radius: var(--radius-sm);
  background: transparent; text-align: left; text-decoration: none; cursor: pointer;
  color: var(--color-text);
}
.export-item:hover, .export-item:focus-visible { background: var(--color-neutral-100); }
.export-item-title { font: 700 12.5px var(--font-body); }
.export-item-sub { font: 400 10.5px var(--font-body); color: var(--color-neutral-600); }

.report-kpis > .card { gap: 9px; }
.report-kpis .kpi-secondary { font: 600 12px var(--font-body); color: var(--color-neutral-700); }

/* Two narrow sections share a row; a wide one (the trend, a long table) takes it.
   `minmax(0, 1fr)` rather than `1fr` so a long Bengali category name wraps inside
   its card instead of widening the grid past the page. */
.report-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }
.report-grid > .is-wide { grid-column: 1 / -1; }
/* 1100px, not the shell's 900px: below it the rail is still beside the page, and
   two report cards in what is left are too narrow for a Bengali category name.
   (A separate breakpoint also keeps the shell's own 900px block the first one,
   which `test_responsive_shell` reads.) */
@media (max-width: 1100px) {
  .report-grid { grid-template-columns: minmax(0, 1fr); }
}
.report-section { min-width: 0; }
.report-section .panel-head { gap: 10px; flex-wrap: wrap; }
.report-section-tools { display: flex; align-items: center; gap: 10px; }
.report-table-toggle { min-height: 32px; padding: 5px 12px; font-size: 12px; }
.report-meter { padding: 4px 0; }
.report-meter .meter-head { gap: 12px; }
.report-meter .meter-label { min-width: 0; overflow-wrap: anywhere; }
.report-meter .meter-count { flex: none; }
/* A definite height: the bars are percentages of it, and a `min-height` alone
   leaves them nothing to be a percentage of. */
.report-chart .bar-chart { height: 200px; min-height: 0; flex: none; }
.report-text { overflow-wrap: anywhere; }
.report-table .row-sub td:first-child { padding-left: calc(var(--space-2) + 18px); }
.report-table .row-lvl-2 td:first-child { padding-left: calc(var(--space-2) + 36px); }
.report-table .row-lvl-3 td:first-child { padding-left: calc(var(--space-2) + 54px); }
.report-table .row-emphasis td { font-weight: 700; }
.report-note { margin: 0; font: 400 11.5px/1.5 var(--font-body); color: var(--color-neutral-600); }
.report-footnotes {
  margin: 0; padding-left: 20px;
  display: flex; flex-direction: column; gap: 4px;
  font: 400 12px/1.55 var(--font-body); color: var(--color-neutral-700);
}

/* A7 report print header: on paper only. */
.print-only { display: none; }

/* ── print ───────────────────────────────────────────────────────────────── */

@media print {
  /* Everything that is software rather than document goes away: the rail, the
     topbar, the back link, the Print button itself. */
  .rail, .topbar, .nav-backdrop, .screen-only, #dialog-root { display: none !important; }

  /* The page title is already on paper: a report prints its own `.report-header`, and
     the invoice overrides the header block entirely. Hidden everywhere because no
     printed screen relies on it. */
  .page-header { display: none !important; }

  /* Panels are plain paper: no tint, no shadow, a thin rule. */
  .card {
    background: white;
    box-shadow: none;
    border: 1px solid var(--color-neutral-300);
  }

  /* Paper, not pixels: `white` and `black` here are the medium, not brand colours,
     and the warm cream of `--color-bg` would print as a full page of ink. This is
     the one context the token ramp deliberately does not govern. */
  body.admin { background: white; font-size: 12px; }
  .shell { display: block; }
  .page-body { display: block; padding: 0; overflow: visible; }

  /* Every tinted panel goes flat. On screen these separate blocks; on paper they
     are a page of ink, and a rule or a border says the same thing for nothing. */
  .invoice { max-width: none; background: transparent; }
  .invoice-lines th { background: transparent; border-bottom: 1.5px solid black; }
  .invoice-note {
    background: transparent;
    border: 1px solid var(--color-neutral-300);
    border-radius: 0;
  }

  /* A line must not be split across two sheets. */
  .invoice-lines tr, .invoice-totals { break-inside: avoid; }

  /* Reports (A7). The header repeats on every sheet, no row is split across two, and
     a negative stays legible without colour because `money_plain` already wraps it
     in parentheses. */
  .print-only { display: block !important; }
  .table thead { display: table-header-group; }
  /* The foot prints once, at the end: left as a footer group it would repeat under
     every sheet, and a ledger's closing balance would sit under page one's. */
  tfoot { display: table-row-group; }
  tr { break-inside: avoid; }
  .report-header { margin-bottom: var(--space-4); border-bottom: 1.5px solid black; padding-bottom: var(--space-2); }
  .report-business { font: 700 14px var(--font-body); }
  .report-title { font: 700 18px var(--font-body); margin: var(--space-1) 0; }
  .report-range, .report-generated { font: 400 12px var(--font-body); color: var(--color-neutral-700); }

  /* P4 reports (ruling f): the chart is a screen device; on paper its table
     stands in for it, always, whatever the toggle says. The `!important` beats
     Alpine's inline `display: none` on a hidden table. */
  .report-chart { display: none !important; }
  .report-section .chart-table { display: block !important; }
  .report-grid { display: block; }
  .report-grid > .report-section { margin-bottom: var(--space-3); }
  .report-kpis { grid-template-columns: repeat(4, 1fr); margin-bottom: var(--space-3); }
  .report-section .table-wrap { overflow: visible; }

  /* The balance sheet's two columns stack on paper, so neither is squeezed. */
  .bs-sides { display: block !important; }
  .bs-sides > * { margin-bottom: var(--space-3); }

  @page { margin: 14mm; }
}
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ── forms / auth ────────────────────────────────────────────────────────── */

.auth-screen {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background: var(--color-bg);
}
.auth-card { width: min(400px, 100%); gap: var(--space-3); }
.form-error {
  font-size: 12.5px;
  color: var(--color-alert-800);
  background: var(--color-alert-100);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}

/* Organic's `.dialog` sizes to its content, which is fine for the two-line
   confirms it was designed for and clips a twelve-field form off the top and
   bottom of the screen. Admin dialogs scroll inside themselves instead. */
.dialog { max-height: calc(100dvh - var(--space-8)); overflow-y: auto; }
/* Above the sticky table header below. A `thead th` with its own z-index paints
   over a positioned-but-unstacked backdrop, which made a dialog opened over any
   list look like its fields were being clipped by the column titles. */
.dialog-backdrop { z-index: 70; align-items: start; padding-block: var(--space-4); }

/* Checkboxes are the one control Organic has no class for; tinting the native
   one keeps it on-system without reimplementing a control. */
input[type='checkbox'] {
  accent-color: var(--color-accent);
  width: 18px; height: 18px;
  cursor: pointer;
}

/* Use this — not `.radio` — to label a checkbox. Organic's `.radio` hides the
   native input (`opacity: 0; pointer-events: none`) because it draws its own
   `.dot`, so a checkbox inside one is invisible and unclickable. */
.checkbox {
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer; font-size: 13px;
}
.checkbox input { flex: none; }

/* Organic pills every input. A textarea or a list box is not a pill — the
   prototype draws both at the card radius, and a 999px radius visibly clips the
   first character of a multi-select's options. */
textarea.input,
select[multiple].input,
select[size].input { border-radius: var(--radius-md); padding-inline: 12px; }
textarea.input { padding-block: 10px; }

/* Alpine hides cloaked elements until it boots — used on dialogs, never on the
   rail, whose closed state is expressed as plain inline CSS instead. */
[x-cloak] { display: none !important; }

/* ── HTMX: the 150ms accent flash on swap (design.md §7) ──────────────────── */

.htmx-swapping { opacity: 0.6; }
.htmx-settling { animation: gatuka-flash 150ms ease-out; }
@keyframes gatuka-flash {
  from { background-color: var(--color-accent-100); }
  to { background-color: transparent; }
}
.htmx-request .htmx-indicator, .htmx-indicator.htmx-request { opacity: 1; }
.htmx-indicator { opacity: 0; transition: opacity 150ms ease-out; }

/* ── motion ──────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .rail-group-chevron { transition: none; }
}

/* ── responsive ──────────────────────────────────────────────────────────── */

@media (max-width: 1100px) {
  .stat-grid { grid-template-columns: repeat(3, 1fr); }
  .panel-row { flex-direction: column; }

  /* Order detail and the 360: the fixed column and the feed stop sharing a row.
     Stacked, the customer and the summary still come first — which is the reading order
     on the phone as well as the reason the column is on the left. */
  .detail-grid { flex-direction: column; }
  .detail-side, .profile-side { width: 100%; }

  /* The receiving screen's own pair, stacked by `.panel-row` above.
     `min-width: 0` alone is not enough here the way it is for `.activity-card`:
     that fix un-floors a flex-*shrink* in the row direction, but stacked,
     width is the cross axis and `align-items: flex-start` sizes an
     unstretched item by `fit-content`, which is clamped below by the item's
     own min-content — the line table's fixed-pixel columns — regardless of
     `min-width`. `width: 100%`, the same override `.detail-side`/
     `.profile-side` use above, is what actually wins: an authored `width`
     determines the used width outright, giving `.table-wrap` inside a real,
     definite containing width to scroll its overflow against instead of
     growing to avoid needing to. */
  .receive-main { min-width: 0; width: 100%; }
  .receive-side { width: 100%; }
}

/* Below 900px the rail becomes a drawer.
   It used to stack above the content instead, which put a full screenful of
   navigation between the staff member and the screen they asked for — you had
   to scroll past all 21 links to reach the dashboard. */
@media (max-width: 900px) {
  :root { --page-gutter: 18px; }

  .shell { display: block; }

  .topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px var(--page-gutter);
    background: var(--color-surface);
    position: sticky;
    top: 0;
    z-index: 30;
  }

  .rail {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(300px, 86vw);
    min-height: 0;
    /* top/bottom size the drawer here; the desktop rule's explicit height would
       win over `bottom` and leave the footer off-screen on a short viewport. */
    height: auto;
    align-self: auto;
    z-index: 60;
    transform: translateX(-100%);
    /* visibility, not just transform: an off-screen rail must also be out of the
       tab order, or keyboard focus walks into a nav nobody can see. */
    visibility: hidden;
    transition: transform 250ms ease-out, visibility 250ms ease-out;
    box-shadow: var(--shadow-lg);
  }
  .rail.is-open { transform: none; visibility: visible; }
  .rail-close { display: inline-flex; }

  .nav-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 50;
    background: color-mix(in srgb, var(--color-neutral-900) 45%, transparent);
  }

  /* The title and the date range stop competing for one row. */
  .page-header { flex-direction: column; align-items: flex-start; gap: 12px; padding-top: 18px; }
  .page-header-spacer { display: none; }
  .page-body { gap: 18px; }
  .toolbar { gap: 12px; }

  .stat-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .panel-row { gap: 12px; }

  /* Manual order: one column of fields, and the summary stops floating. */
  .field-grid { grid-template-columns: minmax(0, 1fr); }
  .manual-summary { width: 100%; position: static; }
}

@media (max-width: 400px) {
  .stat-value { font-size: 20px; }
}

/* Touch targets never fall below 44px on coarse pointers (design.md §9). */
@media (pointer: coarse) {
  .rail-item, .btn, .seg-link, .alert-chip, .status-chip, .log-chip { min-height: 44px; }
}

/* ── Part 3 shared chrome (P6, D87 f) ───────────────────────────────────────
   Patterns the purchasing and inventory screens repeated inline: a dialog's
   title row, the page-of-pages bar under a register, a summary panel's title,
   and the ruled totals block under it. The declarations are the inline ones,
   moved, so nothing on screen changes. */
.dialog-head { display: flex; align-items: center; gap: 10px; }
.pager { display: flex; align-items: center; padding: 14px 20px; }
.pager-ruled { border-top: 1px solid var(--color-divider); }
.pager-label { font: 600 12px var(--font-body); color: var(--color-neutral-600); }
.pager-buttons { display: flex; align-items: center; gap: 5px; }
.panel-title.summary-title { font-size: 16px; }
.kv-list.kv-list-ruled { border-top: 1px solid var(--color-divider); padding-top: 11px; }

/* ── supplier cards (P1) ──────────────────────────────────────────────────────
   The suppliers list is the one list screen that is not a table: a supplier row
   is mostly figures, and three small stats read better stacked in a card than
   strung across a row. `mockup/AdminSuppliers.dc.html` draws it that way.

   Column counts live here rather than inline for the reason `.stat-grid` above
   gives: an inline `grid-template-columns` outranks every media query, which is
   how a card strip ends up running off the side of a narrow screen. */
.card-grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
@media (min-width: 700px)  { .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1101px) { .card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* The three figures inside a supplier card. Two of them are P2's and render as
   an em-dash until purchasing exists. */
.mini-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.mini-stat {
  background: var(--color-neutral-100);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  min-width: 0;
}
.mini-stat .stat-value { font-size: 16px; }

.card-actions {
  display: flex; gap: 8px;
  border-top: 1px solid var(--color-divider);
  padding-top: 12px;
}

/* A label above its value, for the detail page's contact block. */
.fact-row { display: flex; flex-direction: column; gap: 3px; }

/* The detail page's identity row, and its purchase-order history: flush rows
   under a smaller title than a panel's. Here rather than inline (P6, D87 f). */
.supplier-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.history-card > .panel-title { font-size: 15px; padding: 14px 18px 4px; }
.history-rows { padding: 0 18px 4px; }

/* An inline stat in the page header — the list's `Total payable`. */
.stat-inline { flex-direction: row; align-items: center; gap: 14px; padding: 12px 20px; }

/* `[hidden]` alone cannot close a dialog: `.dialog-backdrop` sets `display: grid`,
   which outranks the attribute's `display: none`. Every other dialog in the admin
   is swapped in by HTMX and so is simply absent until opened; the supplier form is
   inline, so it needs this. */
.dialog-backdrop[hidden] { display: none; }

/* ── integrity banner (A8) ──────────────────────────────────────────────────
   The latest nightly check has findings. Terracotta, as `.form-error`, because
   it is urgent; the heading and the link carry the meaning, not the colour. */
.integrity-banner {
  color: var(--color-alert-800);
  background: var(--color-alert-100);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  font-size: 13px;
}
.integrity-banner-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.integrity-banner-link { margin-left: auto; color: inherit; font-weight: 600; }
.integrity-banner-list { margin: var(--space-2) 0 0; padding-left: var(--space-6); }
.integrity-banner-list li { overflow-wrap: anywhere; }
