/* ============================================================================
   Paper & Petrol — component layer

   CSS classes, not framework components. The consumers are Blazor Razor and
   Next.js/Tailwind; neither can import a React component, and this repo has no
   bundler. A class layer serves both, and it loads with the network blocked.

   Components reference SEMANTIC tokens only. No raw hex below this line.
   ============================================================================ */

/* ---------------------------------------------------------------- Buttons -- */

/* The box, and the only place it is declared. `app.css` used to declare it too
   and won by loading second, so what shipped was a hybrid — `min-height` from
   here (the one property this file alone listed), everything else from there.

   The values below are the ones the admin has ALWAYS rendered, restated in
   tokens: `--space-1 --space-2` padding, `--fs-base` (15px, what `font: inherit`
   was resolving to against the 15px root — NOT `--fs-body`), and a 1.5 line
   height. Unifying does move one thing: the transparent border applies to every
   variant, so primary and danger grow 2px and now match secondary, which had
   carried a real border and stood 1.6px taller than its neighbours. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: var(--control-height);
  padding: var(--space-1) var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: var(--fs-base);
  font-weight: var(--weight-semibold);
  line-height: 1.5;
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  /* box-shadow and transform are here for the admin's hover lift. The lift
     itself is a skin the app layer adds (the offsets it needs are off the px
     scale this file is linted against); the transition belongs with the box so
     a consumer that adds a shadow gets it animated rather than snapping. */
  transition: background var(--motion-fast) var(--ease),
    border-color var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease),
    box-shadow var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-primary {
  background: var(--brand);
  color: var(--on-brand);
  border-color: var(--brand);
}
.btn-primary:hover:not(:disabled) { background: var(--brand-hover); border-color: var(--brand-hover); }

.btn-secondary {
  background: var(--surface-1);
  color: var(--text-strong);
  border-color: var(--border-strong);
}
.btn-secondary:hover:not(:disabled) { background: var(--bg-sunken); }

.btn-ghost {
  background: transparent;
  color: var(--text-default);
  border-color: transparent;
}
.btn-ghost:hover:not(:disabled) { background: var(--bg-sunken); }

/* Destructive. Deliberately `critical`, never `heat-urgent` — this button
   deletes something; it is not an order running late. */
/* `--critical-500`, NOT `--status-critical` — the same two-role split the brand
   tokens have, and the same mistake in the other direction. `--status-critical`
   is the MARK role: it resolves to `--critical-bright` (#f2968c) on the dark
   skin so a red rule or icon stays visible on a near-black canvas. Used as a
   BACKGROUND under `--on-brand-light`, that flip put white text on salmon at
   **2.21:1** in dark mode. `--critical-500` is the surface role and holds
   #b3261e in both skins: 6.54:1 either way. The app never saw the 2.21 because
   `app.css` redeclares `.btn-danger` and loads second — a shadowed rule is not
   a dormant one, it is what the next person gets when the shadow is removed. */
.btn-danger {
  background: var(--critical-500);
  color: var(--on-brand-light);
  border-color: var(--critical-500);
}
.btn-danger:hover:not(:disabled) { filter: brightness(0.92); }

.btn-lg { min-height: var(--space-6); padding: 0 var(--space-3); font-size: var(--fs-h3); }

/* Square icon control. Sized off --control-height so it grows with density. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-height);
  height: var(--control-height);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-default);
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease);
}
.icon-btn:hover { background: var(--bg-sunken); }
.icon-btn[aria-pressed="true"] { background: var(--brand-soft); color: var(--brand-ink); }

/* ------------------------------------------------------------------ Cards -- */

.card {
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-1);
}

.card-pad { padding: var(--space-3); }

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border-hair);
}

.card-interactive { cursor: pointer; transition: box-shadow var(--motion-fast) var(--ease); }
.card-interactive:hover { box-shadow: var(--elev-2); }

/* Stat tile. The metric is mono because it is a number someone compares. */
.stat-tile { padding: var(--space-3); }
.stat-label {
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}
.stat-value {
  margin-top: var(--space-05);
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1, "zero" 1;
  font-size: var(--fs-metric);
  font-weight: var(--weight-semibold);
  color: var(--text-strong);
  line-height: var(--lh-tight);
}
.stat-sub { margin-top: var(--space-05); font-size: var(--fs-caption); color: var(--text-muted); }

