/* SUM order portal.
 *
 * The information architecture is the design spec's (docs/design/order-portal-spec.md):
 * evidence above interpretation, exception-only flags, in-page sheets, no prices anywhere.
 * The *visual* tokens below deliberately diverge from that spec's, and now from the spec's
 * screen inventory too — Claude Design's "Portal UI redesign" landed 2026-09-07 and this
 * file is the answer. If the two disagree on colour, type, radius, spacing or which screen
 * a thing lives on, this file and the templates are current and the spec is the history.
 *
 * ---------------------------------------------------------------------------------------
 * ## The direction, and what replaced what
 *
 * Until 2026-09-07 the palette was ISO 3864 — SUM sells safety equipment, so the argument
 * was that the portal should be printed in the colours its own products are: blue tells you
 * to act, orange tells you to look before you move, green clears, red stops. That argument
 * is retired, and it is worth saying why rather than letting it look like drift. It gave
 * every screen four saturated families at once, so the one line actually blocking an order
 * competed with a status chip, a tab badge and a link for the same attention. The redesign
 * spends colour once: a warm stone ground, graphite chrome, and **one** signal — a lime that
 * appears only where something needs a hand.
 *
 * So `--signal` is a *fill*, never ink. It is legible only under near-black, which is the
 * whole point: it cannot be spent on a link or a caption by accident, because it does not
 * work there. Everything the reader is meant to *act* on is still blue (`--accent`), and
 * green/red still clear and stop. What changed is that caution stopped being a colour
 * family and became one mark.
 *
 * Two type registers survive unchanged, because they carry the argument the review screen
 * exists to make. What the client said is set in the human face, large and generous. What
 * the agent made of it is set as a stamped machine record — mono, tight, uppercase — the way
 * a certification code is struck into the band inside a helmet. An approver can tell speech
 * from machine reading across the room, before reading either.
 *
 * No web fonts. The team is on Lebanese mobile data and a font that arrives late is worse
 * than a system font that arrives now, so the personality comes from weight and tracking
 * contrast inside the system stacks rather than from a downloaded face. The Arabic stack
 * leads on client-authored strings so mixed-script lines render in one consistent face.
 *
 * Light ground on purpose, and no dark mode: this is read on phones outdoors in Beirut
 * daylight, where a dark screen is the wrong tool. The navigation rail is the one dark
 * surface, and it is chrome rather than content — nothing is read off it.
 *
 * Mobile-first — approvals happen on phones, often on site. Everything up to 900px is the
 * default: the rail becomes a bottom tab bar, and the review screen is one column. The
 * 76px side rail and the two-pane review layout are both the desktop enhancement.
 */

:root {
  /* Ground and surfaces: warm stone, not the cool steel this file used to be. Paper the
     colour of a site notice rather than of a screen. */
  --bg: #eeece6;
  --sunken: #e5e2da;
  --surface: #ffffff;
  --surface-muted: #f7f5f0;

  /* Ink is near-black and slightly warm, to sit on the stone rather than float over it. */
  --ink: #141310;
  --ink-2: #57534a;
  /* The caption ink. It clears 4.5:1 on --surface (5.71), --bg (4.83) and --surface-muted
     (5.24) — but reads 4.41 on --sunken, which is under AA. Captions on a sunken plate
     take --ink-2 instead; the old palette carried the same footnote about its own --ink-3
     and the constraint outlived the colours. Nothing in the product may set --ink-3 text
     on --sunken. */
  --ink-3: #6b665c;

  --rule: #dcd8ce;
  /* The divider *inside* a surface — list rows, a card's own footer. --rule is for edges
     against the ground and is too heavy for a row separator on white. */
  --rule-soft: #e9e5db;
  --rule-strong: #c4bfb2;

  /* Blue is what the reader is meant to act on: links, the focus ring, the filled button
     that is not an approval. It is the only saturated colour that appears as text. */
  --accent: #1f4b99;
  --accent-soft: #e8edf7;
  --accent-ink: #143566;

  /* The one signal. A FILL, never ink — it fails every contrast threshold as text, which
     is the guard rather than the flaw. Used on: the edge band of a card that needs a hand,
     the count of things waiting, the hazard stripe, and the rail's current destination.
     If a fifth use appears, one of the four was not load-bearing. */
  --signal: #dcf250;
  --signal-ink: #141310;

  /* Caution survives as a tint for prose that has to be read, not as an attention-getter:
     the offer with no catalog wording, the stale age, the sentence about an order the
     thread has moved past. The mark that gets attention is --signal. */
  --alert: #c2410c;
  --alert-deep: #c2410c;
  --alert-soft: #f9ece2;
  --alert-edge: #eeceb5;
  --alert-ink: #8a3405;

  --go: #146c4a;
  --go-deep: #0f5a3d;
  --go-soft: #eaf2ee;
  --stop: #b4291b;
  --stop-deep: #962115;
  --stop-soft: #f9eae8;
  --stop-edge: #cf8f86;

  /* The rail. Its own scale because nothing else in the product sits on a dark ground, and
     borrowing --ink-2 and --rule for it would mean two jobs per token and a re-skin of one
     silently changing the other. --rail-ink reads 7.32:1 on --rail, --rail-ink-quiet 5.26. */
  --rail: #141310;
  --rail-raised: #2c2a23;
  --rail-hover: #241f19;
  --rail-rule: #3b372e;
  --rail-ink: #a8a297;
  --rail-ink-quiet: #8d887c;

  --sans: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", "Roboto Mono", Consolas, monospace;
  --arabic: "Noto Sans Arabic", "Segoe UI", Tahoma, sans-serif;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s7: 32px; --s8: 44px;

  --rail-w: 76px;

  --r-sm: 6px; --r: 10px; --r-lg: 14px; --r-pill: 999px;

  --shadow-1: 0 1px 2px rgba(20, 19, 16, .06), 0 1px 3px rgba(20, 19, 16, .05);
  --shadow-2: 0 1px 2px rgba(20, 19, 16, .06), 0 10px 28px rgba(20, 19, 16, .09);
  --shadow-3: 0 24px 64px rgba(20, 19, 16, .28);

  /* A white gap inside the ring so it stays visible against tinted surfaces too. */
  --focus: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}

* { box-sizing: border-box; }
/* `hidden` has to beat every `display` this file sets, because it is what the palette
   and its two openers are toggled with — a rule like `.rail-key { display: inline-flex }`
   otherwise wins on specificity and the attribute does nothing. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}

h1 {
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -.022em;
  margin: 0 0 var(--s4);
}
h2 { font-size: 17px; font-weight: 650; letter-spacing: -.01em; margin: 0; }
p { margin: 0 0 var(--s2); }

a { color: var(--accent); text-underline-offset: 2px; }
/* Not `.btn`. A link styled as a button takes its colour from its own variant, and the
   filled one hovers to --accent-ink — which this rule, winning on the extra :hover, would
   then paint the label in: "Add an offer" hovered to a blank blue slab. Excluded here
   rather than by restating `color` in every variant's :hover, so a variant added later
   cannot forget it.

   `:where()` and not `:not()`, which is not a style choice: :not() takes its argument's
   specificity, so `a:not(.btn):hover` would read (0,2,1) and outrank every single-class
   hover below it — `.back`, `.tab` and `.to-catalog` would all start hovering blue. This
   rule has to stay exactly as specific as it was, and :where() contributes nothing. */
a:where(:not(.btn)):hover { color: var(--accent-ink); }

:focus-visible { outline: none; box-shadow: var(--focus); border-radius: var(--r-sm); }

/* Forced-colors mode drops box-shadow entirely and keeps outline — which is precisely the
   property the rule above turns off, so without this every control in the portal loses its
   focus indicator for the users most likely to be relying on one. */
@media (forced-colors: active) {
  :focus-visible { outline: 2px solid; outline-offset: 2px; }
}

/* Client-authored text: Arabic face first, never truncated, generous leading. */
[dir="auto"] { font-family: var(--arabic), var(--sans); line-height: 1.65; overflow-wrap: anywhere; }

.page { max-width: 780px; margin: 0 auto; padding: var(--s5) var(--s4) 80px; }
/* Signed in, the page is placed by the shell and no longer centres itself in the viewport:
   it centres in the column beside the rail. Left-aligned rather than centred, because the
   rail is already a left edge and a centred column beside it reads as drifting. */
.shell .page { margin: 0; max-width: 1240px; padding-bottom: 96px; }

/* Some screens have no visible title — the queue is a search box and three tabs, and
   adding a heading above them would only repeat the header bar. They still need one
   for anyone navigating by headings rather than by eye.

   `.topbar-name` joins them below 900px. On a phone the bar already carries a title, a
   subtitle, the palette handle, Catalog and Sign out, and the name is the one item there
   whose job something beside it already does — the avatar. It is hidden this way rather
   than with `display: none` because the avatar is `aria-hidden`: dropped from the page
   instead of from sight, a screen reader on a phone would be told nobody is signed in. */