/* ------------------------------------------------------- Status + the pill -- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-05);
  padding: var(--space-05) var(--space-1);
  border-radius: var(--radius-pill);
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
}

.pill-fresh    { background: var(--heat-fresh-tint);  color: var(--heat-fresh-ink); }
.pill-warn     { background: var(--heat-warn-tint);   color: var(--heat-warn-ink); }
.pill-urgent   { background: var(--heat-urgent-tint); color: var(--heat-urgent-ink); }
.pill-success  { background: var(--grass-tint);       color: var(--grass-ink); }
.pill-critical { background: var(--critical-tint);    color: var(--critical-ink); }
.pill-info     { background: var(--brand-soft);       color: var(--brand-ink); }
/* Categorical, carries no urgency. Lifecycle status that is neither good news
   nor bad: an order that is simply Open. */
.pill-neutral  { background: var(--bg-sunken);        color: var(--text-default); }

/* Status must never rely on colour alone. Each pill carries a glyph, so the
   state survives greyscale, colour-blindness and a monochrome thermal print. */
.pill::before { font-weight: var(--weight-bold); }
.pill-fresh::before   { content: "\25CB"; }  /* ○ open  */
.pill-warn::before    { content: "\25D0"; }  /* ◐ half  */
.pill-urgent::before  { content: "\25CF"; }  /* ● full  */
.pill-success::before { content: "\2713"; }  /* ✓       */
.pill-critical::before{ content: "\2715"; }  /* ✕       */
.pill-neutral::before { content: "\00B7"; }  /* ·       */

/* --------------------------------------------------- THE SIGNATURE: ticket -- */
/*
   The heat edge. A rail down the left of any order-bearing card, coloured by
   how long the order has been waiting against the tenant's own thresholds
   (TenantSettings.KitchenWarnMinutes / KitchenUrgentMinutes, default 10 / 20).

   The state class is set by the caller from those thresholds. This file holds
   no minutes, because the minutes are per-tenant and belong to the domain.

   Three states, because two thresholds make three bands. A fourth would need a
   threshold the domain does not have.
*/
.ticket {
  position: relative;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.ticket::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--heat-edge-width);
  background: var(--ticket-edge, var(--status-fresh));
}

.ticket-fresh  { --ticket-edge: var(--status-fresh); }
.ticket-warn   { --ticket-edge: var(--status-warn); }
.ticket-urgent { --ticket-edge: var(--status-urgent); }

/* Urgent earns one extra affordance beyond colour — a heavier rail. Colour is
   never the only carrier of state. */
.ticket-urgent { --heat-edge-width: 6px; }

.ticket-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.ticket-no {
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1, "zero" 1;
  font-size: var(--fs-h3);
  font-weight: var(--weight-bold);
  color: var(--text-strong);
}

.ticket-age {
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1, "zero" 1;
  font-size: var(--fs-body);
  font-weight: var(--weight-semibold);
  color: var(--ticket-edge, var(--text-muted));
}

.ticket-meta {
  font-size: var(--fs-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}

.ticket-lines { margin: var(--space-1) 0 0; padding: 0; list-style: none; }
.ticket-lines li {
  display: flex;
  gap: var(--space-1);
  padding: 2px 0;
  font-size: var(--fs-body);
  color: var(--text-default);
}
.ticket-lines .qty {
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1;
  font-weight: var(--weight-semibold);
  color: var(--text-strong);
  min-width: 2.5ch;
}

/* The kitchen reads these from across the pass. */
[data-density="touch"] .ticket-no { font-size: var(--fs-ticket); }
[data-density="touch"] .ticket-age { font-size: var(--fs-h3); }

/* ------------------------------------------------------------------ Forms -- */

.field { display: flex; flex-direction: column; gap: var(--space-05); }

.field-label {
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  color: var(--text-default);
}

.input {
  min-height: var(--control-height);
  padding: 0 var(--space-1);
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font-size: var(--fs-body);
}
/* The UA draws a disabled control greyed and flat; `.input` overrides the fill
   and so has to say what disabled looks like itself, or a read-only field
   (TenantEdit's slug and currency, fixed for the life of the tenant) becomes
   indistinguishable from an editable one. Sunken rather than pale: it reads as
   a value that has been recorded, not a field waiting for input. */
.input:disabled {
  background: var(--bg-sunken);
  color: var(--text-muted);
  cursor: not-allowed;
}
.input::placeholder { color: var(--text-faint); }
.input:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 0; border-color: var(--brand-ink); }
.input[aria-invalid="true"] { border-color: var(--status-critical); }

.field-error { font-size: var(--fs-caption); color: var(--status-critical); }
.field-hint  { font-size: var(--fs-caption); color: var(--text-muted); }

/*
   Filter bar. The header of every list page: a row of labelled controls, a
   submit, and a reset. It is a CARD, not a toolbar — it sits on --surface-1 with
   the page's elevation, so the list below reads as the result of it.

   `align-items: end` is what lines the buttons up with the inputs rather than
   with the labels above them. Its labels are eyebrow-cased (uppercase, tracked)
   because they are read as column headings for the controls, not as prompts.
*/
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  align-items: end;
  background: var(--surface-1);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-hair);
  box-shadow: var(--elev-1);
  margin-bottom: var(--space-3);
}

.filter-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-05);
}
.filter-field label {
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
/* These are bare <input>/<select> elements — the filter bar is a plain GET form,
   so there is nothing to hang `.input` on — which makes this rule the only thing
   standing between them and the UA's own control skin. It must therefore carry
   `background` and `color` as well as the box: the same `--surface-1` /
   `--text-strong` pair `.input` uses, so the two cannot disagree about what a
   control looks like. Without them the browser filled in its defaults — #FFFFFF
   and #EFEFEF on paper, #3B3B3B and #6B6B6B on the dark canvas (`color-scheme`
   only tells the UA which greys to pick, not to stop picking) — which put the
   select's label at 3.20:1 in dark mode, below the 4.5:1 floor. */
.filter-field input,
.filter-field select {
  padding: var(--space-1) var(--space-2);
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font: inherit;
}

/* Brand as a BACKGROUND here, so it pairs with --on-brand. */
.filter-submit {
  background: var(--brand);
  color: var(--on-brand);
  border: none;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  font: inherit;
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease);
}
.filter-submit:hover { background: var(--brand-hover); }

/* Reset is a link, not a button — it undoes rather than does. */
.filter-reset {
  padding: var(--space-1) var(--space-2);
  color: var(--text-muted);
  text-decoration: none;
  font-size: var(--fs-body);
}
.filter-reset:hover {
  color: var(--text-strong);
  text-decoration: underline;
}

/* --------------------------------------------------------------- Feedback -- */

.alert {
  display: flex;
  gap: var(--space-1);
  padding: var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: var(--fs-body);
}
.alert-info     { background: var(--brand-soft);    color: var(--brand-ink);      border-color: currentColor; }
.alert-success  { background: var(--grass-tint);    color: var(--grass-ink);      border-color: var(--status-success); }
.alert-warning  { background: var(--heat-warn-tint); color: var(--heat-warn-ink); border-color: var(--status-warn); }
.alert-error    { background: var(--critical-tint); color: var(--critical-ink);   border-color: var(--status-critical); }

/*
   Skeleton. A layout-true placeholder for a page's FIRST load, so a grid does
   not visibly "pop" when the data lands. This is the shimmer only — SIZE IT
   YOURSELF, to the text it stands in for, so the placeholder occupies the same
   box the real content will. A skeleton that is not layout-true is worse than
   a spinner: it moves the page twice.

   The shimmer is decorative, so it stops under prefers-reduced-motion. That
   @media rule and the @keyframes both belong to .skeleton — the three move
   together or the animation breaks silently.
*/
.skeleton {
  display: inline-block;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    var(--skeleton-base) 25%,
    var(--skeleton-sheen) 45%,
    var(--skeleton-base) 65%);
  background-size: 200% 100%;
  animation: skeleton-sheen 1.4s ease-in-out infinite;
  color: transparent;
  user-select: none;
  pointer-events: none;
}