.sr-only,
.topbar-name {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* --- Chrome: the rail, the top bar ---------------------------------------------------
 *
 * Five destinations, always all five, always in the same order. A rail rather than the row
 * of seven text links this replaced: the old header hid Offers, Focus, Senders and Team
 * behind an admin check, so the bar an approver saw and the bar an admin saw were different
 * shapes and neither said what the other had. The rail shows every destination to everyone
 * and lets the wall be the wall — `NotAnAdmin` already renders a named page saying who to
 * ask, which is a better answer than a link that was never there.
 *
 * Mobile-first, so the default here is the bottom tab bar and the side rail is the
 * enhancement at 900px. Bottom rather than top on a phone because the portal is used
 * one-handed on site, and because a top bar plus a sticky page header is two bars of chrome
 * over a screen whose content is the point.
 */

.shell { display: flex; flex-direction: column; min-height: 100vh; }

.rail {
  position: fixed; inset: auto 0 0 0; z-index: 30;
  display: flex; align-items: stretch; justify-content: space-around;
  gap: var(--s1); padding: var(--s1) var(--s2);
  padding-bottom: max(var(--s1), env(safe-area-inset-bottom));
  background: var(--rail);
  border-top: 1px solid var(--rail-rule);
}
/* The brand and the palette key are desktop-only furniture: on a phone the bar is five
   targets and nothing else, because a sixth would take width from all five. */
.rail-mark, .rail-key { display: none; }

.rail-item {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5px; min-height: 52px; padding: var(--s1) 2px;
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--rail-ink-quiet); text-decoration: none; cursor: pointer;
  box-shadow: none;
  transition: background .12s ease, color .12s ease;
}
.rail-item:hover { background: var(--rail-hover); color: var(--bg); }
.rail-item svg { display: block; flex: 0 0 auto; }
.rail-item span {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; line-height: 1;
}
/* The current destination is the one place `--signal` appears in the chrome. On the dark
   ground it is the only fill that reads without a second treatment, and `aria-current`
   carries the same fact to a reader — the class is not the only signal. */
.rail-item.is-current { background: var(--rail-raised); color: var(--signal); }
.rail-item.is-current:hover { background: var(--rail-raised); color: var(--signal); }

@media (forced-colors: active) {
  .rail-item.is-current { outline: 2px solid; outline-offset: -2px; }
}

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

/* Title, subtitle, the palette handle and who you are. Sticky because the review screen is
   long and the reference is the one fact you lose scrolling it. */
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s4);
  padding: var(--s2) var(--s4);
  background: var(--bg); border-bottom: 1px solid var(--rule);
}
.topbar-title {
  flex: 0 0 auto; margin: 0;
  font-size: 17px; font-weight: 650; letter-spacing: -.02em; line-height: 1.2;
}
.topbar-sub {
  flex: 1 1 120px; min-width: 0; margin: 0;
  font-size: 13px; line-height: 1.3; color: var(--ink-3);
}
/* The palette handle. A button and not an input: it opens a dialog whose own field takes
   the typing, so a second focusable text box here would be two places to type one query. */
.topbar-search {
  flex: 0 0 auto; gap: var(--s3);
  min-height: 36px; padding: 0 var(--s2) 0 var(--s3);
  color: var(--ink-3); font-size: 13.5px; font-weight: 400;
  border-radius: var(--r-sm);
}
.topbar-search:hover { border-color: var(--ink); color: var(--ink); background: var(--surface); }
.key {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  padding: 4px 5px; border-radius: var(--r-sm);
  background: var(--sunken); color: var(--ink-2);
}
.topbar-you { display: flex; align-items: center; gap: var(--s2); flex: 0 0 auto; }
.topbar-you .btn-quiet { min-height: 36px; font-size: 12.5px; }
.avatar {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
  background: var(--ink); color: var(--signal);
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  line-height: 28px; text-align: center;
}

.inline { display: inline; }

/* The label sewn into the garment — the sign-in heading, and the rail's own mark. */
.mark {
  display: inline-block; padding: 2px 7px 3px;
  background: var(--ink); color: var(--signal);
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  letter-spacing: .06em; border-radius: var(--r-sm);
}

.back {
  display: inline-flex; align-items: center; gap: var(--s1);
  margin-bottom: var(--s4);
  font-size: 14px; font-weight: 550; color: var(--ink-2); text-decoration: none;
}
.back:hover { color: var(--ink); }

.flash {
  display: flex; gap: var(--s3);
  padding: var(--s3) var(--s4); margin-bottom: var(--s5);
  background: var(--surface); border: 1px solid var(--rule); border-radius: 0 var(--r) var(--r) 0;
  border-left: 5px solid var(--rule-strong);
  font-size: 14.5px;
}
.flash-ok  { border-left-color: var(--go); background: var(--go-soft); }
.flash-err { border-left-color: var(--stop); background: var(--stop-soft); color: var(--stop); }

/* --- Controls ------------------------------------------------------------------------ */

button, .btn, input, select, textarea { font: inherit; }

.btn, button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 44px; padding: 0 var(--s4);
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule-strong); border-radius: var(--r-sm);
  font-size: 15px; font-weight: 600; letter-spacing: -.005em;
  text-decoration: none; cursor: pointer;
  box-shadow: var(--shadow-1);
  transition: background .12s ease, border-color .12s ease, transform .06s ease;
}
/* The coloured variants below match this rule's specificity exactly and win only by
   coming later. Keep them after it — grouping the hover states elsewhere would
   silently turn every filled button grey on hover, with nothing to catch it. */
button:hover, .btn:hover { background: var(--surface-muted); border-color: var(--ink-3); }
button:active, .btn:active { transform: translateY(1px); }

/* Green is reserved for one meaning — an order cleared — so a filled button that is not
   about clearing an order takes the accent instead. Signing in is not an approval. */
.btn-primary {
  background: var(--ink); border-color: var(--ink); color: var(--surface);
}
.btn-primary:hover { background: var(--rail-raised); border-color: var(--rail-raised); }

.btn-confirm {
  background: var(--go); border-color: var(--go); color: var(--surface);
}
.btn-confirm:hover { background: var(--go-deep); border-color: var(--go-deep); }

.btn-reject {
  /* The border is this button's only edge at rest — the shadow is 5% and carries
     nothing — so it has to clear 3:1 as a UI boundary. The softer tint did not. */
  background: var(--surface); border-color: var(--stop-edge); color: var(--stop);
}
.btn-reject:hover { background: var(--stop-soft); border-color: var(--stop); }

.btn-quiet {
  min-height: 44px; padding: 0 var(--s3);
  background: transparent; border-color: var(--rule-strong); color: var(--ink-2);
  font-size: 13.5px; font-weight: 550; box-shadow: none;
}
.btn-quiet:hover { background: var(--sunken); color: var(--ink); }

button.link {
  min-height: 0; padding: 0; border: 0; background: none; box-shadow: none;
  color: var(--ink-2); font-size: 14px; font-weight: 550;
}
button.link:hover { background: none; color: var(--ink); }

/* `number` joined this list with the shipping screen (ADR-033), which is the first place
   the portal asks for one. It is an allowlist rather than a bare `input`, so a type absent
   from it silently keeps UA chrome — the Total field rendered visibly unlike the text field
   directly above it, and below the 44px target every other control here meets. */
input[type=text], input[type=email], input[type=password], input[type=search],
input[type=number], select, textarea {
  min-height: 44px; padding: var(--s2) var(--s3); width: 100%;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule-strong); border-radius: var(--r-sm);
  border-radius: var(--r-sm);
}
input::placeholder, textarea::placeholder { color: var(--ink-3); }
textarea { min-height: 76px; line-height: 1.55; }

label {
  display: block; margin-bottom: var(--s3);
  font-size: 13px; font-weight: 600; color: var(--ink-2);
}
label input, label select, label textarea { margin-top: var(--s1); font-weight: 400; }

summary { cursor: pointer; list-style: none; }
summary::-webkit-details-marker { display: none; }

.hint { color: var(--ink-3); font-size: 13.5px; }

/* Everything machine-authored — timestamps, ids, codes — is set as a stamped record.
   These carry codes and handles with no spaces in them, so they wrap or they overflow;
   there is no third option and truncation is not one of them. */
.meta { color: var(--ink-3); font-size: 12px; font-family: var(--mono); letter-spacing: -.01em;
  overflow-wrap: anywhere; }

.section-head {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-3); margin: var(--s7) 0 var(--s3);
}

/* --- Search and tabs ----------------------------------------------------------------- */

/* Capped: the shell takes `.page` to 1240px for the two-pane review screen and every other
   screen inherits it, which drew a reference box the width of the window. A reference is
   eight characters. */