@keyframes skeleton-sheen {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ---------------------------------------------------------------- Overlay -- */
/*
   A right-anchored drawer and its scrim. This family was NOT designed here — it
   is the POS's shipped line-draft drawer, lifted out of the Blazor app so the
   system documents what actually exists rather than inventing a second modal.
   Its users are the till: the line drawer, the meal-deal drawer, the customer
   drawer and the checkout modal.

   Two things changed on the way in, both to stop it contradicting the system:
   the panel now sits on --surface-1 rather than the --slate-0 compat alias
   (which is NOT re-pointed on the dark skin, so the drawer rendered as a white
   panel under light ink — 1.66:1), and the bespoke leftward shadow became
   --elev-3, which is what the storefront kit already uses for its basket popup.

   The drawer is a SHEET, not a dialog box: it is anchored to an edge and full
   height, because the till is operated with a thumb at the screen's edge and a
   centred box would put the controls where the hand is not. Use it when the
   task is a step in the order being built. For a short confirm-or-cancel
   question, a centred dialog is the better shape — the system does not have one
   yet; add it when a surface needs it rather than bending this.
*/

.drawer-backdrop {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  z-index: 10;
}

.line-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 28rem;
  max-width: 100vw;
  background: var(--surface-1);
  box-shadow: var(--elev-3);
  display: flex;
  flex-direction: column;
  z-index: 11;
}

/* NOTE ON THE NUMBERS BELOW. Several paddings and sizes are off the 8px scale
   (1.25rem = 20px, 0.75rem = 12px, 0.95rem, 1.15rem …). They are reproduced
   EXACTLY as the till ships them. Snapping them to the space or type scale
   would shift
   the drawer's geometry on a live surface for no functional gain, and this
   extraction's whole point is that the system describes what exists. Snap them
   deliberately, in their own change, with eyes on the till. */

.drawer-head {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: 1rem 1.25rem 0.75rem;
  border-bottom: 1px solid var(--border-hair);
}
.drawer-head h2 {
  margin: 0;
  font-size: 1.15rem;
  flex: 1 1 auto;
}

/* Two-line head: item name with a step counter beneath it (the line drawer's
   wizard). Sits INSIDE .drawer-head in place of a bare h2, so the h2 keeps its
   size from the rule above. */
.drawer-head-titles {
  display: flex;
  flex-direction: column;
}
.drawer-head-titles h2 { margin: 0; }
.drawer-step-count { font-size: 0.85rem; }

.drawer-close {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0 var(--space-05);
}
.drawer-close:hover { color: var(--critical-ink); }

.drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0.75rem 1.25rem;
}

.drawer-section {
  margin-bottom: 1.25rem;
  /* A step-wizard advance (keyed on the step index) swaps this element
     wholesale rather than patching it in place, so the entrance animation
     replays on every step — without it, a short step being replaced by a taller
     one reads as the new content appearing in the old step's spot instead of as
     a distinct new step. The keyframes below BELONG to this rule: move them
     together or the animation silently stops. */
  animation: drawer-section-in 0.15s ease-out;
}

@keyframes drawer-section-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