.search { display: flex; gap: var(--s2); margin-bottom: var(--s4); max-width: 480px; }
.search input[type=search] { border-radius: var(--r); }
.search .clear { align-self: center; font-size: 13.5px; white-space: nowrap; }

/* A segmented control rather than underlines: three fixed destinations, one of which is
   always current — the shape should say "pick one", not "here is a menu". */
.tabs {
  display: flex; gap: var(--s1); margin-bottom: var(--s5);
  padding: var(--s1); background: var(--sunken); border-radius: var(--r);
}
.tab {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  flex: 1; min-height: 44px; padding: 0 var(--s3);
  border-radius: var(--r-sm);
  font-size: 14px; font-weight: 600; color: var(--ink-2); text-decoration: none;
  transition: background .12s ease, color .12s ease;
}
.tab:hover { color: var(--ink); }
.tab.is-active {
  background: var(--surface); color: var(--ink);
  box-shadow: var(--shadow-1);
}
.count { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--ink-3); }
/* One step darker inside the segmented control, and only there. `--ink-3` is 4.41:1 on
   `--sunken` — the one pairing this stylesheet's palette note rules out — and the inactive
   tab is the only place in the product a count is drawn on that fill rather than on white.

   `:not(.count-filled)` because this selector is a class more specific than the pill's, and
   without it the active tab's badge would take this colour off `--signal` instead of the
   `--signal-ink` that exists for that one pairing. */
.tab .count:not(.count-filled) { color: var(--ink-2); }
.count-filled {
  padding: 1px 7px 2px; border-radius: var(--r-pill);
  background: var(--signal); color: var(--signal-ink);
}

/* --- Order cards --------------------------------------------------------------------- */

.cards { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3); }

.card {
  position: relative; display: block; overflow: hidden;
  padding: var(--s4) var(--s4) var(--s3);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
  text-decoration: none; color: var(--ink);
  transition: border-color .14s ease, box-shadow .14s ease;
}
.card:hover { border-color: var(--ink); box-shadow: var(--shadow-1); }

/* The edge band is not decoration and not on every card: it marks the orders that need a
   hand before they can move, which is the only thing a queue scan is looking for. */
.card-flagged::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 5px;
  background: var(--signal);
}
.card-flagged { padding-left: calc(var(--s4) + 5px); }

.card-top { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); }

/* The reference is a stamp, so it is set like one — and it reads as the eyebrow above the
   client's name, which is what the eye is actually hunting for in a queue. */
.ref {
  font-family: var(--mono); font-size: 12.5px; font-weight: 600; letter-spacing: .02em;
  color: var(--ink-2); background: var(--sunken);
  padding: 2px var(--s2) 3px; border-radius: var(--r-sm);
}
.ref-lg {
  display: inline-block; font-size: 18px; letter-spacing: .01em;
  padding: var(--s1) var(--s3) 5px; color: var(--ink);
}
.age { font-family: var(--mono); font-size: 12px; color: var(--ink-3); white-space: nowrap; }
.age-stale { color: var(--alert-ink); font-weight: 600; }

/* The card clips, so an unbroken label has to wrap or it is silently truncated with
   no cue. Never an ellipsis: who the order is from is a decision-relevant field. */
.sender {
  font-size: 17px; font-weight: 650; letter-spacing: -.015em;
  margin: var(--s3) 0 var(--s1); overflow-wrap: anywhere;
}
.preview { color: var(--ink-2); font-size: 14.5px; margin-bottom: var(--s3); }

.flags { display: flex; flex-wrap: wrap; gap: var(--s1); }
.flag {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  padding: 3px var(--s2) 4px; border-radius: var(--r-sm);
  background: var(--sunken); color: var(--ink-2);
}
.flag-warn { background: var(--alert-soft); color: var(--alert-ink); }

.empty {
  padding: var(--s8) var(--s5); text-align: center;
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
}
.empty-head { font-size: 18px; font-weight: 650; letter-spacing: -.015em; margin-bottom: var(--s2); }
.empty p:last-child { margin-bottom: 0; color: var(--ink-2); font-size: 14.5px; }

/* --- Order review -------------------------------------------------------------------- */

.order-head { margin-bottom: var(--s5); }
.order-head .meta { display: block; margin-bottom: var(--s3); }
.order-head .meta strong { color: var(--ink); font-family: var(--sans); font-size: 15px; }

.chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-bottom: 0; }
/* Status reads as a tint; full hi-vis is spent on exceptions only (`.flag-warn`) and on the
   hazard band. "Needs review" is the normal case on this screen and must not out-shout the
   one line that is actually blocking. */
.chip {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  padding: 4px var(--s3) 5px; border-radius: var(--r-pill);
  background: var(--accent-soft); color: var(--accent-ink);
}
.chip-confirmed, .chip-shipped { background: var(--go-soft); color: var(--go); }
.chip-rejected, .chip-voided { background: var(--stop-soft); color: var(--stop); }
.chip-on_hold { background: var(--alert-soft); color: var(--alert-ink); }

.note-bar {
  margin-top: var(--s4); padding: var(--s3) var(--s4);
  background: var(--surface); border: 1px solid var(--rule); border-radius: 0 var(--r) var(--r) 0;
  border-left: 5px solid var(--rule-strong);
  font-size: 14.5px;
}
.note-warn { border-left-color: var(--alert); background: var(--alert-soft); }

/* --- The line: evidence, then the rule, then the interpretation ---------------------- */

.line {
  position: relative;
  padding: var(--s4); margin-bottom: var(--s3);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
}

/* The signature. An unmatched line is the one thing that stops an order from moving, and
   diagonal hazard striping is exactly what that means everywhere else in this industry.
   It appears in one place and means one thing; nothing else on the page shouts. */
.line-unmatched { padding-top: var(--s5); }
.line-unmatched::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 7px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: repeating-linear-gradient(-45deg,
    var(--signal) 0 9px, var(--ink) 9px 18px);
}

.said-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-3); margin-bottom: var(--s2);
}
/* Arabic here renders right-to-left, correctly, but is aligned with the column rather
   than with its own script. The label above and the source link below are the app's
   chrome and stay put; without this the three lines alternate edges on the block every
   review decision is read from. These are short fragments, not prose — the thread
   below keeps native alignment, where whole messages earn it. */
.said { font-size: 17px; line-height: 1.6; margin-bottom: var(--s2); text-align: left; }
.jump { font-size: 13px; font-weight: 550; }
.jump-none { color: var(--ink-3); font-weight: 400; }

.line-rule { border: 0; border-top: 1px solid var(--rule); margin: var(--s4) 0; }

.read { display: flex; gap: var(--s4); align-items: flex-start; }

/* The quantity is the most-corrected number on the screen, so it gets the weight. */
.qty {
  font-family: var(--mono); font-size: 24px; font-weight: 700; letter-spacing: -.03em;
  min-width: 2.5ch; text-align: right; line-height: 1.25;
}
.product { flex: 1; min-width: 0; }
.product-name { font-size: 15.5px; font-weight: 650; letter-spacing: -.01em; margin-bottom: var(--s1); }
.product-none { color: var(--alert-ink); }

/* The name is the link to the showroom's page for the product. It keeps the ink colour
   rather than taking the accent: it is still the agent's reading of the line, which is
   what the approver is here to judge, and a blue heading on every line would pull the eye
   away from the one thing on this block that is meant to shout — the hazard stripe. The
   underline is what says it is a link, and the arrow says it leaves.

   Which is why the underline is --ink-3 and not --rule-strong: #c9d2da on --surface read
   at 1.53:1, below the 3:1 a non-text indicator needs, and it was carrying the whole
   affordance on its own. #5c6873 measures 5.70:1 there and is still plainly quieter than
   the 18.20:1 name it sits under. */
.to-catalog { color: inherit; text-decoration: underline; text-decoration-color: var(--ink-3); }
.to-catalog:hover { color: var(--accent); text-decoration-color: currentColor; }
.to-catalog-mark {
  display: inline-block; margin-inline-start: 4px;
  font-size: 12px; font-weight: 400; color: var(--ink-3); text-decoration: none;
}
.to-catalog:hover .to-catalog-mark { color: var(--accent); }

/* The struck-in certification stamp: machine record, visibly not something a person typed. */
.product-code {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
  font-family: var(--mono); font-size: 12px; color: var(--ink-2);
  background: var(--sunken); padding: 2px var(--s2) 3px; border-radius: var(--r-sm);
  margin-bottom: 0; overflow-wrap: anywhere;
}
.provenance { font-size: 12px; color: var(--ink-3); font-style: italic; margin: var(--s1) 0 0; }

.line-actions {
  display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center;
  margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--rule);
}
/* Quiet does not mean small. These are tapped one-handed on site, so they keep the
   full 44px target and shed weight through colour and type instead. */
.line-actions input[type=text] { width: 5.5ch; text-align: center; }
.line-actions .btn { padding: 0 var(--s3); font-size: 13.5px; box-shadow: none; }

.add-line { margin-top: var(--s4); }
.add-line summary { font-size: 14.5px; font-weight: 600; color: var(--accent); }
.add-line[open] summary { margin-bottom: var(--s2); }

/* --- The decision -------------------------------------------------------------------- */

/* The one surface that keeps a lifted shadow as well as an edge: it is where the order is
   decided, and it is meant to sit above the evidence rather than beside it. */
.decide {
  margin-top: var(--s6); padding: var(--s5);
  background: var(--surface); border: 1px solid var(--rule-strong); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
.blocked {
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  background: var(--alert-soft); color: var(--alert-ink);
  border-radius: var(--r); font-size: 14px; font-weight: 600;
}

/* The one sentence on a screen that says an action cannot be undone. `.hint` was carrying
   this on the shipping block, at 13.5px in the faintest ink the palette has — pixel-
   identical to "In your own words" two lines below it. The caution family, unfilled: the
   amber band `.blocked` uses is for work that is *stopped*, and nothing here is blocked. */
.warns {
  margin-bottom: var(--s3);
  color: var(--alert-ink);
  font-size: 14.5px;
  font-weight: 600;
}
.decide-row { display: flex; gap: var(--s3); align-items: flex-start; }
/* Capped, so that on an order blocked from confirmation — where Reject is the only control
   left standing — it does not inherit the full width and read as the recommended move. */
.reject-box { flex: 1 1 auto; max-width: 240px; }
/* `max-width` overrides the flex-basis, so the cap above has to be lifted explicitly
   or the opened sheet renders its reason form inside a 240px column. */
.reject-box[open] { max-width: none; }
.confirm-box { flex: 2; }
.reject-box > summary, .confirm-box > summary { display: flex; width: 100%; }
.reject-box[open], .confirm-box[open] {
  flex: 1 1 100%;
  padding: var(--s4); background: var(--surface-muted);
  border: 1px solid var(--rule); border-radius: var(--r);
}
.decide-row:has(details[open]) { flex-wrap: wrap; }

/* An open sheet turns its own summary into a plain "never mind" control. Left styled as
   the primary button it was, the still-green header competes with the real confirm button
   inside the sheet, and the two together read as two ways to say yes. */
details[open] > summary.btn-confirm,
details[open] > summary.btn-reject {
  background: transparent; color: var(--ink-3); border-color: transparent;
  box-shadow: none; font-size: 13.5px; font-weight: 550;
}
.confirm-ask { font-size: 17px; font-weight: 650; letter-spacing: -.015em; margin: var(--s2) 0 var(--s1); }
.confirm-box form, .reject-box form { margin-top: var(--s2); }
.confirm-box form button, .reject-box form button { width: 100%; margin-top: var(--s2); }
.decide-secondary { margin-top: var(--s4); text-align: center; }

.frozen {
  padding: var(--s4); margin-bottom: 0;
  background: var(--surface-muted); border: 1px solid var(--rule); border-radius: var(--r);
  font-size: 14.5px; color: var(--ink-2);
}
.ship-block { margin-top: var(--s5); }
.ship-block .btn { width: 100%; margin-top: var(--s3); }
/* The two fields are the mis-click guard now that shipping has no undo, so they get the
   room a form deserves rather than the single optional input this block used to hold. */
.ship-block label { display: block; margin-top: var(--s3); }
.ship-block input { width: 100%; }
.ship-block .hint { margin-top: var(--s1); }
.void-box { margin-top: var(--s5); }
.void-box > summary { display: inline-flex; }

/* --- Notes, thread, trail ------------------------------------------------------------ */

.notes form { margin-bottom: var(--s4); }
.notes form button { margin-top: var(--s2); }
.note {
  padding: var(--s3) var(--s4); margin-bottom: var(--s2);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r);
}
.note p:last-child { margin-bottom: 0; }

/* The client's side of the conversation, styled as their side: one aligned column of
   speech, distinct from every machine-authored block on the page. */
.msg {
  padding: var(--s3) var(--s4); margin-bottom: var(--s2);
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r) var(--r) var(--r) var(--r-sm);
  max-width: 90%;
}
.msg p:last-child { margin-bottom: 0; }
/* Line breaks survive. Both sides write them — a client listing four sizes one per line,
   and the agent answering with a numbered list of products — and HTML collapses every one
   of them into a single run-on paragraph. That was invisible while the thread held only
   the client's short messages; it is not invisible against a reply. Scoped to the message
   text (`dir="auto"` is on exactly those paragraphs) so it never reaches the chrome. */
.msg p[dir="auto"] { white-space: pre-wrap; }
.msg:target { box-shadow: var(--focus); }
.msg-empty { color: var(--ink-3); font-style: italic; }

/* A voice note's words are the agent's hearing of them, not something the client typed,
   and the thread is the one screen where that difference decides whether a reviewer
   trusts a line. The rule marks the whole quotation as reported speech — the `.said-label`
   above it names whose. Native alignment, unlike the evidence block above: these are whole
   messages, which is exactly where that block's comment says it is earned. */
/* Logical, not `border-left`: `dir="auto"` puts an Arabic transcript in RTL, and a rule
   pinned to the physical left would sit at the end of the quotation rather than its start. */
.heard {
  border-inline-start: 3px solid var(--rule-strong); padding-inline-start: var(--s3);
  color: var(--ink-2);
}
.msg .said-label { margin-bottom: var(--s1); }

/* The conversations screen has no second column to share the width with, and a bubble
   stretched across 1180px stops reading as speech. Narrower than the page rather than
   centred: the thread hangs from the same left edge as every heading above it. */
.chat { max-width: 720px; }

/* The agent's side, which the spec asked for from the start: "client bubbles left, agent
   bubbles right". Two signals, because one is not enough on a screen read in a hurry — the
   column it hangs from, and a ground that is plainly not the client's white. `--sunken`
   rather than the spec's dark: these are the longest blocks of Arabic on the page, and
   inverting a paragraph of it costs more legibility than the distinction is worth. The
   tail corner mirrors the client's, so the two columns read as one conversation rather
   than as one column with some rows tinted. No accent anywhere: that belongs to what a
   reviewer acts on, and a transcript is evidence, not an action. */
.msg-agent {
  margin-inline-start: auto;
  background: var(--sunken);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r) var(--r) var(--r-sm) var(--r);
  box-shadow: none;
}
/* Right where the bubble is, for the same reason the bubble is: the alternative is a
   timestamp on the far side of a gap from the message it dates. `dir="auto"` is on the
   message itself and not here — this line is chrome. */
.msg-agent .meta { text-align: end; }

.trail-list { list-style: none; margin: 0; padding: 0; font-size: 14px; }
.trail-list li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2);
  padding: var(--s3) 0; border-bottom: 1px solid var(--rule);
}
.trail-list li:last-child { border-bottom: 0; }
.trail-list strong { font-weight: 600; }
.trail-detail { color: var(--ink-2); font-size: 13px; overflow-wrap: anywhere; }
.trail-list .meta { margin-left: auto; }

/* --- Picker and admin ---------------------------------------------------------------- */

.picking-for {
  padding: var(--s4); margin-bottom: var(--s4);
  background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  border-left: 4px solid var(--accent);
}
.picking-for p:last-child { margin-bottom: 0; }

.results, .admin-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.results li, .admin-list li {
  padding: var(--s4); background: var(--surface);
  border: 1px solid var(--rule); border-radius: var(--r);
}
.results form { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: flex-end; }
.result-main { flex: 1 1 220px; }
.result-main p:last-child { margin-bottom: 0; }
.add-fields { display: flex; gap: var(--s2); flex: 1 1 260px; }
.add-fields label { margin-bottom: 0; }

.admin-list li { display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: space-between; align-items: center; }
.admin-main { flex: 1 1 220px; }
.admin-main p:last-child { margin-bottom: 0; }
.admin-name {
  display: flex; align-items: center; gap: var(--s2);
  font-size: 15.5px; font-weight: 650; letter-spacing: -.01em; margin: 0 0 var(--s1);
  overflow-wrap: anywhere;
}
.admin-actions { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.admin-actions input[type=text] { width: auto; }
.is-off { opacity: .55; }

.add-box { margin-bottom: var(--s5); }
.add-box > summary { display: inline-flex; }
.add-box[open] {
  padding: var(--s5); background: var(--surface);
  border: 1px solid var(--rule); border-radius: var(--r-lg);
}
.add-box[open] > summary { margin-bottom: var(--s4); }

/* Shown exactly once — the border is hi-vis because there is no second chance to read it. */
.one-time {
  padding: var(--s5); margin-bottom: var(--s5);
  background: var(--surface); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2); border-top: 5px solid var(--alert);
}
.one-time-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-3); margin-bottom: var(--s2);
}
.one-time-value {
  font-family: var(--mono); font-size: 26px; font-weight: 700; letter-spacing: .02em;
  word-break: break-all; margin-bottom: var(--s2);
}