.drawer-section h3 {
  margin: 0 0 0.4rem;
  font-size: 0.95rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* Half-and-half per-half marker (e.g. "½ Margherita") in a section heading.
   Categorical — it says WHICH half, not how urgent — so it takes the brand
   slot, never a heat tint. */
.drawer-half-chip {
  background: var(--accent-tint);
  color: var(--accent);
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  padding: 0.05rem var(--space-1);
  border-radius: var(--radius-pill);
  margin-left: 0.35rem;
}

.drawer-foot {
  border-top: 1px solid var(--border-hair);
  padding: 0.75rem 1.25rem;
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

/* The money about to be committed. `margin-right: auto` is what pushes the
   Cancel/Add buttons to the far edge — the foot has no spacer element. */
.drawer-total {
  margin-right: auto;
  font-weight: var(--weight-bold);
  font-size: 1.1rem;
}

/* ------------------------------------------------------------- Navigation -- */
/* Tabs (below) switch between siblings. Breadcrumb and pagination are the other
   two ways a list-and-detail admin moves: up, and along. */

/*
   Breadcrumb. ONE line, above the h1, for climbing back out of a detail page —
   not a full path. The admin is two levels deep at most, so "← Back to tenants"
   carries more than "Platform / Tenants / Acme Takeaway" and survives a narrow
   screen. Links take --brand-ink (brand-as-foreground); --brand-hover is a
   BACKGROUND token and must never be a resting link colour.
*/
.breadcrumb {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-body);
}
.breadcrumb a {
  color: var(--brand-ink);
  text-decoration: none;
}
.breadcrumb a:hover { text-decoration: underline; }

/*
   Pagination. Prev / indicator / Next, not numbered pages: an operator looking
   for an order searches or filters, they do not go to page 7. The disabled end
   stays in the DOM and greys out, so the control does not change width at the
   first and last page.
*/
.pagination {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: var(--fs-body);
}
.pagination a {
  color: var(--brand-ink);
  text-decoration: none;
}
.pagination a:hover { text-decoration: underline; }
.pagination .disabled { color: var(--text-faint); }

.page-indicator { color: var(--text-default); }

/* ------------------------------------------------------------------- Tabs -- */
/* The storefront needs these for the category rail, where Special Offers is
   its own tab rather than a banner repeated on every category. */

.tabs {
  display: flex;
  /* --control-gap, not --space-05: under touch density adjacent tabs must keep
     8px of separation, or a thumb lands between two categories. */
  gap: var(--control-gap);
  border-bottom: 1px solid var(--border-hair);
  overflow-x: auto;
  scrollbar-width: thin;
}

.tab {
  flex: 0 0 auto;
  min-height: var(--control-height);
  padding: 0 var(--space-2);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font-size: var(--fs-body);
  font-weight: var(--weight-medium);
  cursor: pointer;
  white-space: nowrap;
}
.tab:hover { color: var(--text-strong); }
.tab[aria-selected="true"] {
  /* Both the label and the underline are brand-as-foreground. A tenant's yellow
     would vanish against paper if either used --brand directly. */
  color: var(--brand-ink);
  border-bottom-color: var(--brand-ink);
  font-weight: var(--weight-semibold);
}

/* ------------------------------------------------------- The kitchen dock -- */
/*
   Since the KDS and Dispatch pages were retired, the dock IS the kitchen: a
   collapsed bar carrying outstanding counts, expanding to the Collection and
   Delivery rails.

   NOTE ON COLOUR. Collection and Delivery are a *category*, not a status. They
   are already distinguished by position and an uppercase label, so the counts
   stay neutral. The live implementation paints Delivery with `--accent` (the
   BRAND token) and Collection with `--success-500` — brand-as-status and
   success-meaning-collection, both wrong. State belongs on the tickets, in the
   heat edge and the pills. Do not colour a fulfilment type.
*/

.dock {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 4;
  background: var(--surface-1);
  border-top: 1px solid var(--border-hair);
  box-shadow: var(--elev-3);
}

.dock-bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  width: 100%;
  /* Fixed, not min-height: consumers reserve exactly --dock-height behind it. */
  height: var(--dock-height);
  padding: 0 var(--space-2);
  background: none;
  border: none;
  cursor: pointer;
}

.dock-half { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.dock-half--del { justify-content: flex-end; }

/* Neutral. The label says which is which. */
.dock-count {
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1, "zero" 1;
  font-size: var(--fs-metric);
  font-weight: var(--weight-bold);
  line-height: 1;
  color: var(--text-strong);
}

.dock-label { display: flex; flex-direction: column; line-height: 1.2; }
.dock-label .t {
  font-size: var(--fs-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--weight-bold);
  color: var(--text-default);
}
.dock-label .s { font-size: var(--fs-caption); color: var(--text-muted); }
.dock-label.right { text-align: right; }

.dock-caret { color: var(--text-faint); }

.dock-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
  padding: var(--space-2);
  border-top: 1px solid var(--border-hair);
  max-height: 46vh;
  overflow-y: auto;
}