/* --- Sign in ------------------------------------------------------------------------- */

.signin {
  max-width: 380px; margin: 8vh auto 0; padding: var(--s7) var(--s6);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
.signin h1 {
  display: flex; align-items: center; gap: var(--s2);
  font-size: 22px; margin-bottom: var(--s6);
}
.signin button { width: 100%; margin-top: var(--s2); }
.signin .hint { margin: var(--s5) 0 0; text-align: center; }

/* --- Desktop: the review screen goes two-pane ---------------------------------------- */

@media (min-width: 900px) {
  .page { max-width: 1180px; padding: var(--s6) var(--s6) 80px; }
  .shell .page { max-width: 1240px; padding: var(--s6) var(--s5) 80px; }
  .cards { grid-template-columns: 1fr 1fr; }
  .tabs { max-width: 480px; }

  /* The bottom tab bar becomes the side rail. `position: sticky` rather than the fixed
     bottom bar's `fixed`, so the main column is laid out beside it by flow and needs no
     matching margin — a fixed rail plus a hand-kept `padding-left` is how the two drift
     apart the first time the width changes. */
  .shell { flex-direction: row; align-items: stretch; }
  .rail {
    position: sticky; inset: 0 auto auto auto; top: 0;
    flex: 0 0 var(--rail-w); width: var(--rail-w); height: 100vh;
    flex-direction: column; justify-content: flex-start; align-items: center;
    gap: var(--s1); padding: var(--s3) 0 var(--s5);
    border-top: 0;
  }
  .rail-mark {
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; margin-bottom: var(--s3);
    border-radius: var(--r-sm); background: var(--signal); color: var(--signal-ink);
    font-family: var(--mono); font-size: 11px; font-weight: 800; letter-spacing: .02em;
    text-decoration: none;
  }
  .rail-item { flex: 0 0 auto; width: 64px; min-height: 0; padding: 9px 0 8px; }
  /* Pushes the palette key to the foot of the rail. A spacer element rather than
     `margin-top: auto` on the key itself, because the key is `display: none` on a phone
     and an auto margin on a hidden element does nothing — the rule would look like it
     worked and the layout would depend on the breakpoint twice. */
  .rail-gap { flex: 1 1 auto; }
  .rail-key {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; padding: 0;
    background: transparent; border: 1px solid var(--rail-rule); border-radius: var(--r-sm);
    color: var(--rail-ink); font-family: var(--mono); font-size: 12px; font-weight: 600;
    box-shadow: none; cursor: pointer;
  }
  .rail-key:hover { background: transparent; border-color: var(--signal); color: var(--signal); }

  .topbar { padding: var(--s2) var(--s5); min-height: 60px; }
  /* Room for it again: out of the sr-only clip and back beside the avatar. */
  .topbar-name {
    position: static; width: auto; height: auto; margin: 0;
    overflow: visible; clip-path: none;
    font-size: 13px; font-weight: 550; color: var(--ink-2);
  }

  /* Lines and decisions on the left, the client's messages alongside — the comparison
     the whole screen exists for, without scrolling between the two halves.

     Rows are named rather than numbered. An earlier version pinned the thread to
     `grid-row: 3`, which silently broke the moment a flash message appeared and pushed
     every row down by one — the thread jumped above the order header. Named areas cannot
     drift that way. */
  body:has(.thread) .page {
    /* Wider than the other screens, and only this one: it is the only place two columns of
       content are read against each other, and the comparison is the whole screen. */
    max-width: 1400px;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "flash flash"
      "back  back"
      "head  head"
      "offers offers"
      "lines thread"
      "decide thread"
      "notes thread"
      "trail trail";
    gap: 0 var(--s7);
    align-items: start;
  }
  body:has(.thread) .flash { grid-area: flash; }
  body:has(.thread) .back { grid-area: back; }
  body:has(.thread) .order-head { grid-area: head; }
  /* Every child of `.page` has to be named. `.order-offers` was not, and an unplaced grid
     item is auto-placed into the first free cell — which, on an order carrying an offer and
     no flash message, is the flash row at the very top. The panel rendered above the "←
     Orders" link and above the reference, describing lines the reader had not reached yet.
     It renders on a minority of orders and only without a flash, which is why it survived:
     the two conditions rarely coincide in a test. */
  body:has(.thread) .order-offers { grid-area: offers; }
  body:has(.thread) .lines { grid-area: lines; }
  body:has(.thread) .decide { grid-area: decide; }
  body:has(.thread) .notes { grid-area: notes; }
  body:has(.thread) .trail { grid-area: trail; }

  /* Two elements, and the split is the whole point. The column is what the grid places
     and what the sticky pane may travel inside; the pane is what sticks. Sticking the
     grid item itself put the pane's containing block beyond its own area — it slid down
     over the Trail, and the Trail's right-aligned timestamps surfaced in the gutter
     beside the messages, clipped by a bubble on one side and the scrollbar on the other.
     The column ends where the notes end, which is the constraint that was missing. */
  /* `align-self` overrides the container's `align-items: start`, for this item only:
     start-aligned, the column would be exactly as tall as the pane inside it and the pane
     would have no room to travel — it would scroll away with the page instead of staying
     beside the lines. Stretched, the column is the height of the three rows it spans,
     which is the distance the pane is allowed to follow the reader down. */
  body:has(.thread) .thread-col { grid-area: thread; align-self: stretch; }
  body:has(.thread) .thread {
    position: sticky; top: 76px;
    max-height: calc(100vh - 100px); overflow-y: auto;
    /* Padding on both sides, not just the scrollbar's: `overflow-y: auto` clips the other
       axis too, and the messages sit flush against the left edge — so the ring that marks
       the message a line points at was being shaved off exactly when it was jumped to. */
    padding-inline: var(--s2);
  }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .card:hover { transform: none; }
  button:active, .btn:active { transform: none; }
}

/* --- Offers (ADR-019, restyled after ADR-022) -----------------------------------------
 *
 * The list is the order cards' grammar reused, with the two type registers put the right way
 * round. This file's argument is that human speech is set in the human face and a machine
 * reading is stamped — and the offer card had it inverted: the title somebody typed wore
 * `.ref`, the mono chip reserved for machine-authored codes, while `describe()` reading the
 * typed parameters back was set as a pulled quote in generous human type.
 *
 * That was right when the card shipped. The quote then held the sentence the team wrote for
 * clients, which really was speech. ADR-022 deleted that column — the agent phrases every
 * offer himself now, per client and per language — and this line kept its styling after its
 * content changed category. Swapping the two registers is the whole move.
 *
 * So the title is a plain link in the sans face, and the mechanic is struck onto a plate the
 * way a certification code is struck into the band inside a helmet. It is the largest thing
 * on the card because "what does this offer do" is the only question a scan is asking; the
 * title is a handle for finding the row again, which is a different job and a quieter one.
 *
 * Status colour follows the ISO mapping the rest of the file uses: green clears (running),
 * blue is the mandatory-action family (scheduled — something the team has committed to),
 * and finished or pulled offers get no signal at all, because they signal nothing.
 *
 * That last clause is literally true: those cards used to carry `opacity: .72` as well.
 * `opacity` composites the whole subtree — the card's white fill along with its text — so
 * against the page ground it took every caption on a stopped card to 3.16:1, the offer title
 * to 3.39:1 and the Stop/Resume label to 3.78:1, all under AA. The card that failed hardest
 * was the one holding Resume, which is the only way to bring a stopped offer back. Nothing
 * was lost by dropping it: these cards sit under their own heading, which already says
 * Stopped or Finished, so the dimming was repeating the heading at the cost of the controls.
 */

/* Heading, count and blurb describe the cards under them, so they travel as one block. The
   bare `.section-head` margin left the blurb floating between two groups. */
.group-head { margin-top: var(--s7); }
.group-head .section-head { margin: 0 0 2px; }
.group-head .hint { margin-bottom: var(--s3); }
.group-head:first-of-type { margin-top: var(--s6); }
/* `letter-spacing` inherits, so the heading's .1em was being applied to a mono numeral too
   and the count read as another tracked-out word rather than as a number. */
.group-head .count { margin-inline-start: var(--s2); letter-spacing: normal; }

/* Transparent on the two states that carry no signal, rather than absent: a card with no
   band was inset 3px less than the ones above it, and a column of cards showed it. */
.offer-card { border-left: 3px solid transparent; }
.offer-card .card-top { margin-bottom: var(--s3); align-items: baseline; }

/* Human-authored, so the human face. It is also the card's only link, and it carries no
   border to say so, which is what the hover underline is for. */
.offer-name {
  font-size: 16px; font-weight: 650; letter-spacing: -.012em;
  color: var(--ink); text-decoration: none; overflow-wrap: anywhere;
}
.offer-name:hover { color: var(--accent-ink); text-decoration: underline; }

.offer-live   { border-left: 3px solid var(--go); }
.offer-scheduled { border-left: 3px solid var(--rule-strong); }

/* The plate is `--sunken`, not a tint of the status colour: the edge band already carries
   status, and colouring the plate too would state it twice and leave the four groups looking
   like four different components rather than one card in four states. */
.stamp { padding: var(--s3) var(--s4); background: var(--sunken); border-radius: var(--r); }

/* Tabular figures because these lines are read down a column of cards and the numbers are
   the content — "10" under "100" should line up on the same stem. */
.offer-text {
  font-family: var(--mono); font-size: 18px; font-weight: 600;
  line-height: 1.35; letter-spacing: -.02em; color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin: 0; overflow-wrap: anywhere;
}
/* The second half of one statement — "20% off" / "on everything from EverGood" — so it sits
   inside the plate rather than under it. Logical properties throughout: a brand or a category
   path can be Arabic, and the paragraph states its own direction. */
.offer-scope {
  font-size: 13px; color: var(--ink-2);
  margin: var(--s1) 0 0; overflow-wrap: anywhere;
}

/* ADR-025 §4: an offer with no catalog wording. The `--alert` family, not `--stop`: this is
   not a broken offer, it is a running one doing half its job, and the team's own screen is
   the only place that fact is visible. It sits under the plate rather than in it, because
   the plate states what the offer *is* and this states what is missing from it. */
.offer-gap {
  margin: var(--s3) 0 0; padding: var(--s3);
  background: var(--alert-soft); border-inline-start: 3px solid var(--alert);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: 13.5px; line-height: 1.5; color: var(--alert-ink);
}

@media (forced-colors: active) {
  .offer-gap { border: 1px solid; }
}

/* What the agent is pointed at, on the offers card's plate but in the sans face: mono and
   tabular figures are for numbers read down a column, and this is a brand or a category
   path. Logical properties and `dir="auto"` because either can be Arabic (ADR-024). */
.focus-target {
  font-size: 18px; font-weight: 600; line-height: 1.35; letter-spacing: -.01em;
  color: var(--ink); margin: 0; overflow-wrap: anywhere;
}
/* The reason the team focused it — the one place in the system it is ever shown, and never
   anywhere the agent can reach. Quiet, because it is a note to a colleague and not part of
   what the card states. */
.focus-note {
  font-size: 13px; color: var(--ink-2); margin: var(--s3) 0 0; overflow-wrap: anywhere;
}

.offer-card .admin-actions { margin-top: var(--s3); }
/* The exact run belongs with the quiet controls: it is the audit fact, and the corner above
   already answers the scanning question of how long is left. */
.offer-run {
  font-family: var(--mono); font-size: 12px; color: var(--ink-3);
  margin-inline-end: auto; white-space: nowrap;
}

/* The authoring form. One column, because it is read top to bottom once and the fields are
   not comparable to each other — a two-column form here would only shorten the page.

   Capped well inside it, though. The desktop rule at the top of this section takes `.page`
   to 1180px for the two-pane review screen, and every other screen inherits it: this form
   was setting its hint paragraphs about 140 characters to the line, which is roughly twice
   a comfortable measure. The list wants the width — it puts cards in two columns. A single
   column of fields does not. */
/* One card, ruled into sections — the shape `.setup-sheet` uses, and the shape every other
   grouping in the product has. This was a bare column of bordered boxes sitting directly on
   the page ground, which is the only place in the portal that happened and the reason the
   screen read as a different application.

   Still one column, and still capped well inside the page. The desktop rule takes `.page` to
   1240px for the two-pane review screen and every other screen inherits it: this form was
   setting its hint paragraphs about 140 characters to the line, roughly twice a comfortable
   measure. The Selling list wants that width because it puts cards in two columns. A single
   column of fields does not — and these fields are not comparable to each other, so a second
   column would only shorten the page at the cost of the reading order. */
.offer-form {
  max-width: 720px;
  padding: var(--s5);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
}
.offer-form label { display: block; margin-bottom: var(--s1); }
.offer-form > .hint { margin-bottom: var(--s4); }
.offer-form .section-head { margin-top: var(--s5); }
.offer-form > input[type=text], .offer-form > textarea { margin-bottom: var(--s1); }
/* The submit sits below the last section's rule with room to be the end of the form. */
.offer-form > button[type=submit] { margin-top: var(--s5); }

/* Every section on the card. A rule above rather than a box around: inside a card, a second
   border is a card inside a card, and the four of them turned one form into four widgets.

   `fieldset` ships a 2px groove, its own padding and a 2px inline margin, all of which are
   zeroed here — what is drawn is the rule and the spacing, nothing the UA chose. `min-width:
   0` because a fieldset's default `min-width: min-content` is what makes a long `<select>`
   or a picker refuse to shrink inside a flex or grid parent. */
.offer-form fieldset {
  border: 0; border-top: 1px solid var(--rule-soft); border-radius: 0;
  padding: var(--s5) 0 0; margin: var(--s5) 0 0; min-width: 0;
}
/* A block, so the legend becomes a heading at the top of its section instead of sitting in a
   border notch — the single most dated thing a form can wear, and on a card there is no
   border left to notch into anyway.

   `display: block` and not `float: left`, which was the first attempt and is the classic
   trick for this: a 100%-wide float leaves no room on its line, so the next shrink-to-fit
   element — "Draft the wording", an inline-flex button — was placed beside it and pushed
   clean outside the card. A block legend needs no clearing from anything after it. */
.offer-form legend { display: block; width: 100%; margin-bottom: var(--s3); }
/* The first control in a section sits under its heading, not a heading-plus-margin away: the
   radio rows carry their own top margin for the gaps *between* them, which doubled up here. */
.offer-form legend + .pick, .offer-form legend + .hint { margin-top: 0; }

/* The stamped register the rest of the product uses for a section label — the same one
   `.section-head` sets, so a heading on this form and a heading on the Selling screen are
   visibly the same kind of thing. */
.scope legend, .wording legend, .announce legend, .counted legend {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-2);
  padding: 0;
}
/* Every scope's own field is visible next to its radio, with no script to show and hide
   them. The form refuses a scope whose field is empty, and drops whatever the others were
   left holding — so what is stored always matches the radio that was picked. */
.scope .pick { display: block; font-weight: 550; margin: var(--s3) 0 var(--s1); }
.scope .pick input { margin-inline-end: var(--s2); }

/* Indented under the radio it belongs to. Flush left and full-bleed, these read as fields of
   the fieldset rather than as the option's own, and the product picker in particular looked
   like a fourth choice instead of the third one's detail. */
.pick-field { margin-inline-start: var(--s6); }
.pick-field select { width: 100%; margin-bottom: var(--s2); }

.picker { margin-top: var(--s2); }
.picked { list-style: none; margin: 0 0 var(--s2); padding: 0; }
.picked li { padding: var(--s1) 0; border-bottom: 1px solid var(--rule); }
.picked li:last-child { border-bottom: 0; }
.picked label { font-weight: 400; margin: 0; }
.picked input[type=checkbox] { margin-inline-end: var(--s2); }
.picked .meta { display: inline; margin-inline-start: var(--s2); }

/* Choosing the mechanic is a step, not a save, so its button is quiet and sits on the select's
   own row — the pair is one control and reads as one. */
.step { margin-bottom: var(--s4); }
.step-row { display: flex; gap: var(--s2); align-items: center; }
.step-row select { flex: 1; min-width: 0; }

/* The chosen mechanic's numbers, on the same plate the list stamps the finished offer onto:
   what is being authored here and what the team will read back are visibly one thing. The
   legend wears the stamp so the echo costs no new shape. */
/* The chosen mechanic's numbers sit on the plate the Selling list stamps the finished offer
   onto: what is being authored here and what the team will read back are visibly one thing.
   The legend wears the stamp, so the echo costs no new shape — and it is the one legend on
   the form that is not the plain section register, deliberately. */
/* No rule above it: it is an inset plate rather than a band of the card, and the hairline
   crossed the top of the fill. */
.params {
  background: var(--surface-muted); border-top: 0; border-radius: var(--r);
  padding: var(--s4); margin-top: var(--s3);
}
.params legend {
  display: inline-block; width: auto;
  font-family: var(--mono); font-size: 12.5px; font-weight: 600; letter-spacing: .02em;
  color: var(--ink-2); background: var(--sunken);
  padding: 2px var(--s2) 3px; border-radius: var(--r-sm);
  margin-bottom: var(--s3);
}
.params > .hint { margin-bottom: var(--s3); }
.params .pick { display: block; font-weight: 550; margin: var(--s3) 0 var(--s1); }
.params .pick input { margin-inline-end: var(--s2); }