.dock-rail { display: flex; flex-direction: column; gap: var(--space-1); }
.dock-rail > .eyebrow { position: sticky; top: 0; background: var(--surface-1); padding: var(--space-05) 0; }
.dock-empty { color: var(--text-muted); font-size: var(--fs-body); padding: var(--space-2) 0; }

/* ------------------------------------------------------------- Scheduling -- */
/*
   Choosing when: the day strip and the slot grid. Contract + rationale live in
   components/scheduling/scheduling.prompt.md — the short version is that
   `input[type=date]` supports `min` and `max` and NOTHING else, so it is
   structurally incapable of expressing "not on a day we are closed". That is
   why this is a component.

   The principle these rules serve: an unorderable day must be UNPICKABLE, not
   pickable-then-empty. Closed days are shown, dimmed and labelled with the
   reason, because a gap in the strip reads as a bug while "Sun — Closed" reads
   as an answer.

   Consumed by the POS phone-order form (day strip + slot grid, FP-4). The
   storefront answers the same contract in Tailwind — it cannot import this
   file — so the two are kept honest by the prompt doc, not by a shared class.
*/

.day-strip {
  display: flex;
  gap: var(--control-gap);
  overflow-x: auto;
  /* Seven touch-sized days do not fit a till or a phone. Without min-width:0 a
     nowrap flex child sets a min-content floor for the whole PAGE rather than
     scrolling itself — see grid-item-min-content-is-a-floor-on-the-whole-page,
     which is exactly how the POS came to overflow below ~1054px. */
  min-width: 0;
  scrollbar-width: thin;
  padding-bottom: var(--space-05);
}

.day {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* A day is a control, so it inherits the density's control height: 48px under
     touch. An operator taps this with a thumb mid-service. */
  min-height: var(--control-height);
  padding: var(--space-05) var(--space-2);
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-md);
  color: var(--text-default);
  font-size: var(--fs-body);
  line-height: var(--lh-tight);
  cursor: pointer;
}

.day:hover:not(:disabled) { background: var(--bg-sunken); }

.day[aria-checked="true"] {
  /* Brand as a BACKGROUND, so its partner is --on-brand. A tenant's yellow used
     as text here would read at 1.2:1 (see the two-roles gotcha). */
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
  font-weight: var(--weight-semibold);
}

.day-name { font-weight: var(--weight-medium); }
.day-date { font-size: var(--fs-caption); opacity: 0.8; }

/* The reason a day cannot be taken. Never blank — a dimmed day with no word is
   the "why can't I order Sunday?" phone call this component exists to prevent. */
.day-note {
  font-size: var(--fs-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--weight-semibold);
}

/* Disabled is `disabled` AND `aria-disabled` in the markup — a class alone does
   not stop a keyboard user activating it. This only paints that fact. */
.day:disabled,
.day[aria-disabled="true"] {
  background: var(--bg-sunken);
  color: var(--text-muted);
  cursor: not-allowed;
}

.slot-grid {
  display: grid;
  /* auto-fill, never auto-fit: auto-fit collapses empty tracks, so a day with
     three remaining slots would stretch them across the full width and the
     times would jump size as the evening wears on. */
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: var(--control-gap);
  min-width: 0;
}

.slot {
  min-height: var(--control-height);
  padding: 0 var(--space-1);
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-md);
  /* Times are digits read in a column — mono keeps them aligned and stops 17:15
     and 11:11 rendering at visibly different widths. */
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1, "zero" 1;
  font-size: var(--fs-body);
  color: var(--text-default);
  cursor: pointer;
}

.slot:hover:not(:disabled) { background: var(--bg-sunken); }

.slot[aria-checked="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
  font-weight: var(--weight-semibold);
}

/* ------------------------------------------------------------------ Table -- */

.data-table { width: 100%; font-size: var(--fs-body); }
.data-table th {
  padding: var(--space-1) var(--space-2);
  text-align: left;
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-hair);
}
.data-table td {
  padding: var(--space-2);
  border-bottom: 1px solid var(--border-hair);
  color: var(--text-default);
}
.data-table tbody tr:hover { background: var(--bg-sunken); }

/* Numeric columns are mono and right-aligned so digits line up down the page. */
.data-table .num { text-align: right; }