/* The `counts_per` question, inside the mechanic's own fieldset. A fieldset for the grouping
   and nothing else: every UA style it ships — the 2px groove, the padding, the 2px inline
   margin — is zeroed, so what is drawn is what the two radios and a paragraph drew before.
   The legend is a question rather than a label, so it is set as the surrounding hints are
   and not as `.scope`'s quiet caption. */
.counted { border-top: 0; padding: 0; margin: var(--s4) 0 0; }
.counted > .hint { margin: var(--s1) 0 var(--s3); }

/* The client-facing pair. It was the one fieldset on this form with no rule of its own,
   which meant the UA drew it: a 2px groove with square corners between `.scope` and
   `.params`, both 1px and rounded. Nothing here is a new decision — it is those two. The
   legend is theirs in the sans face rather than `.params`' struck plate, because what is
   authored inside it is the only speech on the form a client ever reads. */
/* `.offer-form`'s own spacing is a child combinator, and these two fields are a level deeper
   than the ones it was written for, so they were the only inputs on the form sitting flush
   against the hint under them. */
.wording > input[type=text], .wording > textarea { margin-bottom: var(--s1); }

/* The one control on this form that speaks to clients the moment it is saved (ADR-037).
   Borrowed wholesale from `.wording` above rather than given a look of its own — it sits
   directly under it and the two are one thought: here is what the client reads, and here is
   whether they are sent it. What separates them is the orange edge, which is this
   stylesheet's "look before you move" and the only place on the form it appears; a green
   one would read as confirmation and this is not a confirmation, it is a warning that the
   green button below now also sends something. */
/* The one control on this form that speaks to clients the moment it is saved (ADR-037), and
   the only section still drawn as an inset rather than a ruled band. That is the point: what
   separates it from the wording above it is the caution edge, this stylesheet's "look before
   you move", and the only place on the form it appears. A green one would read as
   confirmation, and this is not a confirmation — it is a warning that the green button below
   now also sends something to everybody. */
.announce {
  border-top: 0; border-inline-start: 3px solid var(--alert);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--alert-soft);
  padding: var(--s4); margin: var(--s5) 0 0;
}
.announce legend { color: var(--alert-ink); }
/* `.scope .pick` and `.params .pick` say this twice already; a third selector rather than a
   bare `.pick` because widening it would restyle every checkbox in the picker lists. */
.announce .pick { display: block; font-weight: 550; margin: 0 0 var(--s1); }
.announce .pick input { margin-inline-end: var(--s2); }
/* Whichever of the two paragraphs ends the fieldset — `.warns` today. */
.announce > p:last-child { margin-bottom: 0; }

/* The unit inside the field rather than beside the label: the number and what it is measured
   in are one value, and a "$" two lines up is not there when the eye is on the box. Inert to
   the pointer, so clicking the glyph still lands in the input under it. */
.adorned { position: relative; margin-bottom: var(--s3); }
.adorned input { width: 100%; }
/* A field's width is a claim about how much belongs in it. A quantity and a dollar amount
   are three or four characters; run to the full measure and the box invites a sentence.
   The gift description is the one parameter that really is prose, and it keeps the width. */
.adorned.is-numeric { max-width: 240px; }
.adorned.has-prefix input { padding-inline-start: var(--s7); }
.adorned.has-suffix input { padding-inline-end: var(--s7); }
.adorn {
  position: absolute; top: 0; bottom: 0; display: flex; align-items: center;
  font-family: var(--mono); font-size: 14px; font-weight: 600; color: var(--ink-3);
  pointer-events: none;
}
.adorn-prefix { inset-inline-start: var(--s3); }
.adorn-suffix { inset-inline-end: var(--s3); }

.dates { display: flex; gap: var(--s4); border-top: 0; padding-top: 0; }
.dates label { flex: 1; }
.dates input { width: 100%; }

/* Offers on the order review screen. Amber, not green: this is the caution family's real
   job — look before you move. Somebody has to apply this by hand at invoicing, and the
   internal note is the only place it is written down. */
.order-offers {
  margin: var(--s5) 0;
  padding: var(--s3) var(--s4);
  border-left: 3px solid var(--alert);
  background: var(--alert-soft);
  border-radius: 0 var(--r) var(--r) 0;
}
.order-offers h2 { font-size: 14px; color: var(--alert-ink); margin-bottom: var(--s2); }
.order-offers ul { list-style: none; margin: 0; padding: 0; }
.order-offers li + li { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--rule); }
.order-offers .offer-title { font-weight: 650; margin-bottom: var(--s1); }
.order-offers .offer-note { font-size: 14px; color: var(--ink-2); margin: var(--s1) 0 0; }
/* The TEAM badge in front of the internal note. It carries its own `dir` because `dir="auto"`
   on the paragraph resolves from the first strong character: a Latin badge in front of Arabic
   computed the whole note LTR and threw any trailing Latin run to the wrong end of the line.
   Lives here rather than with the offer list — the note is the only place it is used. */
.offer-lang {
  font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
  color: var(--ink-3); margin-inline-end: var(--s2);
}

/* --- Quoted prices on the order (ADR-020) -------------------------------------------
   The portal showed no money at all until prices shipped; `order-portal-spec.md` said
   "No prices anywhere" and this is the amendment. Deliberately quiet type: this is a
   record of what the client was told, not a figure the screen is asking anyone to act
   on. The decision buttons stay the loudest thing on the page. */
.line-price {
  margin: 2px 0 0; font: 500 13px/1.4 var(--sans); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.line-price-total { margin-left: 8px; font-weight: 600; color: var(--ink); }

.quoted-total {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px;
  margin: var(--s4) 0 0; padding-top: var(--s3); border-top: 1px solid var(--rule);
}
.quoted-total-label { font: 500 13px/1.4 var(--sans); color: var(--ink-2); }
.quoted-total-figure {
  font: 700 20px/1.2 var(--sans); color: var(--ink); font-variant-numeric: tabular-nums;
}
/* A total covering only some of the lines must never read as covering all of them. */
.quoted-total-partial {
  flex-basis: 100%; font: 400 12.5px/1.45 var(--sans); color: var(--ink-3);
}

/* --- Today (the redesign's landing screen) ---------------------------------------------
 *
 * The one screen that answers "what should I do next" instead of "what is in this list".
 * Everything on it is a pointer somewhere else: nothing is decided here, and no control on
 * it changes state. That is deliberate — a dashboard that also acts is a second place to
 * confirm an order, and there is exactly one.
 *
 * Two columns on desktop, one on a phone, and the order matters in the one-column case:
 * what needs a hand comes first, what the agent is running second, the day's trail last.
 */

.counters { display: flex; flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s6); }
.counter {
  flex: 1 1 150px;
  padding: var(--s3) var(--s4) var(--s4);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
}
.counter-label {
  margin: 0 0 var(--s2);
  font: 700 9.5px/1 var(--mono); letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3);
}
/* Tabular figures and a tight stem: these are read across a row, and four counts that do
   not line up are four numbers you compare one at a time. */
.counter-value {
  margin: 0;
  font: 700 30px/1 var(--mono); letter-spacing: -.04em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.counter-note { margin: 6px 0 0; font-size: 12.5px; line-height: 1.35; color: var(--ink-2); }

.today-cols { display: flex; flex-wrap: wrap; gap: var(--s7); align-items: flex-start; }
.today-main { flex: 1 1 480px; min-width: 0; }
.today-side { flex: 1 1 320px; min-width: 0; display: grid; gap: var(--s6); }

/* The heading of a Today block: a stamped label with its count beside it. `.section-head`
   is the same register, so this only adds the count and the baseline alignment. */
.block-head {
  display: flex; align-items: baseline; gap: var(--s2) var(--s3);
  margin: 0 0 var(--s3);
}
.block-head .section-head { margin: 0; }

/* Everything the portal is holding up. Each item wears the signal band for the same reason
   a flagged order card does — it is the same claim, made about a different noun. */
.attention { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3); }
.attention > li {
  position: relative; overflow: hidden;
  padding: var(--s3) var(--s4) var(--s3) calc(var(--s4) + 5px);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
}
.attention > li::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 5px;
  background: var(--signal);
}
.attention-top {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2);
  margin-bottom: 6px;
}
.attention-kind {
  font: 700 9.5px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-2);
}
.attention-where { font: 600 11.5px/1 var(--mono); color: var(--ink-3); }
.attention-what {
  margin: 0 0 var(--s3);
  font-size: 15px; font-weight: 550; line-height: 1.45; letter-spacing: -.01em;
  color: var(--ink); text-wrap: pretty;
}
.attention-do { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.attention-do .btn { min-height: 38px; padding: 0 var(--s3); font-size: 13px; }
.attention-age { font: 400 12px/1.4 var(--mono); color: var(--ink-3); }

/* What the agent is quoting and leaning toward, read-only. Green on the kind because these
   are the things that are *running* — the same meaning `.offer-live` gives its edge. */
.running { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.running > li {
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r);
}
.running-top {
  display: flex; justify-content: space-between; gap: var(--s3);
  align-items: baseline; margin-bottom: 6px;
}
.running-kind {
  font: 700 9.5px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--go);
}
.running-left { font: 400 11.5px/1 var(--mono); color: var(--ink-3); }
.running-terms {
  margin: 0;
  font: 600 15px/1.3 var(--mono); letter-spacing: -.02em;
  color: var(--ink); font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.running-scope { margin: 3px 0 0; font-size: 12.5px; line-height: 1.4; color: var(--ink-2); }

/* The day's decisions, newest first. Rows in one card rather than a card each: this is a
   log, and a log reads as a column of lines. */
.trail-today {
  list-style: none; margin: 0; padding: 0;
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r);
}
.trail-today li {
  display: flex; flex-wrap: wrap; gap: 6px var(--s3); align-items: baseline;
  padding: 11px var(--s3);
  border-bottom: 1px solid var(--rule-soft);
}
.trail-today li:last-child { border-bottom: 0; }
.trail-today strong { font-size: 13.5px; font-weight: 600; line-height: 1.3; }
.trail-today .trail-detail { font-size: 12.5px; line-height: 1.3; color: var(--ink-2); }
.trail-today .when {
  margin-inline-start: auto;
  font: 400 11.5px/1.3 var(--mono); color: var(--ink-3); white-space: nowrap;
}

/* --- Selling: offers and focus, side by side -------------------------------------------
 *
 * One screen because they answer one question — what the agent puts in front of a client.
 * Two columns rather than two tabs: the reason to look at either is usually the other one.
 */

.selling { display: flex; flex-wrap: wrap; gap: var(--s6); align-items: flex-start; }
.selling-col { flex: 1 1 400px; min-width: 0; }
.selling-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: 6px; }
.selling-head h2 { flex: 1 1 auto; font-size: 18px; letter-spacing: -.02em; }
.selling-head .btn { flex: 0 0 auto; min-height: 38px; padding: 0 var(--s3); font-size: 13px; }
.selling-col > .hint { max-width: 56ch; margin-bottom: var(--s4); }
/* Inside a column the cards are one per row: the column is already the narrow axis, and
   the two-up grid the queue screens use would put four cards across a desktop. */
.selling-col .cards { grid-template-columns: 1fr; }
.selling-col .group-head:first-of-type { margin-top: var(--s4); }

/* --- Setup: the door, the team, and who prices whom -------------------------------------
 *
 * Master-detail. The list answers "who may reach the agent", the sheet answers "what do we
 * know about this one" — two questions that used to share a row, with the second folded
 * behind two `<details>` per sender. On this screen the sheet's fields are visible without
 * opening anything, which is what makes the ADR-023 split legible: the two the agent reads
 * and the three he cannot are side by side under their own headings.
 */

.setup { display: flex; flex-wrap: wrap; gap: var(--s6); align-items: flex-start; }
.setup-list { flex: 1 1 300px; min-width: 0; max-width: 400px; }
.setup-sheet {
  flex: 1 1 440px; min-width: 0;
  padding: var(--s5);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg);
}
.setup-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: 6px; }
.setup-head h2 { flex: 1 1 auto; font-size: 18px; letter-spacing: -.02em; }
.setup-head .btn { flex: 0 0 auto; min-height: 36px; padding: 0 var(--s3); font-size: 12.5px; }

.sender-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.sender-pick {
  display: block; width: 100%; text-align: left;
  min-height: 0; padding: 13px var(--s3);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r);
  box-shadow: none; font-weight: 400;
}
.sender-pick:hover { background: var(--surface); border-color: var(--ink); }
/* The selected row is the dark slab, matching the filled button: it is the one thing on
   this column that is *current*, and current is what the ink fill means everywhere here. */
.sender-pick.is-current,
.sender-pick.is-current:hover { background: var(--ink); border-color: var(--ink); }
/* Block, because `.btn` is an inline-flex row and these two would otherwise run together on
   one line — the name and the handle read as a single sentence. */
.sender-pick .sender-name, .sender-pick .meta { display: block; }
.sender-pick .sender-name {
  margin: 0 0 var(--s1);
  font-size: 15px; font-weight: 650; line-height: 1.3; letter-spacing: -.012em;
  color: var(--ink); overflow-wrap: anywhere;
}
.sender-pick .meta { font: 400 11.5px/1.4 var(--mono); color: var(--ink-3); }
.sender-pick.is-current .sender-name { color: var(--surface); }
.sender-pick.is-current .meta { color: var(--rail-ink); }
.sender-pick .flag { margin-top: 7px; }

.sheet-head {
  display: flex; flex-wrap: wrap; gap: var(--s3); align-items: flex-start;
  padding-bottom: var(--s4); margin-bottom: var(--s4);
  border-bottom: 1px solid var(--rule-soft);
}
.sheet-who { flex: 1 1 220px; min-width: 0; }
.sheet-who h2 { font-size: 21px; letter-spacing: -.022em; margin: 0 0 5px; }
.sheet-who .meta { font: 400 12.5px/1.5 var(--mono); color: var(--ink-3); }
.sheet-act { display: flex; gap: var(--s2); flex-wrap: wrap; }
.sheet-cols { display: flex; flex-wrap: wrap; gap: var(--s5); align-items: flex-start; }
.sheet-col { flex: 1 1 260px; min-width: 0; }
.sheet-col h3 { margin: 0 0 var(--s1); }
.sheet-col > .hint { margin-bottom: var(--s3); }

/* The one field on this screen that decides money (ADR-027). Its own plate and its own
   form, so linking a pricelist never looks like a note being tidied. The alert family
   rather than the signal: this is prose that has to be read before it is acted on, and
   `--signal` is a mark for something waiting. */
.money-box {
  margin-top: var(--s4); padding: var(--s4);
  background: var(--surface-muted);
  border: 1px solid var(--rule); border-left: 5px solid var(--alert);
  border-radius: 0 var(--r) var(--r) 0;
}
.money-box h3 { color: var(--alert-ink); margin: 0 0 var(--s2); }
.money-box .hint { max-width: 64ch; }
.money-row { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: flex-end; }
.money-row label { flex: 0 1 170px; margin: 0; }
.money-row input { font-family: var(--mono); font-weight: 600; }
.money-row .btn { flex: 0 0 auto; }

@media (forced-colors: active) {
  .money-box { border: 1px solid; }
}

/* --- The command palette ---------------------------------------------------------------
 *
 * Progressive enhancement, and it has to stay that way. The markup is rendered on every
 * signed-in page with `hidden` on it and every entry is a real `<a href>`, so with
 * JavaScript off the palette simply never opens and nothing is lost — every destination in
 * it is reachable from the rail or a list. `palette.js` filters what is already in the
 * document; there is no search endpoint behind this and there should not be one.
 */

.palette[hidden] { display: none; }
.palette {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 10vh var(--s4) var(--s4);
  background: rgba(20, 19, 16, .42);
  animation: fade .12s ease both;
}
.palette-panel {
  width: 100%; max-width: 620px; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--rule-strong); border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  animation: rise .16s ease both;
}
.palette-field {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--rule-soft);
}
.palette-field .said-label { margin: 0; }
/* Overrides the shared input rule: inside the panel this is the panel's own top edge, and
   a bordered box within a bordered box reads as two fields. */
.palette-field input[type=text] {
  flex: 1; min-width: 0; min-height: 36px; padding: 0;
  border: 0; border-radius: 0; background: transparent;
  font-size: 17px; line-height: 1.3;
}
.palette-field input[type=text]:focus-visible { box-shadow: none; outline: 0; }
.palette-results { max-height: 52vh; overflow-y: auto; padding: var(--s2); }
.palette-group { margin-bottom: 6px; }
.palette-group > p {
  margin: var(--s2) var(--s3) var(--s1);
  font: 700 9.5px/1 var(--mono); letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3);
}
.palette-item {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; min-height: 44px; padding: var(--s2) var(--s3);
  background: transparent; border: 0; border-radius: var(--r-sm); box-shadow: none;
  color: var(--ink); text-align: left; text-decoration: none;
  font-size: 14.5px; font-weight: 550;
}
.palette-item:hover { background: var(--surface-muted); border: 0; }
.palette-tag {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: var(--r-sm);
  background: var(--sunken); color: var(--ink-2);
  font: 700 10.5px/26px var(--mono); text-align: center;
}
.palette-label { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.palette-hint { flex: 0 0 auto; font: 400 12px/1 var(--mono); color: var(--ink-3); }
.palette-empty {
  margin: 0; padding: var(--s6) var(--s3); text-align: center;
  font-size: 14px; line-height: 1.5; color: var(--ink-3);
}

@keyframes fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes rise {
  from { opacity: 0; transform: translateY(8px) }
  to { opacity: 1; transform: none }
}
