/* =============================================================================
   BakiCheck structure and finish — the colour, finish and typeface pass.
   =============================================================================

   Every colour, size, radius and duration here is a token from tokens.css. The
   only literals in this file are media-query thresholds (560 / 999 / 1000 /
   1180 / 1440), because plain CSS cannot hold a breakpoint in a custom
   property, and a handful of geometric ratios (50%, 100%, 45deg).

   WHAT THIS PASS DID AND DID NOT DO
   The structure was signed off: "we finally got the sizing and structuring
   right." So NOTHING here moves a region, changes a grid, a gutter, a padding,
   a row height, a control height or an order of elements. Every diff in this
   file is one of three things: a colour, a finish, or a typeface size/weight
   that LD named himself. If you are reading this to find the layout, it is the
   same layout — read the section headings, they are unchanged.

   THE FINISH, AS RULES YOU CAN CHECK BY SEARCHING THIS FILE
   1. GLOSS LIVES ON BUTTONS, CARDS AND THE ACTIVE MENU PILL. Search for
      --gloss-top: a vertical gradient, a white inner line at the top, a dark
      inner line at the bottom, a small shadow, and 1px of travel on :active.
   2. THE CREDIT ANSWER IS MATTE. Search for "THE MATTE RULE" below: .credit
      forces background-image: none and box-shadow: none at every size. It is
      the one object a distributor must read instantly and correctly.
   3. ONE PRIMARY PER AREA. Search for "SECOND PRIMARY": a second .btn.primary
      inside one action row is demoted to a secondary automatically, so the rule
      holds even when a new state adds a button.
   4. SOLID YELLOW = ACTION, PALE YELLOW = STATUS, ALWAYS. A #FFD43B fill with
      dark text is a button or the active menu pill and nothing else. A
      --warn-bg tint with --warn text is a state and nothing else. Search for
      --btn-primary-bg and --warn-bg: the two never meet in one object.
   5. GREEN IS DONE OR GOOD. RED IS OVERDUE MONEY ONLY, with the single standing
      exception of a system error sentence, which has nowhere else to go.

   MOTION. A 120–160ms fade, a 1px button press and one functional pulse on the
   live mic. No lift on hover, no skeleton shimmer, no ticker. All of it
   collapses under prefers-reduced-motion.
   ============================================================================= */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

/* =========================================================================
   THE ROOT SCALE
   =========================================================================
   LD, on his laptop: "basically how it is scaled. It's oversized and on my
   face." His panel is 1280x720 at a device pixel ratio of 1.5, so the CSS
   viewport is 1280x529 - wide enough, but barely half the height the design
   was drawn at. The dashboard came to 587px against 529px of room.

   Every --fs-* and every --sp-* in tokens.css is already in rem, which means
   ONE declaration scales the type and the spacing together, in proportion,
   with no component touched and no layout re-tuned. That is this rule.

     width term   0.3125vw + 10px   16px at 1920, 14px at 1280
     height term  0.9vh + 9px       about 13.8px at 529, 16px at 780
     min()                          the SHORTER constraint wins, because his
                                    problem is height and not width
     clamp 13-16                    never smaller than 13, never larger than
                                    the 16 he already approved on his monitor

   It is scoped to pointer-width screens on purpose: below 1000px the root
   stays at 16px, so --tap-min and --input-h keep their full 44 and 48 pixels
   on a phone, where a shrunken tap target would be a real defect rather than
   a tidier page.

   No colour is involved, and the side menu is untouched - its type rides the
   same proportional scale as everything else, which is the point. */
@media (min-width: 1000px) {
  :root { font-size: clamp(13px, min(0.3125vw + 10px, 0.9vh + 9px), 16px); }
}

/* THE FLOORS THAT DO NOT SCALE.
   Three sets of tokens in tokens.css carry an explicit instruction in their
   own comments, and a proportional shrink would quietly break all three. They
   are pinned here rather than edited in tokens.css, so that file stays exactly
   as it was approved and the reason for each floor sits beside the rule that
   needs it. Each keeps its rem value as the UPPER half of a max(), so on a
   full size screen nothing changes at all - the px only takes over once the
   root has shrunk past it.

     --fs-indic      "13 - never smaller, never hidden". Devanagari and
                     Gujarati need every pixel of their matras; this is the one
                     size in the product that is a legibility rule, not taste.
     --fs-label      "the floor". It is already the smallest type we allow.
     --fs-credit-*   the credit answer is the product's whole job. It does not
                     get to become small or timid because the window is short. */
:root {
  --fs-indic: max(13px, 0.8125rem);
  --fs-label: max(12px, 0.75rem);
  --fs-credit-pill: max(14px, 0.875rem);
  --fs-credit-word: max(18px, 1.125rem);
  --fs-credit-figure: max(24px, 1.5rem);
}

/* CONTROLS KEEP THEIR SIZE ON EVERY DESK (04m 9.1, D22; 06n BUG11).
   The root scale above shrinks rem, and the control heights are rem, so a
   32px button became 28px at 1280 and a finger on a sideways tablet lost its
   44px. These are px floors in the same max() pattern as the block above.
   Touch first: a finger always gets 44 and 48. Then the mouse block, which
   must come after it, or a mouse would get 44. The rem half still lets a
   larger browser font grow them. tokens.css is not edited. */
:root {
  --ctrl-h: max(44px, 2.75rem);
  --ctrl-h-sm: max(44px, 2.75rem);
  --input-h: max(48px, 3rem);
  --tap-min: max(44px, 2.75rem);
  --chip-h: max(44px, 2.75rem);
}
@media (pointer: fine) {
  :root {
    --ctrl-h: max(32px, 2rem);
    --ctrl-h-sm: max(28px, 1.75rem);
    --input-h: max(32px, 2rem);
    --tap-min: max(32px, 2rem);
    --chip-h: max(28px, 1.75rem);
  }
}
body {
  background: var(--ground);
  color: var(--ink);
  font: var(--t-body);
  font-variant-numeric: var(--num);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img { max-width: 100%; }
[hidden] { display: none !important; }
button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4, p, dl, dd { margin: 0; }

/* Pine is the product's one interactive ink. It is never a fill. */
a { color: var(--pine); text-decoration: none; text-underline-offset: var(--nudge-underline); }
a:hover { text-decoration: underline; }
a:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }

/* an inverted selection: unmistakable, and not a tint */
::selection { background: var(--ink); color: var(--on-ink); }

* { scrollbar-width: thin; scrollbar-color: var(--ring) transparent; }
*::-webkit-scrollbar { width: var(--sp-8); height: var(--sp-8); }
*::-webkit-scrollbar-thumb { background: var(--ring); border-radius: var(--r-4); }
*::-webkit-scrollbar-track { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =========================================================================
   IDENTITY
   =========================================================================
   The mark carries the brand: a green rupee glyph with a gold tick sweeping
   through it, drawn by common.js from --pine (currentColor) and
   --gold-bright. LD's own wordmark sets "Baki" in gold and "Check" in green,
   so the two words carry their own colours everywhere the wordmark appears.
   ON A DARK GREEN FIELD the glyph and "Check" switch to --nav-mark-green, a
   lighter green built for contrast on that field; "Baki" stays the same gold,
   which already reads there because it is the same yellow as the active pill.
   There are three such fields and they are listed once, below: the side menu,
   and the two top bars. Measured on the lightest of them, #305D4A in Eucalyptus
   light, which is the worst case: --nav-mark-green 4.96:1, gold "Baki" 5.28:1.
   Both clear AA for normal text and 4.96:1 is the floor anywhere on a bar. */
.wordmark {
  display: inline-flex; align-items: center; gap: var(--sp-8);
  font: var(--t-wordmark); letter-spacing: var(--ls-mark);
  font-stretch: var(--wd-mark);
  color: var(--ink);
}
.wordmark .baki { color: var(--gold); }
.wordmark .check { color: var(--pine); }
.mark { width: var(--sp-24); height: var(--sp-24); flex: 0 0 var(--sp-24); color: var(--pine); }

/* THE DARK GREEN FIELDS. One list, so a new one cannot be added without the
   wordmark coming with it. */
:is(.sidebar, .app-bar, .mobile-bar) .wordmark { color: var(--nav-ink); }
:is(.sidebar, .app-bar, .mobile-bar) .wordmark .check { color: var(--nav-mark-green); }
:is(.sidebar, .app-bar, .mobile-bar) .mark { color: var(--nav-mark-green); }

/* =========================================================================
   PAGE FRAME
   ========================================================================= */
/* THE TOP BAR. The salesman app, the parser bench and the WhatsApp simulator
   mount this; the owner app's phone strip below is the same thing with a menu
   button instead of a status line, so the two share every colour.
     LD, earlier: "header white, one line under it. Same silhouette as before,
     white instead of the page grey."
     LD, 2 October 2026, reversing that: "Why is the top bar in white? It's the
     same in mobile salesman app as well. Fix it."
   So it is the theme's dark green now, and it takes the SAME tokens the side
   menu takes, --nav-*, rather than a second set of its own. The silhouette LD
   approved is untouched: one line under it, same height, same padding; only
   --header-bg became --nav-bg and --header-edge became --nav-edge. The white
   that was here is gone from the app entirely.
   Every piece of text on a bar is white or 80 percent white, so it goes through
   the rail's own ladder: --nav-ink for anything you must read or tap, --nav-ink-2
   for a supporting fact, --nav-focus for a ring that is never yellow. */
.app-bar {
  background: var(--nav-bg);
  color: var(--nav-ink);
  padding: var(--sp-12) var(--gutter);
  display: flex; align-items: center; gap: var(--sp-16); flex-wrap: wrap;
  position: sticky; top: 0; z-index: var(--z-sticky-bar);
  min-height: var(--bar-h);
  border-bottom: var(--hair) solid var(--nav-edge);
}
.app-bar__meta { font: var(--t-meta); margin-left: auto; text-align: right; color: var(--nav-ink-2); }
.app-bar a { color: var(--nav-ink); }
.app-bar a:focus-visible { outline: var(--focus-ring) solid var(--nav-focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }

/* The page frame scales with the viewport (LD: "I think some of it should be
   scalable automatically depending on the screen size"). Both clamps are
   capped at today's values, so on his monitor nothing moved at all; on a short
   laptop screen the frame gives back the vertical room that was pushing the
   page below the fold. */
.app-shell {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding-inline: clamp(var(--sp-12), 1.2vw, var(--gutter));
  padding-block: clamp(var(--sp-16), 2.6vh, var(--sp-32)) clamp(var(--sp-24), 4vh, var(--sp-40));
}

/* The page headline. On the order page and the store page this IS the store
   name, which is why LD sized it: 26px weight 800 on a pointer, 24 on touch. */
.page-title { font: var(--t-store); letter-spacing: var(--ls-title); margin-bottom: var(--sp-4); text-wrap: balance; }
.page-sub { font: var(--t-meta); color: var(--text-2); margin-bottom: var(--sp-12); max-width: var(--measure-sub); }
.hi { font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num); }
.hi + .hi { margin-top: var(--sp-4); }
.meta { font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num); }

.stack > * + * { margin-top: var(--sp-12); }
.stack-tight > * + * { margin-top: var(--sp-8); }

/* =========================================================================
   THE SIDE MENU — a dark field, white text, one yellow pill
   =========================================================================
   Same width, same rows, same order, same collapse behaviour. Only the colour
   changed: --nav-bg carries the theme's identity (Eucalyptus green, Charcoal
   near-black), the labels are white, inactive labels are white at 80% in
   Eucalyptus and 70% in Charcoal, and the current item is the one yellow pill
   on the screen. The pill is a SOLID yellow fill with dark text, so it obeys
   the same rule every yellow object obeys: solid yellow is an action or the
   place you are, a pale yellow tint is a status. */
.app-frame { display: block; }
.sidebar-menu-btn { display: none; }

/* The owner app's phone strip. It is the same top bar as .app-bar above, so it
   is green on the same tokens: LD's note was that the white bar was wrong "in
   mobile salesman app as well", and leaving one white bar behind in the owner
   app would be the same complaint a day later. On a phone this strip and the
   off canvas rail are now one continuous green field. */
.mobile-bar {
  display: none; align-items: center; gap: var(--sp-12);
  padding: var(--sp-8) var(--gutter); min-height: var(--bar-h);
  background: var(--nav-bg); color: var(--nav-ink);
  border-bottom: var(--hair) solid var(--nav-edge);
  position: sticky; top: 0; z-index: var(--z-sticky-bar);
}
@media (max-width: 999px) { .mobile-bar { display: flex; } }

.sidebar {
  background: var(--nav-bg);
  color: var(--nav-ink);
  display: flex; flex-direction: column; width: var(--sidebar-w);
  border-right: var(--hair) solid var(--nav-bg);
  transition: width var(--dur-med) var(--ease-out);
}
.sidebar__head {
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-16) var(--sp-12); min-height: var(--bar-h);
  overflow: hidden;
}
.sidebar__head .wordmark { flex: 1 1 auto; min-width: 0; overflow: hidden; }
.sidebar__head .wordmark > span:last-child {
  display: inline-block; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}
.sidebar__toggle {
  flex: 0 0 auto; width: var(--ctrl-h); height: var(--ctrl-h); border-radius: var(--r-6);
  border: 0; background: none; color: var(--nav-ink-2);
  display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sidebar__toggle:hover { background: var(--nav-hover); color: var(--nav-ink); }
.sidebar__toggle:focus-visible { outline: var(--focus-ring) solid var(--nav-focus); outline-offset: var(--focus-offset); }
.sidebar__toggle-icon {
  width: var(--icon-nav); height: var(--icon-nav); fill: none; stroke: currentColor;
  stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
  transition: transform var(--dur-med) var(--ease-out);
}

.sidebar__nav { display: flex; flex-direction: column; gap: var(--sp-2); padding: 0 var(--sp-8) var(--sp-8); flex: 1; overflow-y: auto; }
.sidebar__divider { border: 0; border-top: var(--hair) solid var(--nav-edge); margin: var(--sp-8) var(--sp-12); }
.sidebar__links { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-8) var(--sp-8) var(--sp-16); }

.sidebar__footer {
  border-top: var(--hair) solid var(--nav-edge);
  padding: var(--sp-12); display: flex; align-items: center; gap: var(--sp-8);
}
/* connection is chrome, so it is a white dot: filled when connected, a hollow
   ring when not. The words beside it say it in full either way. */
.sidebar__footer-dot {
  width: var(--dot-sm); height: var(--dot-sm); border-radius: 50%; flex: 0 0 auto;
  background: transparent; box-shadow: inset 0 0 0 var(--hair) var(--nav-ink-2);
}
.sidebar__footer-dot.is-on { background: var(--nav-ink); box-shadow: none; }
.sidebar__footer-text { min-width: 0; overflow: hidden; }
.sidebar__footer-name { font: var(--t-meta-strong); color: var(--nav-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar__footer-meta { font: var(--t-label); color: var(--nav-ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar.is-collapsed .sidebar__footer { justify-content: center; padding-inline: var(--sp-8); }
.sidebar.is-collapsed .sidebar__footer-text { display: none; }

.sidebar__item {
  display: flex; align-items: center; gap: var(--sp-8); min-height: var(--nav-h);
  border: 0; background: none; color: var(--nav-ink-2);
  font: var(--t-meta-strong); text-decoration: none;
  cursor: pointer; padding: 0 var(--sp-12); border-radius: var(--r-pill); width: 100%; text-align: left;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
  position: relative;
}
.sidebar__item:hover { color: var(--nav-ink); background-color: var(--nav-hover); }
.sidebar__item:focus-visible { outline: var(--focus-ring) solid var(--nav-focus); outline-offset: calc(var(--focus-offset) * -1); }
/* THE ACTIVE PILL. A solid yellow fill with dark text, a gradient one shade
   lighter at the top and the same white/dark inner lines every raised object
   in this product carries. It is the only fill in the menu. */
.sidebar__item[aria-current="true"] {
  color: var(--nav-active-ink); font-weight: 700;
  background-color: var(--nav-active-bg);
  background-image: linear-gradient(to bottom, var(--nav-active-top), var(--nav-active-bg));
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top), inset 0 calc(var(--hair) * -1) 0 var(--gloss-bottom);
}
.sidebar__item[aria-current="true"]:hover { color: var(--nav-active-ink); background-color: var(--nav-active-bg); }
.sidebar__item-icon {
  width: var(--icon-nav); height: var(--icon-nav); flex: 0 0 var(--icon-nav); fill: none;
  stroke: currentColor; stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
}
.sidebar__item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* a subsection is indented, not hung off a guide rule: one less line on screen */
.sidebar__subnav { display: flex; flex-direction: column; gap: var(--sp-2); margin: var(--sp-2) 0 var(--sp-8); }
.sidebar__subnav[hidden] { display: none; }
.sidebar__item--sub { padding-left: calc(var(--sp-12) + var(--sp-24)); font-weight: 400; }
.sidebar__item--sub[aria-current="true"] { font-weight: 600; }

.sidebar__item-caret {
  flex: 0 0 var(--icon-nav); width: var(--icon-nav); height: var(--icon-nav);
  fill: none; stroke: currentColor; stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
  transform: rotate(180deg); transition: transform var(--dur-fast) var(--ease-out);
}
.sidebar__item[aria-expanded="true"] .sidebar__item-caret { transform: rotate(-90deg); }

/* THE COUNT. Expanded it is a plain right-aligned tabular figure — no capsule,
   no gold, nothing to go out of sync with, because there is no longer a shape
   to size. Collapsed the label is gone, so the same figure becomes a 16px Ink
   dot on the icon: the one place other than the credit answer where a fully
   round shape is allowed (rule R2). The button's own aria-label carries the
   number in words in both states. */
.sidebar__item-count {
  flex: 0 0 auto; margin-left: auto;
  font: var(--t-label); font-variant-numeric: var(--num);
  font-stretch: var(--wd-num);
  color: var(--nav-ink-2); white-space: nowrap;
}
.sidebar__item[aria-current="true"] .sidebar__item-count { color: var(--nav-active-ink); font-weight: 700; }

.sidebar.is-collapsed { width: var(--sidebar-w-collapsed); }
.sidebar.is-collapsed .sidebar__head {
  flex-direction: column; justify-content: center; min-height: 0;
  padding: var(--sp-12) var(--sp-8); gap: var(--sp-8);
}
.sidebar.is-collapsed .sidebar__head .wordmark { flex: 0 0 auto; }
.sidebar.is-collapsed .sidebar__head .wordmark > span:last-child { display: none; }
.sidebar.is-collapsed .sidebar__item-label { display: none; }
.sidebar.is-collapsed .sidebar__item { justify-content: center; padding: 0; }
.sidebar.is-collapsed .sidebar__item-caret { display: none; }
.sidebar.is-collapsed .sidebar__subnav { display: none; }
.sidebar.is-collapsed .sidebar__item-count {
  position: absolute; top: 0; right: var(--sp-8); margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  height: var(--badge-h); min-width: var(--badge-h); padding: 0 var(--sp-4);
  border-radius: var(--r-pill);
  /* white on the menu, never yellow: a count is a number, not an action, and
     solid yellow is reserved for things you press or the place you are */
  background: var(--nav-ink); color: var(--nav-bg);
  font-size: var(--badge-fs); font-weight: 700; line-height: 1;
  box-shadow: 0 0 0 var(--hair-strong) var(--nav-bg);
}
.sidebar.is-collapsed .sidebar__toggle-icon { transform: rotate(180deg); }

@media (min-width: 1000px) {
  .app-frame { display: flex; align-items: flex-start; }
  .sidebar { position: sticky; top: 0; height: 100vh; flex: 0 0 auto; }
  .app-shell { flex: 1 1 auto; min-width: 0; }
}

@media (max-width: 999px) {
  .sidebar-menu-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--tap-min); height: var(--tap-min); border-radius: var(--r-6);
    border: var(--hair) solid var(--control-edge); color: var(--ink);
    background-color: var(--paper);
    background-image: linear-gradient(to bottom, var(--ctrl-top), var(--ctrl-bottom));
    box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top),
                inset 0 calc(var(--hair) * -1) 0 var(--ctrl-gloss-bottom),
                var(--sh-btn);
    cursor: pointer; padding: 0;
  }
  .sidebar-menu-btn:active { transform: translateY(var(--hair)); box-shadow: var(--sh-btn-active); }
  /* the ring lands on the green bar outside the white button, so it is the
     bar's white ring. The green --focus ring would have been green on green. */
  .sidebar-menu-btn:focus-visible { outline: var(--focus-ring) solid var(--nav-focus); outline-offset: var(--focus-offset); }
  .sidebar-menu-btn svg {
    width: var(--icon-nav); height: var(--icon-nav); fill: none; stroke: currentColor;
    stroke-width: var(--stroke-icon); stroke-linecap: round;
  }

  /* off canvas, so it is an overlay, so it is the one place the rail gets the
     shadow token */
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: var(--z-scrim);
    width: min(80vw, var(--sidebar-w-phone)); box-shadow: var(--sh-overlay);
    background: var(--nav-bg);
    transform: translateX(-100%); transition: transform var(--dur-med) var(--ease-out);
  }
  .sidebar.is-collapsed { width: min(80vw, var(--sidebar-w-phone)); }
  .sidebar.is-collapsed .sidebar__item-label { display: block; }
  .sidebar.is-collapsed .sidebar__item-caret { display: block; }
  .sidebar.is-collapsed .sidebar__item-count {
    position: static; margin-left: auto; height: auto; min-width: 0; padding: 0;
    background: none; color: var(--nav-ink-2); font-size: var(--fs-label);
    box-shadow: none; border-radius: 0;
  }
  .sidebar.is-collapsed .sidebar__item { justify-content: flex-start; padding: 0 var(--sp-12); }
  .sidebar.is-collapsed .sidebar__subnav { display: flex; }
  .sidebar.is-collapsed .sidebar__head { flex-direction: row; padding: var(--sp-16) var(--sp-12); }
  .sidebar.is-collapsed .sidebar__head .wordmark > span:last-child { display: inline-block; }
  .sidebar__toggle { display: none; }

  .sidebar-scrim {
    position: fixed; inset: 0; background: rgba(var(--scrim-rgb), .45); z-index: calc(var(--z-scrim) - 1);
    backdrop-filter: blur(var(--blur-scrim));
  }
  body.is-sidebar-open .sidebar { transform: translateX(0); }
}

/* =========================================================================
   THE TWO COLUMN WORKSPACE
   ========================================================================= */
.workspace { display: block; }
.workspace__list { min-width: 0; }
.workspace__detail { min-width: 0; container-type: inline-size; container-name: detail; }
.btn.detail-back { display: inline-flex; margin-bottom: var(--sp-16); }

@keyframes pane-in { from { opacity: 0; } to { opacity: 1; } }
.workspace__detail > * { animation: pane-in var(--dur-med) var(--ease-out) both; }

@media (min-width: 1000px) {
  .workspace {
    display: grid;
    grid-template-columns: var(--list-col) minmax(0, 1fr);
    gap: var(--sp-24);
    align-items: start;
  }
  .workspace__list {
    position: sticky;
    top: var(--sp-24);
    max-height: calc(100vh - var(--sp-40));
    overflow-y: auto;
    padding-right: var(--sp-8);
  }
  .btn.detail-back { display: none; }
  /* Schemes: the row IS the offer, read as a sentence (LD: "he reads the
     offer as a sentence"), so the list needs real width, not the name-sized
     column every other list gets. Same page, same shell, same detail pane
     mechanics - only this one column is wider. */
  .workspace--wide-list { grid-template-columns: var(--list-col-wide) minmax(0, 1fr); }
}
@media (max-width: 999px) {
  body.is-detail-open .workspace__list { display: none; }
  body:not(.is-detail-open) .workspace__detail { display: none; }
}

/* =========================================================================
   LIST PAGE AND DETAIL PAGE (03n F8, 04m W07 to W10)
   =========================================================================
   Orders and Stores show ONE of the two panes at every width: the list the
   width of the content, or the record the width of the content. The body
   class `is-detail-open` says which, the same class a phone already used. The
   sticky, scrolling list column of the two pane layout is switched off here,
   so the page scrolls as a page and the browser's back button has something
   to come back to. Products, Schemes and Setup keep their two panes. */
.workspace--pages { display: block; }
@media (min-width: 1000px) {
  .workspace--pages { display: block; }
  .workspace--pages .workspace__list {
    position: static; max-height: none; overflow: visible; padding-right: 0;
  }
}
body.is-detail-open .workspace--pages .workspace__list { display: none; }
body:not(.is-detail-open) .workspace--pages .workspace__detail { display: none; }

/* The header row of a list page: the title left, its one control right. */
.list-head--bar {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-16); flex-wrap: wrap;
}
.list-head--bar .page-title { margin-bottom: var(--sp-4); }
.list-head--bar .stores-toolbar { margin: 0; }
.list-summary { font: var(--t-meta-strong); color: var(--ink); font-variant-numeric: var(--num); }
.list-notice { font: var(--t-body); color: var(--ink); margin: var(--sp-8) 0 var(--sp-12); }

/* The way back: a crumb on a desktop, one back link on a phone (5.9). */
.pagenav { margin-bottom: var(--sp-8); }
.pagenav__back {
  display: none; align-items: center; min-height: var(--tap-min);
  border: 0; background: none; padding: 0; margin: 0 0 var(--sp-4); cursor: pointer;
  font: var(--t-button); color: var(--pine);
}
.pagenav__back:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
@media (max-width: 999px) {
  .pagenav .pagenav__crumb { display: none; }
  .pagenav .pagenav__back { display: inline-flex; }
}
.order-bill-link { margin: var(--sp-8) 0 0; }

/* On a desktop the row is a row of columns (5.8): the name and its greys, the
   credit badge in a column of its own, the amount, a quiet chevron. Under
   1000 px the G1 to G3 stack is untouched. */
@media (min-width: 1000px) {
  .workspace--pages .list-rows--cards .row:not(.row--static) {
    display: grid; grid-template-columns: minmax(0, 1fr) 11rem 8rem var(--sp-16);
    align-items: center; column-gap: var(--sp-16); position: relative;
  }
  .workspace--pages .list-rows--cards .row:not(.row--static) .row__head,
  .workspace--pages .list-rows--cards .row:not(.row--static) .row__sub { display: contents; }
  .workspace--pages .list-rows--cards .row:not(.row--static) .row__name { grid-column: 1; grid-row: 1; }
  .workspace--pages .list-rows--cards .row:not(.row--static) .row__greys { grid-column: 1; grid-row: 2; width: auto; }
  .workspace--pages .list-rows--cards .row:not(.row--static) .credit { grid-column: 2; grid-row: 1 / span 2; justify-self: center; }
  .workspace--pages .list-rows--cards .row:not(.row--static) .row__money { grid-column: 3; grid-row: 1 / span 2; align-self: center; }
  .workspace--pages .list-rows--cards .row:not(.row--static)::after {
    content: '›'; grid-column: 4; grid-row: 1 / span 2; justify-self: end;
    color: var(--text-3); font: var(--t-body); line-height: 1;
  }
}

/* =========================================================================
   LIST ROWS — one shape, shared by Orders and Stores
   =========================================================================
   One white container with a hairline edge, rows inside it separated by one
   hairline and nothing else: no gap, no radius, no shadow, no card. The name
   reads first at 14/600, the credit pill sits beside it at 24px, the money is
   right aligned in tabular Ink, and everything else folds into one 13px meta
   line. That is what turns eight visible orders into thirteen. */
.list-head { margin-bottom: var(--sp-12); }
/* Step 1 moved .page-sub out of .list-head so phone views sit under the title
   at a fixed Y. That left an extra ~8 px (list-head's margin) above the
   subtitle at 1280. When the head is only the title, drop its margin and let
   .page-title's own 4 px bridge to .page-sub. Phones keep the previous 12 px
   gap under the title (the Store views sit on that gap). */
@media (min-width: 1000px) {
  .list-head:has(> .page-title:last-child) { margin-bottom: 0; }
}

/* A named subsection inside one list page. Overdue credit is drawn as two of
   these - Overdue by amount and Overdue by period - because a store can be in
   both and he needs to see which is which (LD). The heading is the same
   15/700 as a card title so it reads as a section and not as a page, and the
   count sits beside it in meta type so the two never level out. */
.list-section + .list-section { margin-top: var(--gap-section); }
.list-section__head { display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap; }
.list-section__title {
  font: var(--t-card-title); letter-spacing: var(--ls-normal); color: var(--ink); margin: 0;
}
.list-section__count {
  font: var(--t-meta-strong); color: var(--text-2); font-variant-numeric: var(--num);
}
.list-section__sub {
  font: var(--t-meta); color: var(--text-2); margin: var(--sp-2) 0 var(--sp-8);
  max-width: var(--measure-sub);
}
.list-rows {
  display: flex; flex-direction: column; gap: 0;
  background: var(--paper);
  border: var(--hair) solid var(--hairline);
  border-radius: var(--r-12);
  overflow: hidden;
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}

.row {
  display: block; width: 100%; text-align: left;
  background: var(--paper); color: var(--ink);
  border: 0; border-top: var(--hair) solid var(--hairline); border-radius: 0;
  padding: var(--row-pad-y) var(--row-pad-x);
  cursor: pointer; min-height: var(--row-h);
  transition: background var(--dur-fast) var(--ease-out);
}
.row:first-child { border-top: 0; }
.row:hover { background: var(--hover); }
.row:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-offset) * -1); }
.row[aria-current="true"] { background: var(--selected); }
.row--static { cursor: default; }
.row--static:hover { background: var(--paper); }

/* Soft edged cards instead of table rows (LD: "make the cards with soft
   edges. This looks like a table."). Same row markup and content, just
   framed as separated cards with a little air between them rather than a
   hairline-divided table strip. Colour and shadow stay for Picasso to set;
   this only changes the shape. */
.list-rows--cards {
  gap: var(--sp-8); background: transparent; border: 0; border-radius: 0; overflow: visible;
  box-shadow: none;
}
.list-rows--cards .row {
  border: var(--hair) solid var(--card-edge); border-radius: var(--r-12); border-top: var(--hair) solid var(--card-edge);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}

.row__head { display: flex; align-items: flex-start; gap: var(--sp-8); }
.row__top { display: flex; align-items: center; gap: var(--sp-8); }
.row__head .credit { align-self: center; flex: 0 1 auto; min-width: 0; }
/* the credit chip's new home: a second line, beside the meta text rather
   than crowding the name on the first (see listRow in owner.js) */
.row__sub { display: flex; align-items: center; gap: var(--sp-8); margin-top: var(--sp-2); min-width: 0; }
.row__sub .credit { flex: 0 0 auto; }
.row__sub .row__greys { margin-top: 0; flex: 1 1 auto; min-width: 0; }
.row__sub .row__meta { margin-top: 0; }
/* MONEY IS GREEN. LD: "links and prices #2F5E4A" in Eucalyptus, #0F5A3D in
   Charcoal — one ink for the two things a distributor reaches for. It never
   collides with the credit answer, which is a tinted pill with an icon and a
   word, never a bare figure. */
.row__side { font: var(--t-money); color: var(--price); white-space: nowrap; font-variant-numeric: var(--num); }
.row__name {
  font: var(--t-row-name); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Schemes only: the name is a full sentence (LD: "he reads the offer as a
   sentence"), never a short label like a shop or item name, so a single
   truncated line loses the offer entirely. The sentence wraps in full and
   is never cut: a line clamp with overflow visible painted the fourth line
   over the scheme code (BUG-R37), and the dates, which come last, were the
   part lost. */
.row__name--wrap {
  white-space: normal; overflow: visible; text-overflow: clip;
  display: block; overflow-wrap: anywhere;
}
.row__top--wrap { align-items: flex-start; }
.row__top--wrap .state-tag { margin-top: var(--sp-2); flex: 0 0 auto; }
/* money column: amount, then optional "of ₹limit" / "N items" under it (G1) */
.row__money {
  flex: 0 0 auto; min-width: var(--amount-col); text-align: right;
  display: flex; flex-direction: column; align-items: flex-end; gap: 0;
}
.row__amount {
  font: var(--t-money); color: var(--price); white-space: nowrap;
  font-variant-numeric: var(--num); font-stretch: var(--wd-num);
}
.row__amount-under {
  font: var(--t-meta); color: var(--text-2); white-space: nowrap;
  font-variant-numeric: var(--num); font-stretch: var(--wd-num);
}
.row__greys { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0; }
.row__greys .row__meta { margin-top: 0; }
/* Owner list cards (G1/G3): badge under the greys at every width, so badges
   line up down the list on desktop the same as on the phone. Salesman
   .store-card keeps the older side-by-side chip + meta. */
.list-rows--cards .row:not(.store-card) .row__sub {
  flex-direction: column; align-items: flex-start; gap: var(--sp-6);
}
.list-rows--cards .row:not(.store-card) .row__greys { width: 100%; }
.row__meta {
  font: var(--t-meta); color: var(--text-2); margin-top: var(--sp-2);
  font-variant-numeric: var(--num);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* the one thing worth calling out reads first on the meta line, in Ink. Never
   amber, never a chip of its own. */
.row__flag { color: var(--ink); font-weight: 600; }
.row__actions { display: flex; gap: var(--sp-8); flex-wrap: wrap; margin-top: var(--sp-12); }

/* =========================================================================
   CHIPS AND TAGS — rounded pills
   =========================================================================
   LD: "badges are rounded pills". Same size, same padding, same row: only the
   corner changed. A filter chip carries no dot, because a dot in this product
   means a state and a filter is a control. */
.chip {
  display: inline-flex; align-items: center; white-space: nowrap;
  font: var(--t-meta-strong); border-radius: var(--r-pill);
  padding: var(--sp-2) var(--sp-8); min-height: var(--chip-h);
  background: var(--paper); border: var(--hair) solid var(--hairline); color: var(--ink);
}
.chip--flag, .chip--brand { border-color: var(--control-edge); color: var(--ink); }

.chip-row { display: flex; flex-wrap: wrap; gap: var(--sp-8); margin: var(--sp-8) 0 var(--sp-12); }
button.chip.chip--filter {
  cursor: pointer; min-height: var(--chip-h); padding: 0 var(--sp-12);
  font-variant-numeric: var(--num); border-color: var(--control-edge);
  background-image: linear-gradient(to bottom, var(--ctrl-top), var(--ctrl-bottom));
  box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top),
              inset 0 calc(var(--hair) * -1) 0 var(--ctrl-gloss-bottom);
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
button.chip.chip--filter:hover {
  background-color: var(--hover);
  background-image: linear-gradient(to bottom, var(--ctrl-hover-top), var(--ctrl-hover-bottom));
  border-color: var(--ink);
}
/* pressed is Ink on Paper text: never yellow, because a filter is not the one
   primary action on the page, and never a credit tint */
button.chip.chip--filter[aria-pressed="true"] {
  background-color: var(--ink); background-image: none;
  border-color: var(--ink); color: var(--on-ink);
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top);
}
button.chip.chip--filter:active { transform: translateY(var(--hair)); }
button.chip.chip--filter:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* =========================================================================
   CARDS — 12px corners, a 1px edge, a top highlight, a soft wide shadow
   =========================================================================
   Same padding, same margin, same stacking. LD's finish: "12px corners, 1px
   border, a thin white highlight on the top edge, a soft wide shadow below."
   The highlight is an inset 1px line, so it costs no height and nothing on the
   page moves by a pixel. */
.card {
  position: relative;
  background: var(--paper); border: var(--hair) solid var(--card-edge); border-radius: var(--r-12);
  padding: var(--pad-card-main); margin-bottom: var(--gap-card);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}
.card.clickable {
  width: 100%; text-align: left; display: block; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.card.clickable:hover { background-color: var(--hover); }
.card.clickable:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* THE RED TOP EDGE. LD: a 4px bar across the top of the credit card. It is
   drawn on any card that is carrying an OVERDUE credit answer, and on nothing
   else, because red in this product means overdue money and nothing else. It
   is an overlay, not a border, so adding it never changes the card's height. */
.card:has(.credit.stop)::before {
  content: ''; position: absolute; z-index: 1;
  top: calc(var(--hair) * -1); left: calc(var(--hair) * -1); right: calc(var(--hair) * -1);
  height: var(--edge-alert); background: var(--edge-overdue);
  border-radius: var(--r-12) var(--r-12) 0 0;
}

/* an h2 card title titles a whole screen or pane */
.card__title { font: var(--t-title); letter-spacing: var(--ls-title); margin-bottom: var(--sp-12); text-wrap: balance; }

/* an h3 card title names a section. LD: card titles 15px weight 700, sentence
   case. The row name beside it stays 14/600, so the two never level out. */
h3.card__title {
  font: var(--t-card-title); letter-spacing: var(--ls-normal); text-transform: none;
  color: var(--ink); margin: var(--gap-section) 0 var(--sp-12);
}
h3.card__title:first-child { margin-top: 0; }
h3.card__title.is-primary { font: var(--t-card-title); color: var(--ink); }
h3.card__title.is-primary:first-child { margin-top: 0; }

.card > h2:not([class]), .card > h3:not([class]) {
  font: var(--t-card-title); color: var(--ink); margin-bottom: var(--sp-8);
}
.card > h2:not([class]) { font: var(--t-title); letter-spacing: var(--ls-title); margin-bottom: var(--sp-12); }
.card > p:not([class]) { color: var(--text-2); font: var(--t-body); }
.card > p:not([class]) + * { margin-top: var(--sp-12); }

/* the store page's own header. The name here IS the store name, so it takes the
   store name type, exactly like the order page's h1. */
.card-top { display: flex; align-items: flex-start; gap: var(--sp-16); margin-bottom: var(--sp-16); }
.card-top h2, .card-top h3 { font: var(--t-store); letter-spacing: var(--ls-title); text-wrap: balance; }
.card-top .meta { margin-top: var(--sp-4); }
.card-top .right {
  margin-left: auto; text-align: right; white-space: nowrap;
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
}

/* =========================================================================
   THE CREDIT ANSWER — the only tinted object in the product
   =========================================================================
   One shape at three sizes, not four shapes across four states. The radius no
   longer changes with the tone; the hazard stripe is gone. What separates the
   four tones is what survives a greyscale print:
     Clear   filled circle with a tick,   1px edge
     Amber   filled triangle with a bar,  1px edge
     Red     filled octagon with a bar,   2px edge  (the state a man must not miss)
     Grey    dashed ring with a question, dashed edge
   The icons are drawn filled by common.js and are the only filled icons in the
   product (rule R3). The four *-bg tokens below are the only tinted fills in
   this file (rule R1). --r-pill appears here and on the collapsed nav count
   and nowhere else (rule R2). */

/* THE MATTE RULE. Everything else in this product got a gradient, a highlight
   or a shadow in this pass. The credit answer got none of them, on purpose: it
   is the one thing a distributor has to read instantly and correctly, and light
   playing across it is decoration on the wrong element. These four lines are
   deliberate and are not to be "tidied up" later. */
.credit,
.credit.big,
.credit.compact,
.credit.pill,
.credit.block {
  background-image: none;
  box-shadow: none;
  text-shadow: none;
  filter: none;
}

/* SIZE 1 — the pill. List rows and beside the order title. */
.credit {
  display: inline-flex; align-items: center; gap: var(--sp-4);
  min-height: var(--credit-pill-h); max-width: 100%;
  padding: 0 var(--sp-8) 0 var(--sp-4);
  border: var(--hair) solid currentColor; border-radius: var(--r-pill);
  font: var(--t-credit-pill); white-space: nowrap; margin: 0;
}

/* A COLLISION, FIXED. common.js gives the badge the classes
   "credit <tone> pill", so the bills table's .pill rule below was landing on
   the credit badge too and stripping its padding, its border and its corners,
   then adding a stray dot in front of its icon. These four lines restore the
   pill the .credit block above always intended. Nothing moved on the page: the
   badge simply gets back the 8px of padding and the round end it was written
   with. The dot is suppressed because this badge already carries an ICON, which
   is the stronger carrier and survives greyscale. */
.credit.pill {
  padding: 0 var(--sp-8) 0 var(--sp-4);
  border: var(--hair) solid currentColor;
  border-radius: var(--r-pill);
  gap: var(--sp-4);
  font: var(--t-credit-pill);
  min-height: var(--credit-pill-h);
}
.credit.pill::before { content: none; display: none; }
.credit.stop.pill { border-width: var(--hair-strong); }
.credit .icon { flex: 0 0 var(--icon-credit-pill); width: var(--icon-credit-pill); height: var(--icon-credit-pill); }
.credit .b { display: flex; align-items: center; gap: var(--sp-8); min-width: 0; }
.credit .word { font: var(--t-credit-pill); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.credit .figure { font: var(--t-credit-pill); font-variant-numeric: var(--num); flex: 0 0 auto; }
.credit .hindi, .credit .stamp { display: none; }

.credit.ok    { background: var(--ok-bg);    color: var(--ok); }
.credit.warn  { background: var(--warn-bg);  color: var(--warn); }
.credit.stop  { background: var(--stop-bg);  color: var(--stop); border-width: var(--hair-strong); }
.credit.muted { background: var(--muted-bg); color: var(--muted); border-style: dashed; }

/* SIZE 2 — the panel. The shop detail, the over-limit sheet, the salesman's
   answer screen. It is the one place in the product exempt from the small type
   rule: a 16/650 word and a 20/650 figure, because this is the product. */
/* The two live lines under the "Days you give" choice in Setup. They restate
   the number he has just tapped as a date he can picture, and then the real
   cost of the choice counted from his own book, BEFORE he saves. Italic and
   quiet, because they are the product talking back rather than a label; the
   cost line sits on the ink colour because it is the consequential one.
   Tokens only, both themes, no new colour. */
.days-live {
  margin: var(--sp-8) 0 0; font: var(--t-meta); color: var(--text-2);
  font-style: italic; font-variant-numeric: var(--num);
}
.days-live--cost { font: var(--t-meta-strong); color: var(--ink); font-style: italic; font-variant-numeric: var(--num); }

/* The arithmetic under the credit answer, shown only when a store is past the
   days it was given: "Bill INV-0412, 3 September. You gave 14 days. Today is
   day 28." Pulled up tight against the card it explains - it belongs to that
   card, not to the quick actions below it - then carrying the section gap on
   its own bottom edge. Tokens only; no colour and no new size. */
.credit-sum {
  margin: calc(var(--sp-8) - var(--gap-section)) 0 var(--gap-section);
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
}

.credit.big,
.workspace__detail .credit.compact {
  display: flex; align-items: flex-start; gap: var(--sp-12);
  width: 100%; white-space: normal;
  min-height: 0; padding: var(--sp-12) var(--sp-16);
  border-radius: var(--r-8);
  margin: 0 0 var(--gap-section);
}
.credit.big .icon,
.workspace__detail .credit.compact .icon {
  flex: 0 0 var(--icon-credit-panel); width: var(--icon-credit-panel); height: var(--icon-credit-panel);
  margin-top: var(--sp-2);
}
.credit.big .b,
.workspace__detail .credit.compact .b { display: block; flex: 1 1 auto; }
.credit.big .word,
.workspace__detail .credit.compact .word {
  font: var(--t-credit-word); font-weight: var(--fw-heavy); letter-spacing: var(--ls-title);
  text-wrap: balance; overflow: visible; text-overflow: clip;
}
/* the second language line is never the casualty: always present, always 13px,
   never shrunk to make room for the Latin */
.credit.big .hindi,
.workspace__detail .credit.compact .hindi {
  display: block; font: var(--t-indic); color: var(--text-2); margin-top: var(--sp-2);
}
.credit.big .figure,
.workspace__detail .credit.compact .figure {
  display: block; font: var(--t-credit-fig); font-weight: var(--fw-heavy);
  letter-spacing: var(--ls-figure);
  font-variant-numeric: var(--num); font-stretch: var(--wd-num); margin-top: var(--sp-8);
}
.credit.big .stamp,
.workspace__detail .credit.compact .stamp {
  display: block; font: var(--t-meta); color: var(--text-2); margin-top: var(--sp-4);
}

/* the compact badge in a salesman list row stays the pill */
.row .credit.compact { margin-top: var(--sp-8); }

/* SIZE 1b — THE BLOCK. The order page's credit strip and nothing else.
   LD: "that overdue banner is for some reason too long. Can you just make it a
   little broad and make it a little short." It was one pill running most of the
   page: Overdue, then the outstanding, the limit, the overshoot and the bill
   age all on one unbroken line. Same facts, same tone, same icon, same matte -
   the word and the headline figure lead on their own row, the rest wraps under
   them, and the whole thing is capped at 22rem so it is narrower and two or
   three lines tall instead of a thin strip. The parts come from the server
   already split (credit.figureHead / figureRest), which are joined back into
   figureLine by credit.js itself, so nothing in it can go missing. */
.credit.block {
  display: flex; align-items: flex-start; gap: var(--sp-8);
  width: auto; max-width: min(100%, 22rem); white-space: normal;
  min-height: 0; padding: var(--sp-8) var(--sp-12);
  border: var(--hair) solid currentColor; border-radius: var(--r-8);
  margin: 0;
}
.credit.stop.block { border-width: var(--hair-strong); }
.credit.block .icon {
  flex: 0 0 var(--icon-credit-panel); width: var(--icon-credit-panel); height: var(--icon-credit-panel);
  margin-top: var(--sp-2);
}
.credit.block .b { display: block; flex: 1 1 auto; min-width: 0; }
.credit.block .credit__head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-4) var(--sp-8); min-width: 0;
}
.credit.block .word {
  font: var(--t-credit-word); font-weight: var(--fw-heavy); letter-spacing: var(--ls-title);
  overflow: visible; text-overflow: clip; min-width: 0;
}
.credit.block .figure {
  font: var(--t-credit-fig); font-weight: var(--fw-heavy); letter-spacing: var(--ls-figure);
  font-variant-numeric: var(--num); font-stretch: var(--wd-num); flex: 0 1 auto;
}
/* the supporting facts, beneath, separated by the same middle dot the one line
   form used — so the strip reads as the same sentence, only folded */
.credit.block .credit__rest {
  display: flex; flex-wrap: wrap; gap: 0 var(--sp-8);
  font: var(--t-meta-strong); font-variant-numeric: var(--num); margin-top: var(--sp-2);
}
.credit.block .credit__rest > span + span::before { content: '· '; }

/* SIZE 1c — THE FOUR PART BLOCK (03n F2, 04m 5.5). The order page's block,
   the store page's panel and the override dialog's big block: the shape with
   the word, the Hindi line, one figure, one sentence, and nothing else. The
   icon sits inline with the word, so the four parts fit in five lines at 360.
   Matte, tone background with tone text, 2 px edge on red (K3), 22rem wide at
   most except in a dialog. No colour here that the tones above do not set. */
.credit.credit--four {
  display: block; white-space: normal; min-height: 0;
  width: auto; max-width: min(100%, 22rem); margin: 0;
  padding: var(--sp-12); border: var(--hair) solid currentColor; border-radius: var(--r-8);
}
.credit.credit--four.big { max-width: none; width: 100%; margin: 0 0 var(--gap-section); }
.credit.stop.credit--four { border-width: var(--hair-strong); }
.credit.muted.credit--four { border-style: dashed; }
.credit--four .credit__line { display: flex; align-items: center; gap: var(--sp-8); min-width: 0; }
.credit--four .icon {
  flex: 0 0 var(--icon-credit-panel); width: var(--icon-credit-panel); height: var(--icon-credit-panel); margin: 0;
}
.credit--four .word {
  font: var(--t-credit-word); font-weight: var(--fw-heavy); letter-spacing: var(--ls-title);
  overflow: visible; text-overflow: clip; white-space: normal; min-width: 0;
}
.credit--four .hindi { display: block; font: var(--t-indic); color: var(--text-2); margin-top: var(--sp-2); }
.credit--four .figure {
  display: block; font: var(--t-credit-fig); font-weight: var(--fw-heavy); letter-spacing: var(--ls-figure);
  font-variant-numeric: var(--num); font-stretch: var(--wd-num); margin-top: var(--sp-4);
}
.credit--four .credit__sentence { font: var(--t-meta); font-variant-numeric: var(--num); margin-top: var(--sp-4); }

/* NEVER CLIP A FIGURE. The pill is nowrap by design, which is right in a wide
   list row and wrong on a phone sized card: in the salesman app the tail of the
   figure line ("oldest bill 11 days, you gave 14") was running past the card and
   being cut off by the list's own overflow. A badge asked to wrap reflows onto a
   second line instead, and the pill keeps its round ends either way. */
/* Written as .credit.credit--wrap, not .credit--wrap, ON PURPOSE. The badge
   carries the class "pill", and the bills table's own .pill rule further down
   this file also declares white-space: nowrap at the same specificity - so a
   single class selector here loses to it on source order alone and the badge
   silently keeps clipping. This is the same collision the note above
   .credit.pill records; two classes settle it for good, whatever order the
   file ends up in. */
.credit.credit--wrap {
  white-space: normal; height: auto; min-height: var(--credit-pill-h);
  padding-block: var(--sp-4); border-radius: var(--r-12);
  align-items: flex-start;
}
.credit.credit--wrap .icon { margin-top: var(--sp-2); }
.credit.credit--wrap .b { flex-wrap: wrap; align-items: baseline; row-gap: var(--sp-2); }
.credit.credit--wrap .word { overflow: visible; text-overflow: clip; white-space: nowrap; }
.credit.credit--wrap .figure {
  flex: 1 1 100%; min-width: 0; white-space: normal; overflow-wrap: anywhere;
}
.row__sub .credit.credit--wrap { flex: 1 1 auto; min-width: 0; }

/* SIZE 3 — the status badge. The bills table only. LD's badge shape: a rounded
   pill with a small dot before the text. The tint and the ink are the shared
   status pair, identical in both themes, and the word is always there, so the
   badge survives greyscale and colour blindness on the word alone.
   NOTE: Overdue is the pale --stop-bg tint, never a solid red fill. Pending is
   the pale --warn-bg tint, never the solid #FFD43B: pale yellow is a status,
   solid yellow is an action, and that never swaps. */
.pill {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  background: var(--muted-bg); color: var(--muted);
  border: 0; border-radius: var(--r-pill); padding: 0 var(--sp-8);
  min-height: var(--credit-pill-h);
  white-space: nowrap; font: var(--t-meta-strong);
}
.pill::before {
  content: ''; width: var(--dot); height: var(--dot); border-radius: 50%;
  background: currentColor; flex: 0 0 auto;
}
.pill--overdue { color: var(--stop); background: var(--stop-bg); }
.pill--pending { color: var(--warn); background: var(--warn-bg); }
.pill--paid    { color: var(--ok);   background: var(--ok-bg); }

/* =========================================================================
   STAT STRIP — the figures behind the answer
   =========================================================================
   Not tiles any more. A sunken grey rectangle inside a white card is the 2005
   portal look and it was on this product six times over. These are label and
   figure pairs on the card's own paper, fenced by one hairline top and bottom.
   The tile that reflects a credit problem takes a 2px INK bar, never an amber
   wash and never the filled triangle, which belongs to the credit answer. */
.stat-tiles {
  display: flex; flex-wrap: wrap; gap: var(--sp-16) var(--sp-32);
  margin: 0 0 var(--gap-section); padding: var(--sp-12) 0;
  border-top: var(--hair) solid var(--hairline);
  border-bottom: var(--hair) solid var(--hairline);
}
.stat-tile { min-width: 0; flex: 1 1 var(--tile-min); }
.stat-tile__label { font: var(--t-label); color: var(--text-2); }
.stat-tile__figure {
  font: var(--t-total); font-weight: var(--fw-heavy);
  letter-spacing: var(--ls-figure); color: var(--ink);
  margin-top: var(--sp-4); font-variant-numeric: var(--num); font-stretch: var(--wd-num);
  display: flex; align-items: center; gap: var(--sp-8); overflow-wrap: anywhere;
}
/* the tile that reflects a credit problem takes a 3px bar in the attention ink.
   Not a yellow bar: #FFD43B on white is 1.43:1 and would not be seen. Not a
   yellow wash either, because a wash here would be a status tint fighting the
   credit answer three centimetres away. */
.stat-tile--warn { padding-left: var(--sp-12); box-shadow: inset var(--bar-ink) 0 0 var(--warn); }
/* the filled triangle is the amber credit answer's shape carrier. It is not
   allowed to appear anywhere else, so it is not drawn here. */
.warn-icon { display: none; }

/* =========================================================================
   TABLES — a paper head, hairline rules, no banding
   ========================================================================= */
.table-wrap {
  overflow: auto; overscroll-behavior: contain; max-height: var(--table-max-h);
  border-radius: var(--r-12); background: var(--paper);
  border: var(--hair) solid var(--hairline);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}
table {
  border-collapse: separate; border-spacing: 0; width: 100%;
  font: var(--t-meta); font-variant-numeric: var(--num);
}
th, td { text-align: left; padding: var(--sp-8) var(--sp-12); white-space: nowrap; }
th { color: var(--text-2); font: var(--t-label); background: var(--paper); }
td { background: var(--paper); border-top: var(--hair) solid var(--rule); height: var(--table-row-h); }

table:has(thead) thead th {
  position: sticky; top: 0; z-index: var(--z-sticky-sub);
  border-bottom: var(--hair) solid var(--hairline);
}
table:has(thead) td { border-top: var(--hair) solid var(--rule); }
table:has(thead) tbody tr:first-child td { border-top: 0; }
table:has(thead) tbody tr:hover td { background: var(--hover); }
/* money aligns on its last digit whatever the typeface does with figures */
table:has(thead) th:nth-child(3),
table:has(thead) td:nth-child(3) { text-align: right; }
table:has(thead) td:nth-child(3) {
  font: var(--t-meta-strong); font-variant-numeric: var(--num);
  font-stretch: var(--wd-num); color: var(--price);
}
table:has(thead) th:last-child,
table:has(thead) td:last-child { text-align: right; }

/* =========================================================================
   ORDER LINES
   =========================================================================
   LD, on his laptop: "the information is too cramped, especially the items in
   this order section, it needs to be clearly displayed... I like the font and
   all the cards and the details that are shown, it's just the way it's visually
   presented needs to be resized." On his screen the product names wrapped to
   three lines, "= 240 packet" was clipped off the right edge, and the card had
   a scrollbar of its own.

   So a line is a four column GRID, not a flex row that lets the right hand
   figures be squeezed:

       minmax(0, 2.4fr)   110px   110px   110px
       item               qty     rate    amount

   The item column is the only one allowed to wrap, so a long product name
   takes a second line instead of pushing a figure off the card. The three
   money columns are a FIXED 110px each, which is exactly what stops the
   clipping: they cannot be compressed by whatever is to their left. One column
   head above the rows names them once, so no row has to repeat a label.
   The card no longer scrolls inside itself — see .order-items-card below. */
.lines { margin-top: 0; container-type: inline-size; container-name: items; }

/* The columns answer to the CARD's own width, not the window's. That
   distinction is the whole bug LD photographed: the order page put its rail
   beside the items whenever the WINDOW was wide, so on his laptop the items
   card was left about 340px wide while the three money columns still asked for
   330px of it - which is what squeezed the product names onto three lines and
   pushed "= 240 packet" off the right edge. A container query asks the only
   question that matters: is there room in THIS card. */
.linehead,
.linerow {
  display: grid;
  /* auto-fit with a floor, not a flat three: a quantity field squeezed below
     about 80px can no longer show its own number, so the cells drop to two and
     then to one rather than holding three and cutting the figures. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 5rem), 1fr));
  gap: var(--sp-8) var(--sp-12);
  align-items: start;
}
.linehead { display: none; }
.linerow__item { grid-column: 1 / -1; }

@container items (min-width: 30rem) {
  .linehead,
  .linerow {
    grid-template-columns: minmax(0, 2.4fr) 110px 110px 110px;
    gap: var(--sp-12);
  }
  .linehead { display: grid; }
  .linerow__item { grid-column: auto; }
}
.linehead {
  padding: var(--sp-8) 0; border-bottom: var(--hair) solid var(--hairline);
  font: var(--t-label); color: var(--text-2);
}
.linehead__amount { text-align: right; }

.linerow {
  padding: var(--row-pad-y) 0; border-bottom: var(--hair) solid var(--rule); width: 100%;
  background: none; border-left: 0; border-right: 0; border-top: 0;
  text-align: left; color: var(--ink);
}
.linerow:last-child { border-bottom: 0; }

/* the item cell: what he said, what it matched, then one quiet meta line */
.linerow__item { min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.linerow__said { font: var(--t-meta); font-style: italic; color: var(--text-2); overflow-wrap: anywhere; }
.linerow__name { font: var(--t-section); color: var(--ink); overflow-wrap: anywhere; }
.linerow__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4) var(--sp-12);
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
  margin-top: var(--sp-2);
}
.linerow__pack { white-space: nowrap; }
.linerow__note { font: var(--t-meta); color: var(--text-2); }
.linerow__note.is-warn { color: var(--warn); }

/* the three narrow cells. Each stacks its own small label over its control or
   figure, so the column reads top to bottom and nothing needs a tooltip. */
.linerow__cell { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }
.linerow__cell-label { font: var(--t-label); color: var(--text-2); }
.linerow__value { font: var(--t-money); color: var(--ink); font-variant-numeric: var(--num); font-stretch: var(--wd-num); }
/* the distributor's own unit, under the figure rather than fighting it for
   room on the same line — this is the text that used to be clipped */
.linerow__conv {
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
  overflow-wrap: anywhere;
}
.linerow__cell--amount { align-items: flex-end; text-align: right; }

.line-field-label { display: flex; flex-direction: column; gap: var(--sp-4); margin-bottom: 0; min-width: 0; }
.line-field-label > span { font: var(--t-label); color: var(--text-2); }
.line-field {
  width: 100%; min-width: 0; min-height: var(--input-h); height: var(--input-h);
  padding: 0 var(--sp-8); text-align: right;
}
.line-amount {
  font: var(--t-money); font-variant-numeric: var(--num); font-stretch: var(--wd-num);
  color: var(--price); white-space: nowrap; min-height: var(--input-h);
  display: flex; align-items: center; justify-content: flex-end;
}
/* a text button: Pine, underlined on hover only */
.line-change-link {
  border: 0; background: none; padding: 0; margin: 0; cursor: pointer;
  font: var(--t-meta-strong); color: var(--pine); text-decoration: none;
  text-underline-offset: var(--nudge-underline);
}
.line-change-link:hover { text-decoration: underline; }
.line-change-link:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }

/* A line that needs a fix is the definition of "needs attention", so it is the
   one place in the items list that takes the pale yellow STATUS tint and the
   attention ink on its bar. Pale tint, never the solid #FFD43B: the solid fill
   is what you press, and pressing this opens a fix sheet rather than doing
   anything, so it does not get to look like the primary button. Ink text on
   #FFEFA8 is 14.11:1. */
.linerow.flagged {
  background: var(--warn-bg); cursor: pointer; border-radius: var(--r-8);
  padding: var(--sp-12) var(--sp-16); margin: var(--sp-8) 0;
  border: 0; box-shadow: inset var(--bar-ink) 0 0 var(--warn);
  transition: background-color var(--dur-fast) var(--ease-out);
}
.linerow.flagged .linerow__said { font-weight: 600; color: var(--ink); font-style: normal; }
.linerow.flagged:hover { filter: brightness(.97); }
.linerow.flagged:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* a line's own state word, and a scheme's Live / Paused / Expired. Ink plus a
   word, always. Done is a filled Ink dot, anything still open is a hollow Ink
   ring — a shape difference that survives greyscale without a second hue. */
.state-tag { font: var(--t-meta-strong); color: var(--ink); white-space: nowrap; }
.state-tag::before {
  content: ''; display: inline-block; vertical-align: var(--nudge-dot);
  width: var(--dot); height: var(--dot); border-radius: 50%;
  background: currentColor; margin-right: var(--sp-8);
}
/* green means done, yellow means this one still wants you */
.state-tag.matched { color: var(--ok); }
.state-tag.suggested, .state-tag.unit_flagged, .state-tag.needs_qty, .state-tag.unmatched, .state-tag.rate-needed { color: var(--warn); }
.state-tag.suggested::before, .state-tag.unit_flagged::before,
.state-tag.needs_qty::before, .state-tag.unmatched::before, .state-tag.rate-needed::before {
  background: transparent; box-shadow: inset 0 0 0 var(--hair-strong) currentColor;
}

/* W06 THE RATE CELL on a line that has no selling rate: label, field, Save
   rate, and its error under the field (04m 5.10). Ink error text with the warn
   field edge, never the credit red (D19). */
.rate-cell { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-4) var(--sp-8); }
.rate-cell .field-error { flex: 1 1 100%; margin-top: 0; }

/* THE STOP SENTENCE (04m 5.7): why Confirm is greyed, said once, directly
   above the action row it belongs to. Ink, not red: it is a reason, not an
   alarm. Empty, it takes no room. */
.stop-sentence { font: var(--t-meta); color: var(--ink); font-variant-numeric: var(--num); margin: 0 0 var(--sp-8); max-width: var(--measure); }
.stop-sentence:empty { display: none; }
.order-card .stop-sentence { padding: var(--sp-8) var(--sp-16) 0; margin: 0; }
.order-credit-card .credit-extra { margin-top: var(--sp-8); }

/* a note, not a box: paper, a 3px bar, a 12px label and 14px body */
.notes {
  background: none; border-radius: 0;
  padding: 0 0 0 var(--sp-12); margin-top: var(--sp-12);
  box-shadow: inset var(--bar-ink) 0 0 var(--ink);
  font: var(--t-body); color: var(--ink);
}
.notes + .notes { margin-top: var(--sp-8); }
.notes b { display: block; margin-bottom: var(--sp-2); font: var(--t-label); color: var(--text-2); }

/* THE STORE'S OWN WORDS. LD: the quoted message in a soft yellow box, using the
   warning background. It sits where it already sat — inside the order rail's
   Details card — and it is scoped to that card on purpose, so the catalogue's
   "words this is matched by" note stays a neutral note and never puts a yellow
   attention box around a list of aliases. */
.order-rail-card .notes {
  background: var(--warn-bg); color: var(--ink);
  border-radius: var(--r-12);
  padding: var(--sp-12); box-shadow: none;
}
.order-rail-card .notes b { color: var(--warn); }

/* =========================================================================
   SCHEMES — money you can still move, so no credit hue anywhere near them
   =========================================================================
   The old build put a SOLID GREEN capsule with a tick a few centimetres from
   the green Clear pill with a tick. That was the one real confusion risk in
   the product and it is gone: Applied is an INK filled tag, Available an
   outlined tag, Almost there a dashed outlined tag. Rows are rows on the
   card's own paper — no sunken fill, no coloured left rule. */
.scheme-panel { margin: 0 0 var(--sp-12); padding: 0; }
.scheme-panel.order-level { margin-top: var(--sp-16); }
.scheme-panel > h4 { font: var(--t-label); color: var(--text-2); margin-bottom: var(--sp-8); }

.scheme-line-group { margin-bottom: var(--sp-16); }
.scheme-line-group:last-child { margin-bottom: 0; }
.scheme-line-label { font: var(--t-section); color: var(--ink); margin-bottom: var(--sp-8); }

.scheme-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-16);
  padding: var(--sp-12) 0; background: none; border-radius: 0;
  border-bottom: var(--hair) solid var(--rule);
}
.scheme-row:last-child { border-bottom: 0; }
.scheme-row .g { flex: 1; min-width: 0; }
.scheme-row .t { font: var(--t-body); font-variant-numeric: var(--num); color: var(--ink); }
.scheme-row .s { font: var(--t-meta); font-variant-numeric: var(--num); color: var(--text-2); margin-top: var(--sp-2); }
.scheme-row > .btn { flex: 0 0 auto; }
.scheme-row.applied, .scheme-row.available, .scheme-row.nearmiss { box-shadow: none; background-image: none; }

/* Rounded pills like every other badge. The FILL stays ink rather than green:
   a solid green tag with a tick, a few centimetres from the green "Clear"
   credit answer with a tick, is the one real confusion risk in this product and
   it is not being reintroduced for the sake of a nicer colour. */
.scheme-tag {
  display: inline-flex; align-items: center; gap: var(--sp-4);
  font: var(--t-label); white-space: nowrap;
  padding: 0 var(--sp-8); min-height: var(--sp-20);
  border-radius: var(--r-pill); border: var(--hair) solid transparent;
  margin-bottom: var(--sp-4);
}
.scheme-tag::before { font-size: var(--fs-label); line-height: 1; }
.scheme-tag.applied { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.scheme-tag.applied::before { content: '\2713'; }
.scheme-tag.available { background: none; border-color: var(--control-edge); color: var(--text-2); }
.scheme-tag.available::before { content: '\002B'; }
.scheme-tag.nearmiss { background: none; border-style: dashed; border-color: var(--control-edge); color: var(--ink); }
.scheme-tag.nearmiss::before { content: '\2191'; }

/* the margin maths: two rupee figures compared. Plain tabular text, no box. */
.margin-line {
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
  background: none; border-radius: 0; padding: 0; margin-top: var(--sp-4);
  display: block;
}
/* an applied outcome and money left on the table. Both stay ink text on paper,
   neither is a fill; only the bar carries the tone. Green for what was banked,
   attention amber for what is still sitting there unclaimed. */
.free-line, .unspent-note {
  font: var(--t-body); color: var(--ink); font-variant-numeric: var(--num);
  background: none; border-radius: 0;
  box-shadow: inset var(--bar-ink) 0 0 var(--ink);
  padding: var(--sp-8) 0 var(--sp-8) var(--sp-12);
}
.free-line { margin-bottom: var(--sp-8); box-shadow: inset var(--bar-ink) 0 0 var(--ok); }
.unspent-note { margin-top: var(--sp-12); box-shadow: inset var(--bar-ink) 0 0 var(--warn); }

/* =========================================================================
   DISCLOSURE
   ========================================================================= */
details { margin-top: var(--gap-section); border-top: var(--hair) solid var(--hairline); padding-top: var(--sp-12); }
details summary {
  cursor: pointer; list-style: none; display: flex; align-items: center;
  gap: var(--sp-8); min-height: var(--ctrl-h);
}
details summary::-webkit-details-marker { display: none; }
/* a drawn chevron, not a typographic glyph standing in for an icon */
details summary::before {
  content: ''; flex: 0 0 auto; width: var(--sp-6); height: var(--sp-6);
  border-right: var(--stroke-icon) solid var(--text-3);
  border-bottom: var(--stroke-icon) solid var(--text-3);
  transform: rotate(-45deg); margin-left: var(--sp-2);
  transition: transform var(--dur-fast) var(--ease-out);
}
details[open] summary::before { transform: rotate(45deg); }
details summary:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
details .hi { padding: var(--sp-4) 0 0 var(--sp-16); font-variant-numeric: var(--num); }

/* =========================================================================
   BUTTONS — 32px on a pointer, 44px on touch. Same sizes, new finish.
   =========================================================================
   LD's gloss, kept subtle: a vertical gradient lighter at the top, a thin white
   inner line at the top, a thin dark inner line at the bottom, a small soft
   shadow, and 1px of travel on press. Nothing about the size, the padding or
   the gap changed — a button is exactly as big as it was.

   THE PRIMARY. Eucalyptus: a solid #FFD43B fill with #14231C text. Charcoal: a
   solid #0F5A3D fill with white text. Either way a SOLID fill of the theme's
   accent means ACTION, and it is the only thing in the product that looks like
   this. The pale --warn-bg tint that means "needs attention" never takes this
   shape and this fill never takes that job.
   The 1px --btn-primary-edge is doing two jobs: it is the dark bottom line the
   gloss asks for, and it is the 3:1 boundary SC 1.4.11 asks for, which a raw
   #FFD43B fill on white (1.43:1) cannot provide on its own.

   THE SOFTENING PASS. LD: "Record payment and Overdue stores look old school,
   make them modern and softer." The dated part was never the colour, it was the
   shape and the bevel: a 6px corner, and a 1px --control-edge border with a
   second dark inset line sitting immediately inside it, which read as a 2003
   chisel and made one boundary look like two.

   WHAT SOFTER IS NOT. It is NOT a lighter border. --control-edge exists exactly
   because the warm --hairline (#E4E2D6) is 1.30:1 and is not a legal boundary
   for a control under SC 1.4.11 — see the note at the top of tokens.css. The
   border colour is UNCHANGED in every theme and still carries the 3:1 boundary
   on its own: 3.20:1 in Eucalyptus, 3.26:1 in Charcoal, measured against the
   page --ground, which is the worse of the two grounds a button sits on.

   SO THE SOFTNESS COMES FROM SHAPE, SURFACE AND WEIGHT:
     shape    --r-6 -> --r-10. This is most of what reads as modern.
     weight   the dark inset bottom line is gone. The border is the only edge
              now, so the boundary reads as ONE hairline rather than a chisel.
              Same contrast, half the apparent weight.
     lift     the inset top highlight stays, and --sh-btn is joined by a wider,
              much fainter ambient layer built from --ctrl-gloss-bottom, so the
              button sits on the page instead of being stamped into it.
     hover    the edge used to jump to near-black --ink. It now moves one step,
              to --text-2, and the surface and the shadow carry the rest.
   Height, padding, gap, font and colour are all untouched, so nothing moves. */
/* --r-btn is declared by Indigo Pop only (its pill buttons). Everywhere else
   it falls back to --r-10, so the corner is unchanged, and the store tiles,
   which also read --r-10, never turn into circles. */
.btn {
  min-height: var(--ctrl-h); padding: 0 var(--sp-12); border-radius: var(--r-btn, var(--r-10));
  border: var(--hair) solid var(--control-edge); color: var(--ink);
  background-color: var(--paper);
  background-image: linear-gradient(to bottom, var(--ctrl-top), var(--ctrl-bottom));
  box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top),
              var(--sh-btn),
              0 var(--sp-2) var(--sp-6) calc(var(--hair) * -1) var(--ctrl-gloss-bottom);
  font: var(--t-button); cursor: pointer;
  /* SIZED TO ITS WORDS (03n F4, 04m 5.2): never narrower than 96 px, never
     wider than 320 px, and a label that would pass 320 wraps to a second line
     inside the button. Never an ellipsis. */
  min-width: var(--btn-min-w); max-width: var(--btn-max-w);
  white-space: normal; text-align: center; line-height: 1.2;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-8);
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.btn:hover {
  background-color: var(--hover); border-color: var(--text-2);
  background-image: linear-gradient(to bottom, var(--ctrl-hover-top), var(--ctrl-hover-bottom));
  box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top),
              var(--sh-btn-hover),
              0 var(--sp-4) var(--sp-8) calc(var(--hair) * -1) var(--ctrl-gloss-bottom);
}
/* press: down one pixel, shadow gone. The travel is a transform, so it never
   reflows a row, and it is a state change rather than an animation, so it
   survives prefers-reduced-motion honestly. */
.btn:active { transform: translateY(var(--hair)); box-shadow: var(--sh-btn-active); }
.btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
/* THE PRIMARY KEEPS ITS JOB AND ITS COLOUR. It takes the same radius and the
   same ambient lift so the two read as one family, and nothing else: the fill,
   the ink and the --btn-primary-edge boundary are exactly as they were, because
   a solid accent fill with dark text still means ACTION and only ACTION. */
.btn.primary, .btn.gold {
  background-color: var(--btn-primary-bg);
  background-image: linear-gradient(to bottom, var(--btn-primary-top), var(--btn-primary-bottom));
  border-color: var(--btn-primary-edge); color: var(--btn-primary-ink);
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top),
              var(--sh-btn),
              0 var(--sp-2) var(--sp-6) calc(var(--hair) * -1) var(--gloss-bottom);
}
.btn.primary:hover, .btn.gold:hover {
  background-color: var(--btn-primary-bg);
  background-image: linear-gradient(to bottom, var(--btn-primary-hover-top), var(--btn-primary-hover-bottom));
  border-color: var(--btn-primary-edge); color: var(--btn-primary-ink);
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top),
              var(--sh-btn-hover),
              0 var(--sp-4) var(--sp-8) calc(var(--hair) * -1) var(--gloss-bottom);
}
/* SECOND PRIMARY. Only one primary button per area. If a state ever puts two
   in the same action row, the second is demoted to a secondary here rather
   than left to fight the first. */
.btn-row .btn.primary ~ .btn.primary,
.order-actions-row .btn.primary ~ .btn.primary,
.actionbar .btn.primary ~ .btn.primary,
.onboarding__actions .btn.primary ~ .btn.primary {
  background-color: var(--paper); color: var(--ink);
  background-image: linear-gradient(to bottom, var(--ctrl-top), var(--ctrl-bottom));
  border-color: var(--control-edge);
  box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top),
              var(--sh-btn),
              0 var(--sp-2) var(--sp-6) calc(var(--hair) * -1) var(--ctrl-gloss-bottom);
}
/* destructive: a secondary button with red ink. A solid red fill is permitted
   in one place only, inside a confirmation dialog, which is what .sheet is. */
.btn.danger { background-color: var(--paper); border-color: var(--control-edge); color: var(--stop); }
.btn.danger:hover { background-color: var(--hover); border-color: var(--stop); color: var(--stop); }
.sheet .btn.danger, .onboarding .btn.danger {
  background-color: var(--stop); background-image: none;
  border-color: var(--stop); color: var(--paper);
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top), var(--sh-btn),
              0 var(--sp-2) var(--sp-6) calc(var(--hair) * -1) var(--gloss-bottom);
}
.sheet .btn.danger:hover, .onboarding .btn.danger:hover { background-color: var(--stop); border-color: var(--ink); color: var(--paper); }
/* the small button is shorter, so it takes one step less corner. 10px on a
   28px control starts to read as a chip, and a chip is a different object. */
.btn.small { min-height: var(--ctrl-h-sm); padding: 0 var(--sp-8); border-radius: var(--r-8); }
/* disabled dims the whole control, never the text alone, and drops the gloss:
   a button you cannot press should not look raised */
.btn[disabled] { opacity: .5; cursor: not-allowed; box-shadow: none; }
.btn[disabled]:hover { background-color: var(--paper); border-color: var(--control-edge); box-shadow: none; }
.btn[disabled]:active { transform: none; }
.btn.primary[disabled], .btn.gold[disabled] { box-shadow: none; }
.btn.primary[disabled]:hover, .btn.gold[disabled]:hover {
  background-color: var(--btn-primary-bg);
  background-image: linear-gradient(to bottom, var(--btn-primary-top), var(--btn-primary-bottom));
  border-color: var(--btn-primary-edge); box-shadow: none;
}
/* 8px is the minimum gap between two adjacent targets on a fine pointer */
.btn-row { display: flex; gap: var(--sp-8); flex-wrap: wrap; align-items: flex-end; }
.btn-row > * { flex: 0 0 auto; }
.btn-row--tight > * { flex: 0 0 auto; }

.seg {
  display: flex; gap: 0; background: var(--paper); padding: 0;
  border: var(--hair) solid var(--control-edge); border-radius: var(--r-pill);
  margin-bottom: var(--sp-16); overflow: hidden;
  box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top), var(--sh-btn);
}
.seg__btn {
  flex: 1; min-height: var(--ctrl-h); border: 0; border-left: var(--hair) solid var(--hairline);
  background: none; color: var(--text-2);
  font: var(--t-button); border-radius: 0; cursor: pointer; padding: 0 var(--sp-12);
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
  font-variant-numeric: var(--num);
}
.seg__btn:first-child { border-left: 0; }
.seg__btn:hover { color: var(--ink); background-color: var(--hover); }
.seg__btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-offset) * -1); }
/* the chosen segment is ink, not the accent: a segmented control chooses a
   VIEW, and the accent fill is reserved for the one action on the screen */
.seg__btn[aria-pressed="true"] { background-color: var(--ink); color: var(--on-ink); font-weight: 700; }

/* level 3, sticky: paper, a hairline, and the card's own soft shadow so it
   reads as sitting above the page it is pinned to */
.actionbar {
  position: sticky; bottom: var(--sp-16); z-index: var(--z-actionbar);
  display: flex; gap: var(--sp-8); flex-wrap: wrap; align-items: center;
  margin-top: var(--sp-16); padding: var(--sp-12);
  background: var(--paper); border: var(--hair) solid var(--hairline);
  border-radius: var(--r-12);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}
.actionbar > * { flex: 0 1 auto; }
.blockmsg { font: var(--t-meta); font-variant-numeric: var(--num); color: var(--ink); padding: var(--sp-4) 0; }
.blockmsg:empty { display: none; }

/* =========================================================================
   INPUTS — a real 1px edge, because a man must see where to type
   =========================================================================
   NO GLOSS HERE, on purpose. A gradient and a top highlight make an object look
   raised, and a field is a hole you type into, not a key you press. The only
   finish an input gets is its 3:1 edge. Every visible label stays visible. */
label.field { display: block; margin-bottom: var(--sp-12); }
label.field > span { display: block; font: var(--t-meta-strong); color: var(--ink); margin-bottom: var(--sp-4); }
input[type=text], input[type=number], input[type=search], input[type=file], input[type=date], select, textarea {
  width: 100%; min-height: var(--input-h); padding: 0 var(--sp-8); border-radius: var(--r-6);
  border: var(--hair) solid var(--control-edge); background: var(--paper); color: var(--ink);
  font: var(--t-body);
  transition: border-color var(--dur-fast) var(--ease-out);
}
/* BUG-R20: a phone field inside a labelled form row looks and measures like the
   other fields (it was the browser's bare box, 29 px tall at 360 with touch).
   The sign in page keeps its own phone styling in auth.css. */
label.field input[type=tel] {
  width: 100%; min-height: var(--input-h); padding: 0 var(--sp-8); border-radius: var(--r-6);
  border: var(--hair) solid var(--control-edge); background: var(--paper); color: var(--ink);
  font: var(--t-body); font-variant-numeric: var(--num);
  transition: border-color var(--dur-fast) var(--ease-out);
}
textarea, input[type=file] { padding: var(--sp-8); }
input[type=number], input[type=date] { font-variant-numeric: var(--num); }
input::placeholder, textarea::placeholder { color: var(--text-3); }
textarea { min-height: var(--textarea-min-h); resize: vertical; font: var(--t-body); line-height: 1.5; }
input:hover, select:hover, textarea:hover { border-color: var(--ink); }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset);
  border-color: var(--ink);
}
input[type=checkbox] { width: var(--sp-20); height: var(--sp-20); accent-color: var(--ink); flex: 0 0 auto; }
label.field:has(input[type=checkbox]) {
  display: flex; align-items: center; gap: var(--sp-12); min-height: var(--ctrl-h); margin-bottom: var(--sp-12);
}
.manual-add-form { margin-top: var(--sp-16); padding-top: var(--sp-16); border-top: var(--hair) solid var(--hairline); }

/* =========================================================================
   VOICE — the one live moment in the product
   ========================================================================= */
.voice {
  border: var(--hair) solid var(--hairline); background: var(--paper);
  border-radius: var(--r-12); padding: var(--pad-card-main); margin-bottom: var(--gap-card);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}
.voice__row { display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap; }
/* the mic is the one action on this card, so it takes the primary finish */
.voice__mic {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-8);
  min-height: var(--mic-h); padding: 0 var(--sp-20); border-radius: var(--r-btn, var(--r-10));
  border: var(--hair) solid var(--btn-primary-edge);
  background-color: var(--btn-primary-bg);
  background-image: linear-gradient(to bottom, var(--btn-primary-top), var(--btn-primary-bottom));
  color: var(--btn-primary-ink);
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top),
              var(--sh-btn),
              0 var(--sp-2) var(--sp-6) calc(var(--hair) * -1) var(--gloss-bottom);
  font: var(--t-button); cursor: pointer; flex: 1 1 auto;
  transition: background-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.voice__mic:hover {
  background-image: linear-gradient(to bottom, var(--btn-primary-hover-top), var(--btn-primary-hover-bottom));
}
.voice__mic:active { transform: translateY(var(--hair)); }
.voice__mic:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.voice__mic-label { font: var(--t-button); }
.voice__icon { width: var(--icon-inline); height: var(--icon-inline); flex: 0 0 var(--icon-inline); }
.voice__lang { flex: 0 0 auto; width: auto; min-width: 0; max-width: 100%; min-height: var(--mic-h); }

/* THE ONE PIECE OF CONTINUOUS MOTION IN THE PRODUCT, and it is functional: it
   means the microphone is live. The ring is --pulse-rgb, which is the theme's
   own button edge rather than the button's own fill — a yellow ring around a
   yellow button in Eucalyptus would simply not be seen. Under reduced motion
   it becomes a static ring: the fact survives, the movement does not. */
.voice.is-listening .voice__mic { animation: mic-pulse 1.8s var(--ease-out) infinite; }
@keyframes mic-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--pulse-rgb), .75); }
  70%  { box-shadow: 0 0 0 var(--sp-12) rgba(var(--pulse-rgb), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--pulse-rgb), 0); }
}
@media (prefers-reduced-motion: reduce) {
  .voice.is-listening .voice__mic { animation: none; box-shadow: 0 0 0 var(--sp-4) rgba(var(--pulse-rgb), .9); }
}

.voice__live {
  margin-top: var(--sp-16); padding-top: var(--sp-12);
  border-top: var(--hair) solid var(--hairline);
  background: none; border-radius: 0;
  font: var(--t-body); min-height: var(--ctrl-h);
}
.voice__heard { color: var(--ink); }
.voice__interim {
  color: var(--text-2);
  text-decoration: underline dotted var(--control-edge);
  text-underline-offset: var(--nudge-underline);
}
.voice__status { font: var(--t-meta); color: var(--text-2); margin: var(--sp-12) 0 0; display: flex; align-items: center; gap: var(--sp-8); }
.voice__status::before { content: ''; width: var(--dot); height: var(--dot); border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.voice__status--live, .voice__status--done { color: var(--ink); font-weight: 500; }
.voice__status--off { color: var(--text-2); margin: 0; }
/* an error sentence is the one non-credit use of red ink the direction allows */
.voice__status--error { color: var(--stop); font-weight: 500; }

/* Which mode in app voice actually ran (handover 27.10). Its own line, same
   dot idiom as .voice__status above, because it answers a different
   question and must stay on screen after the outcome line moves on. */
.voice__mode { font: var(--t-meta); color: var(--text-2); margin: var(--sp-4) 0 0; display: flex; align-items: center; gap: var(--sp-8); }
.voice__mode::before { content: ''; width: var(--dot); height: var(--dot); border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.voice__mode--ondevice { color: var(--ink); font-weight: 500; }
.voice__mode--unknown { color: var(--warn); font-weight: 500; }

/* =========================================================================
   OVERLAYS — the only level that gets a shadow
   ========================================================================= */
/* THE DIALOG (04m 5.1, W01; 03n F6). One centred dialog everywhere; the
   bottom sheet is gone. The scrim centres it on both axes inside a 16 px
   inset, in the visible area (the scrim shrinks above a phone keyboard). Two
   widths, small 380 and form 560; under 480 wide, the screen minus 32 px.
   All four corners round. The header and body scroll together inside the
   box; the footer row is sticky at its bottom on every size, so the primary
   and Close are always on screen (D4). */
.scrim {
  position: fixed; inset: 0; background: rgba(var(--scrim-rgb), .45); z-index: var(--z-dialog);
  display: flex; align-items: center; justify-content: center; padding: var(--dialog-inset);
  backdrop-filter: blur(var(--blur-scrim));
  animation: dialog-fade var(--dur-fast) var(--ease-out);
}
@keyframes dialog-fade { from { opacity: 0; } to { opacity: 1; } }
.sheet {
  background: var(--paper); width: 100%; max-width: var(--dialog-w-form);
  max-height: var(--dialog-max-h);
  display: flex; flex-direction: column; overflow: hidden;
  border: var(--hair) solid var(--hairline);
  border-radius: var(--r-12); box-shadow: var(--sh-overlay);
}
.dialog--small { max-width: var(--dialog-w-small); }
.dialog__scroll {
  flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain;
  padding: var(--sp-20);
  scroll-padding-bottom: var(--sp-16);
}
.sheet h2 { font: var(--t-title); letter-spacing: var(--ls-title); margin-bottom: var(--sp-4); }
.sheet h2:focus { outline: none; }
.sheet h2:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.sheet .sheet-sub { font: var(--t-meta); font-variant-numeric: var(--num); color: var(--text-2); margin-bottom: var(--gap-section); max-width: var(--measure); }
.dialog__head { margin-bottom: var(--sp-4); }
.dialog__foot {
  flex: 0 0 auto; background: var(--paper);
  border-top: var(--hair) solid var(--hairline);
  padding: var(--sp-12) var(--sp-20);
}
.dialog.is-scrolled .dialog__foot { box-shadow: var(--sh-card); }
.dialog__foot[hidden] { display: none; }
/* The footer row: primary first, plain buttons, Close (or the safe choice) last. ONE ROW at every width (04o R1): a button
   never wraps under another or shrinks below a thumb (44 px, BUG-RT4-4); a long label wraps inside its own button. */
.dialog__actions { display: flex; flex-wrap: nowrap; gap: var(--sp-8); align-items: center; justify-content: flex-start; }
.dialog__actions > * { flex: 0 1 auto; min-width: 44px; overflow-wrap: anywhere; }
.dialog__leave { font: var(--t-meta); color: var(--ink); margin-bottom: var(--sp-8); }
.dialog__error { margin-top: var(--sp-8); }
.dialog__invalid { font: var(--t-meta); color: var(--ink); }
.dialog__error[hidden], .dialog__leave[hidden] { display: none; }
@media (max-width: 480px) {
  .dialog__scroll { padding: var(--sp-16); }
  .dialog__foot { padding: var(--sp-12) var(--sp-16); }
}
@media (prefers-reduced-motion: reduce) {
  .scrim { animation: none; }
}
/* The launcher never covers a dialog's Close (04k R4). */
.has-dialog .chotu-launcher { display: none; }

/* MORE, as menu rows in a small dialog (04m 5.4; 03n F4 AC3). */
.menu-rows { display: flex; flex-direction: column; margin: 0 calc(var(--sp-20) * -1); }
.menu-row {
  display: flex; align-items: center; width: 100%; min-height: var(--menu-row-h);
  padding: 0 var(--sp-20); background: none; border: 0; border-top: var(--hair) solid var(--hairline);
  font: var(--t-body); color: var(--ink); text-align: left; cursor: pointer;
}
.menu-row:last-child { border-bottom: var(--hair) solid var(--hairline); }
.menu-row:hover { background-color: var(--hover); }
.menu-row:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-ring) * -1); }
@media (max-width: 480px) {
  .menu-rows { margin: 0 calc(var(--sp-16) * -1); }
  .menu-row { padding: 0 var(--sp-16); }
}

/* A field's own error: Ink text, the field gets a 2 px warn edge (D19). */
.field-error { font: var(--t-meta); color: var(--ink); margin-top: var(--sp-4); }
.field-error[hidden] { display: none; }
input[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--warn); box-shadow: 0 0 0 var(--hair) var(--warn);
}

.sheet-group { margin: var(--gap-section) 0 0; }
.sheet-group:first-child { margin-top: 0; }
.sheet-group + .sheet-group {
  margin-top: var(--sp-20); padding-top: var(--sp-20);
  border-top: var(--hair) solid var(--hairline);
}
.sheet-group > h4 { font: var(--t-card-title); color: var(--ink); margin-bottom: var(--sp-8); }

/* a pick row is a row: paper, a hairline between, a hover fill. Not a tile. */
.pick {
  width: 100%; text-align: left; min-height: var(--row-h); padding: var(--sp-8);
  margin: 0; border: 0; border-radius: 0;
  background: none; color: var(--ink); cursor: default;
  display: flex; align-items: center; gap: var(--sp-16);
  transition: background var(--dur-fast) var(--ease-out);
}
.pick + .pick { border-top: var(--hair) solid var(--rule); }
button.pick, a.pick { cursor: pointer; }
button.pick:hover, a.pick:hover { background: var(--hover); }
.pick:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-offset) * -1); }
.pick .g { flex: 1; min-width: 0; }
.pick .t { font: var(--t-row-name); font-variant-numeric: var(--num); color: var(--ink); }
.pick .s { font: var(--t-meta); font-variant-numeric: var(--num); color: var(--text-2); margin-top: var(--sp-2); }
.pick .c { font: var(--t-meta-strong); color: var(--text-2); white-space: nowrap; font-variant-numeric: var(--num); }

.limit-row { display: flex; gap: var(--sp-12); align-items: flex-end; flex-wrap: wrap; }
/* W03 Update limit: two fields side by side in the 560 dialog, stacked on a
   phone. The note and the upload line sit under them. */
.limit-dialog__fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-12); }
@media (max-width: 480px) { .limit-dialog__fields { grid-template-columns: 1fr; } }
.limit-row__field { flex: 1 1 var(--field-min); margin-bottom: 0; }
.limit-row__btn { flex: 0 0 auto; }

/* =========================================================================
   THE ORDER PAGE
   ========================================================================= */
/* The order page scales the same way the dashboard does: card padding and the
   column gap clamp against the viewport, capped at today's values, so the page
   tightens on a laptop and is unchanged on a monitor. */
.order-page {
  --o-pad: clamp(var(--sp-12), 1.4vh, var(--pad-card-main));
  max-width: var(--shell-max);
}
.order-page .card { padding: var(--o-pad); }
.order-page .card:last-of-type { margin-bottom: 0; }

.order-breadcrumb {
  display: flex; align-items: center; gap: var(--sp-8);
  font: var(--t-meta); color: var(--text-2); margin-bottom: var(--sp-8);
}
.order-breadcrumb__link {
  border: 0; background: none; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: var(--text-2); text-decoration: none;
  text-underline-offset: var(--nudge-underline);
}
.order-breadcrumb__link:hover { color: var(--ink); text-decoration: underline; }
.order-breadcrumb__link:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
.order-breadcrumb__sep { color: var(--text-3); }
.order-breadcrumb__current { color: var(--ink); font-weight: 500; font-variant-numeric: var(--num); }

/* the header: the store name is the headline, the reference a demoted aside,
   and the order's own facts sit top right as label / value pairs */
.order-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-16); flex-wrap: wrap; margin-bottom: var(--sp-12);
}
.order-header__main { display: flex; align-items: flex-start; gap: var(--sp-12); flex-wrap: wrap; min-width: 0; }
.order-header__name { min-width: 0; }
.order-title { font: var(--t-title); letter-spacing: var(--ls-title); }
.order-header__ref { font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num); }
.order-header__meta {
  display: grid; grid-template-columns: auto auto; gap: var(--sp-4) var(--sp-12);
  font: var(--t-meta); flex: 0 0 auto; margin-left: auto;
}
.order-header__meta dt { color: var(--text-2); }
.order-header__meta dd { color: var(--ink); font-weight: 600; text-align: right; font-variant-numeric: var(--num); font-stretch: var(--wd-num); }

/* every action is a visible button on one row. Nothing hides behind a hover. */
.order-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-16); flex-wrap: wrap; margin-bottom: var(--sp-12); }
.order-title-row__main { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; min-width: 0; }
.order-title-row__action { flex: 0 0 auto; }
.order-secondary-actions { display: flex; gap: var(--sp-8); flex-wrap: wrap; margin-bottom: var(--gap-page); }
.order-actions-row { display: flex; gap: var(--sp-8); flex-wrap: wrap; align-items: center; margin-bottom: var(--gap-page); }

/* STACKED, STATUS AND DETAILS FIRST (LD: "on the laptop, the status and detail
   section go to the bottom of the order page. Instead can they be on top of the
   order page"). When the rail cannot sit beside the items it used to fall below
   them, which is the one place he never looks. The column is a flex column now
   purely so the rail can be ordered above the items without moving a line of
   markup: header, actions, Status, Details, Items, Bill, Schemes. The wide
   layout below is untouched, because he likes it. */
.order-grid { display: flex; flex-direction: column; gap: var(--gap-card); }
.order-main { min-width: 0; order: 2; }
.order-rail { order: 1; }
.order-rail .card { margin-bottom: var(--gap-card); padding: var(--pad-card); }
.order-rail .card:last-child { margin-bottom: 0; }
/* Same correction one level up: the rail used to appear at a WINDOW width of
   1180, by which point the detail pane itself is only about 670px - so the
   items were handed roughly 370px of it and the rail 290px. It now appears only
   when the pane it actually lives in is wide enough for both. */
@container detail (min-width: 54rem) {
  .order-grid {
    display: grid; grid-template-columns: minmax(0, 2fr) minmax(var(--rail-col), 1fr);
    gap: var(--sp-16); align-items: start;
  }
  /* explicit columns, so the stacked ordering above cannot leak into the wide
     layout and swap the two sides over */
  .order-main { grid-column: 1; order: 0; }
  .order-rail { grid-column: 2; order: 0; margin-top: 0; position: sticky; top: var(--sp-24); }
}
.order-rail-scroll { max-height: var(--rail-scroll-h); overflow-y: auto; overscroll-behavior: contain; }

/* =========================================================================
   THE STORE PAGE (issue #8 G4 / step 2a)
   Header → Credit → Outstanding bills → Payments. Desktop right rail holds
   Contact, Credit limit and days, Store code and sales rep, What this store
   buys. Editors open as sheets (2b).
   ========================================================================= */
.store-page {
  --o-pad: clamp(var(--sp-12), 1.4vh, var(--pad-card-main));
  max-width: var(--shell-max);
}
.store-page .card { padding: var(--o-pad); margin-bottom: var(--gap-card); }
.store-page .card:last-child { margin-bottom: 0; }

.store-header {
  display: flex; flex-direction: column; gap: var(--sp-12);
  margin-bottom: var(--sp-16);
}
.store-header__top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-16); flex-wrap: wrap;
}
.store-header__identity { min-width: 0; flex: 1 1 16rem; }
.store-header__title-row {
  display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.store-header__name {
  font: var(--t-store); letter-spacing: var(--ls-title); margin: 0;
}
.store-header__meta {
  font: var(--t-meta); color: var(--text-2); margin-top: var(--sp-2);
}
.store-header__actions {
  display: flex; flex-wrap: wrap; gap: var(--sp-8); align-items: center;
  flex: 0 1 auto;
}
.store-header__reach {
  display: flex; flex-wrap: wrap; gap: var(--sp-8);
  margin-top: var(--sp-8);
}

/* Desktop markup splits main/rail in JS (wide()). Stack until the detail pane
   is ~50rem wide so the main column does not shrink to ~260 px at a 1000–1100
   window; the right column still holds at 1280. */
.store-page:not(.store-page--phone) .store-grid {
  display: flex; flex-direction: column; gap: var(--gap-card);
}
.store-page:not(.store-page--phone) .store-main { min-width: 0; }
.store-page:not(.store-page--phone) .store-rail { min-width: 0; }
@container detail (min-width: 50rem) {
  .store-page:not(.store-page--phone) .store-grid {
    display: grid; grid-template-columns: minmax(0, 2fr) minmax(var(--rail-col), 1fr);
    gap: var(--sp-16); align-items: start;
  }
  .store-page:not(.store-page--phone) .store-main { grid-column: 1; }
  .store-page:not(.store-page--phone) .store-rail {
    grid-column: 2; position: sticky; top: var(--sp-24);
  }
}
.store-rail .card { margin-bottom: var(--gap-card); padding: var(--pad-card); }
.store-rail .card:last-child { margin-bottom: 0; }
.store-page--phone .store-grid { display: flex; flex-direction: column; gap: var(--gap-card); }

/* Credit card: panel, then rows, arithmetic, stamp, gaps last. */
.store-credit-card .credit.compact { margin-bottom: var(--sp-12); }
.store-credit-card .credit-sum {
  margin: var(--sp-12) 0 var(--sp-8);
  color: var(--ink);
}
.credit-figs {
  display: flex; flex-direction: column;
  border-top: var(--hair) solid var(--hairline);
}
.credit-fig {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-12); padding: var(--sp-8) 0;
  border-bottom: var(--hair) solid var(--hairline);
}
.credit-fig:last-child { border-bottom: 0; }
.credit-fig__label { font: var(--t-meta); color: var(--text-2); }
.credit-fig__value { text-align: right; min-width: 0; }
.credit-fig__figure {
  font: var(--t-row-name); font-weight: var(--fw-heavy); color: var(--ink);
  font-variant-numeric: var(--num); font-stretch: var(--wd-num);
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-6);
}
.credit-fig__under {
  font: var(--t-meta); color: var(--text-2); margin-top: var(--sp-2);
  font-variant-numeric: var(--num);
}
.credit-stamp { margin-top: var(--sp-8); }
.credit-stamp__line {
  font: var(--t-meta); color: var(--text-2); margin: var(--sp-2) 0 0;
}

/* Gaps last inside the Credit card: ink text, not grey (G4). */
.store-credit-card .credit-gaps {
  margin-top: var(--sp-16);
  border-left-color: var(--ink);
}
.credit-gaps--ink .gap-line {
  color: var(--ink); background: transparent; padding-left: 0; padding-right: 0;
}
.credit-gaps--ink > h4 { color: var(--ink); }

.store-contact__name { font: var(--t-section); color: var(--ink); margin: 0 0 var(--sp-4); }
.store-contact__phone {
  font: var(--t-meta); color: var(--ink); font-variant-numeric: var(--num);
  margin: 0 0 var(--sp-2);
}

/* Phone: store-page--phone is set from wide() in JS, so these targets hold
   at ≥44px even when a desktop pointer media query is active (Playwright). */
.store-page--phone .store-header__actions { width: 100%; }
.store-page--phone .store-header__actions .btn,
.store-page--phone .store-header__reach .btn {
  flex: 1 1 auto;
  min-height: 2.75rem; /* 44 */
}
.store-page--phone .line-change-link {
  min-height: 2.75rem; /* 44 — text links that open editor sheets */
  align-items: center;
}

.store-rail-card .line-change-link,
.store-page .sheet-group > .line-change-link {
  margin-top: var(--sp-8);
  display: inline-flex;
}

/* Rail cards nest sheet-groups; pull the first one's top margin. */
.store-rail-card > .sheet-group:first-child { margin-top: 0; }
.store-rail-card > .sheet-group + .sheet-group {
  margin-top: var(--gap-section);
  padding-top: var(--gap-section);
  border-top: var(--hair) solid var(--hairline);
}


.order-items-card { display: flex; flex-direction: column; position: relative; }
/* the bill: three summary figures, the itemised list, then the total behind a
   hairline. A tax line lands as one more dt/dd pair in .kv when it exists. */
.order-bill-card .kv { margin-top: var(--sp-12); }
.bill-total-row {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: var(--sp-12); padding-top: var(--sp-12);
  border-top: var(--hair) solid var(--hairline);
  font: var(--t-section); color: var(--ink);
}
.bill-total-row__value {
  font: var(--t-total); font-weight: var(--fw-heavy); letter-spacing: var(--ls-figure);
  font-variant-numeric: var(--num); font-stretch: var(--wd-num); color: var(--price);
}

/* Payment, as an AMOUNT and not a fifth status step. A retailer pays against
   bills, often partly and often in a lump that spans several of them, so
   "₹2,000 of ₹4,946 paid" is the honest shape and a Paid circle on the tracker
   is not. The pill is deliberately the product's NEUTRAL badge pair (muted ink
   on muted ground) in all three states, never the credit answer's green /
   yellow / red: the credit colours have to keep meaning exactly one thing, and
   a small pill that borrowed them would be the second thing. The state is read
   from the word, which is derived from the two figures beside it. */
.bill-paid-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-8);
  flex-wrap: wrap; margin-top: var(--sp-8); padding-top: var(--sp-8);
  border-top: var(--hair) solid var(--rule);
  font: var(--t-meta-strong); color: var(--text-2); font-variant-numeric: var(--num);
}
.bill-paid-row__text { color: var(--ink); }
.paypill {
  display: inline-flex; align-items: center; border-radius: var(--r-pill);
  background: var(--muted-bg); color: var(--muted); border: var(--hair) solid var(--hairline);
  padding: 0 var(--sp-8); min-height: var(--credit-pill-h);
  font: var(--t-label); white-space: nowrap;
}
.paypill::before {
  content: ''; display: inline-block; width: var(--dot-sm); height: var(--dot-sm);
  border-radius: 50%; margin-right: var(--sp-6);
  background: transparent; box-shadow: inset 0 0 0 var(--hair-strong) currentColor;
}
/* part paid is a half filled dot, fully paid a solid one: a SHAPE difference,
   so the three states separate in greyscale without a second colour */
.paypill--part::before { background: linear-gradient(to right, currentColor 50%, transparent 50%); }
.paypill--paid::before { background: currentColor; }

/* THE STATUS TRACKER. LD asked for the one useful thing from the preview: the
   same four steps, in the same card, in the same place in the rail, laid out
   left to right instead of top to bottom. Done steps are filled in the side
   menu's green and carry a tick, the current step is a yellow circle with a
   soft glow ring, future steps are white with a grey outline.
   The state is carried by THREE things, not one: the fill, the tick, and the
   label itself, which is always printed and never abbreviated. The glow is a
   static box-shadow, not an animation, so there is nothing for reduced motion
   to turn off.
   The yellow circle carries a --track-now-edge ring because #FFD43B on white is
   1.43:1 and a yellow disc on its own would not be a visible state. */
/* Four steps across an 18rem rail gave each label about 54px, and "Confirmed"
   needs 62 at the 12px label floor - so the four words were overlapping their
   neighbours and spilling out of the card. They now claim a real minimum and
   wrap to two rows of two when the rail cannot hold four, which is every width
   the rail actually appears at. Nothing else about the step moved: same dots,
   same icons, same states, same colours.
   overflow-x is clipped because the connector between two dots is drawn as an
   absolutely positioned line reaching back into the PREVIOUS step's half - on
   the first step of a wrapped row there is no previous step, and that purely
   decorative line must not escape the card. The labels themselves fit. */
.order-timeline {
  /* 6rem, not 4.5: at 4.5 three steps squeezed onto the first row and left the
     fourth alone on the second, which reads as a broken row rather than a
     deliberate 2x2. */
  --track-step-min: 6rem;
  display: flex; flex-direction: row; align-items: flex-start; flex-wrap: wrap;
  row-gap: var(--sp-12); overflow-x: clip;
}
.order-timeline__step {
  position: relative;
  flex: 1 1 var(--track-step-min); min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: var(--track-gap);
  padding: var(--sp-4) var(--sp-2) 0; text-align: center;
  font: var(--t-label); color: var(--text-2);
  overflow-wrap: break-word;
}
/* the connector runs from the previous step's centre to this one's, at the
   dot's own vertical centre */
.order-timeline__step::before {
  content: ''; position: absolute;
  left: -50%; right: 50%; top: calc(var(--sp-4) + var(--sp-32) / 2);
  bottom: auto; width: auto; height: var(--hair); background: var(--track-line);
}
.order-timeline__step:first-child::before { display: none; }
/* Each step now carries ITS OWN icon rather than four identical circles (LD:
   "it will be nice if each value had an icon of its own") - an inbox, an eye, a
   tick, a van, drawn from the same NAV_ICON_PATHS set and the same stroke
   weight as the side menu so the product keeps one icon system. The circle has
   to grow from 18px to 32px to hold one, which is the only measurement that
   moved. The STATE is carried exactly as it was: the circle's own fill (dark,
   yellow, white - three distinct lightnesses, so it survives greyscale), the
   glow ring on the current step, and the printed label's weight. No colour
   changed; the CSS tick is gone only because the icon it sat behind replaced
   it. */
.order-timeline__dot {
  position: relative;
  flex: 0 0 var(--sp-32); width: var(--sp-32); height: var(--sp-32);
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--track-future); box-shadow: inset 0 0 0 var(--hair) var(--track-future-edge);
  color: var(--track-future-edge);
}
.order-timeline__icon {
  width: var(--icon-inline); height: var(--icon-inline);
  fill: none; stroke: currentColor; stroke-width: var(--stroke-icon);
  stroke-linecap: round; stroke-linejoin: round;
}
.order-timeline__step.is-done { color: var(--ink); }
.order-timeline__step.is-done .order-timeline__dot {
  background: var(--track-done); box-shadow: none; color: var(--track-done-ink);
}
.order-timeline__step.is-done::before { background: var(--track-done); }
.order-timeline__step.is-current { font-weight: 700; color: var(--ink); }
.order-timeline__step.is-current .order-timeline__dot {
  background: var(--track-now);
  color: var(--track-now-edge);
  box-shadow: inset 0 0 0 var(--hair) var(--track-now-edge),
              0 0 0 var(--sp-4) var(--track-now-glow);
}
/* the held note that can follow the steps gets its own full-width line rather
   than becoming a fifth column */
.order-timeline > .hi { flex: 1 0 100%; margin-top: var(--sp-12); text-align: left; }

/* a real select, not an underlined word */
.order-rep__field { margin-bottom: 0; }
.order-rep__field select {
  font: var(--t-meta-strong); color: var(--ink); width: auto; max-width: 100%;
  min-height: var(--input-h); border: var(--hair) solid var(--control-edge);
  background: var(--paper); border-radius: var(--r-6); padding: 0 var(--sp-8);
  text-decoration: none; cursor: pointer;
}

/* =========================================================================
   MESSAGES, NOTES AND EMPTY STATES
   =========================================================================
   A plain banner stays paper with a bar. A banner that carries a STATE takes
   that state's pale tint and ink: green for good, pale yellow for something
   that wants attention, pale red for money that is overdue. Pale tints only —
   a banner is never a solid accent fill, because a solid accent fill in this
   product means "press me" and a banner is not a button. The one standing
   exception to "red is overdue money only" is a system or validation error
   sentence, which has nowhere else to go. */
.banner {
  background: none; border-radius: 0;
  box-shadow: inset var(--bar-ink) 0 0 var(--ink);
  padding: var(--sp-8) 0 var(--sp-8) var(--sp-12);
  font: var(--t-meta); font-variant-numeric: var(--num); color: var(--text-2);
  margin-bottom: var(--sp-12);
}
.banner__title { display: block; font: var(--t-card-title); color: var(--ink); margin-bottom: var(--sp-2); }
.banner.gold, .banner.ok, .banner.stop {
  border-radius: var(--r-12);
  padding: var(--sp-12); box-shadow: none;
}
.banner.gold { background: var(--warn-bg); color: var(--warn); }
.banner.gold .banner__title { color: var(--ink); }
.banner.ok { background: var(--ok-bg); color: var(--ok); }
.banner.ok .banner__title { color: var(--ink); }
.banner.stop { background: var(--stop-bg); color: var(--stop); }
.banner.stop .banner__title { color: var(--stop); }
.banner .stack-tight { margin-top: var(--sp-8); font: var(--t-meta); font-variant-numeric: var(--num); color: var(--text-2); }
.banner.gold .stack-tight, .banner.ok .stack-tight, .banner.stop .stack-tight { color: inherit; }
.banner .stack-tight + .stack-tight { padding-top: var(--sp-8); border-top: var(--hair) solid var(--hairline); }

/* a stock shortfall is exactly what "needs attention" means, so it is the pale
   yellow status tint and the attention ink — never the solid yellow fill */
.stock-warn {
  font: var(--t-meta); color: var(--warn);
  background: var(--warn-bg); border-radius: var(--r-12);
  box-shadow: none;
  padding: var(--sp-8) var(--sp-12); margin: 0 0 var(--sp-8);
}

/* an empty state is two sentences on ground. No ring graphic, no illustration:
   the distributor is here to see that nothing is waiting, not to be charmed. */
.empty {
  text-align: left; color: var(--text-2); font: var(--t-meta);
  padding: var(--sp-24) var(--sp-16);
  border-radius: var(--r-12); background: var(--ground);
}
.empty__title { font: var(--t-card-title); color: var(--ink); margin-bottom: var(--sp-2); }
.list-rows > .empty { background: var(--paper); border-radius: 0; }

.placeholder {
  display: flex; flex-direction: column; justify-content: center;
  text-align: left; gap: var(--sp-2);
  min-height: calc(100vh - var(--sp-40) - var(--sp-40));
  border-radius: var(--r-12); background: var(--ground);
  padding: var(--sp-24); color: var(--text-2); font: var(--t-meta);
}
.placeholder__title { font: var(--t-card-title); color: var(--ink); }
.placeholder p { max-width: var(--measure-tight); }
/* Schemes: the detail pane here is wide enough that a plain tint reads as
   an empty hole in the page rather than a considered "nothing picked yet"
   state (LD, via XO: "floating as bare text with no card"). The list still
   gets the plain tint everywhere else - only this wider pane gets the same
   card frame the picked-offer view itself uses. */
.workspace--wide-list .placeholder {
  background: var(--paper); border: var(--hair) solid var(--hairline);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
  /* A framed card should size to its two lines of text, not to the pane -
     the plain tint above fills the pane on purpose (an empty hole reads as
     nothing to see here); a bordered card that size reads as broken instead. */
  min-height: 0;
}

/* the toast is level 4: an ink fill with paper text */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: var(--sp-24); z-index: var(--z-toast);
  background: var(--ink); color: var(--on-ink); padding: var(--sp-8) var(--sp-16); border-radius: var(--r-pill);
  font: var(--t-meta-strong); font-variant-numeric: var(--num);
  max-width: var(--toast-max-w); box-shadow: var(--sh-overlay);
}

/* =========================================================================
   KEY / VALUE — the rail ledger
   =========================================================================
   Label 12/500 in Text 2 on the left, value 13/500 in Ink on the right, 6px
   between rows, and NO hairline per row: this is the single change that makes
   the rail read like 2026 without touching a word inside it. */
.kv { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-6) var(--sp-16); margin-top: var(--sp-12); }
.kv dt { color: var(--text-2); font: var(--t-label); display: flex; align-items: center; }
.kv dd {
  text-align: right; font: var(--t-meta-strong); font-variant-numeric: var(--num);
  font-stretch: var(--wd-num); color: var(--ink);
  display: flex; align-items: center; justify-content: flex-end; overflow-wrap: anywhere;
}

/* =========================================================================
   THE INVOICE - a standard from/to layout, bigger throughout (LD: "From and
   To could be a little bigger, one on the left one on the right. All the
   fonts can be bigger."). Body copy steps up from meta (13px) to body
   (15px); the party names step up to the same size as a page title. Still
   only tokens already in the scale, nothing invented.
   ========================================================================= */
.invoice { font: var(--t-body); }
.invoice__head { display: flex; justify-content: space-between; gap: var(--sp-32); flex-wrap: wrap; margin-bottom: var(--gap-section); }
.invoice__head > div:last-child { text-align: right; }
.invoice__label { font: var(--t-section); color: var(--text-2); }
/* the party names are store names, so they take the store name type */
.invoice__party { font: var(--t-store); color: var(--ink); margin: var(--sp-4) 0; }
.invoice h4 { font: var(--t-card-title); color: var(--ink); margin: var(--gap-section) 0 var(--sp-8); }
.invoice h4:first-of-type {
  margin-top: var(--sp-20); padding-top: var(--sp-20);
  border-top: var(--hair) solid var(--hairline);
}
.invoice .kv dt, .invoice .kv dd { font-size: var(--fs-body); }
.invoice-table th, .invoice-table td { font: var(--t-body); }
.invoice-table th:nth-child(4), .invoice-table td:nth-child(4) { text-align: right; }

/* Printing a bill shows the invoice and nothing else: not the app chrome, not
   the sheet's own title or close button, not the mark-as-paid form. The
   palette is forced back to light in tokens.css so it survives dark mode. */
/* A standard invoice print format (LD: "think of a standard invoice print
   format, check online, there must be many that are considered standard").
   One clean page: a modest margin, body copy a point size up again over the
   screen version, and nothing but the invoice itself - no app chrome, no
   sheet title, no buttons. The preview (the same markup, the same CSS
   minus this block) already looks like this printout, just smaller and in
   a scrollable sheet - it never has to guess at the print layout. */
@page { margin: 16mm; }
@media print {
  /* the gloss comes off before anything else: a gradient and a drop shadow on
     a cheap inkjet is a smear, and this page is a legal-ish document */
  *, *::before, *::after {
    background-image: none !important;
    box-shadow: none !important;
    text-shadow: none !important;
    filter: none !important;
  }
  .card::before { display: none !important; }
  .app-frame, .mobile-bar, .app-bar, .toast, .sidebar, .sidebar-scrim { display: none !important; }
  .scrim {
    position: static !important; background: none !important;
    backdrop-filter: none !important; display: block !important; padding: 0 !important;
  }
  .sheet {
    display: block !important; position: static !important; max-width: none !important; max-height: none !important;
    box-shadow: none !important; border: 0 !important; padding: 0 !important;
    overflow: visible !important; border-radius: 0 !important;
  }
  .dialog__scroll { overflow: visible !important; padding: 0 !important; }
  .dialog__head, .dialog__foot { display: none !important; }
  .sheet-body > *:not(.invoice) { display: none !important; }
  .invoice { break-inside: avoid; }
  .invoice-table { break-inside: auto; }
  .invoice-table tr { break-inside: avoid; }
  /* Every item row prints (06o BUG24): the item box drops its screen height
     and scroll, and a long bill runs on to the next page with rows whole. */
  .table-wrap { max-height: none !important; overflow: visible !important; }
  .bill-items tr { break-inside: avoid; }
}

/* =========================================================================
   SETUP, PRODUCTS AND STOCK
   ========================================================================= */
.toggle-row { flex-direction: row; align-items: center; gap: var(--sp-12); }
.toggle-row input[type="checkbox"] { width: var(--sp-20); height: var(--sp-20); accent-color: var(--ink); }

.stock-rows { gap: 0; }
.stock-row {
  display: flex; align-items: center; gap: var(--sp-16);
  padding: var(--sp-8) var(--row-pad-x); min-height: var(--row-h);
  border-top: var(--hair) solid var(--hairline);
  border-radius: 0; background: var(--paper);
}
.stock-row:first-child { border-top: 0; }
.stock-row .g { flex: 1; min-width: 0; }
.stock-row .t { font: var(--t-row-name); font-variant-numeric: var(--num); color: var(--ink); }
.stock-row .s { font: var(--t-meta); font-variant-numeric: var(--num); color: var(--text-2); margin-top: var(--sp-2); }
.stock-row__field { flex: 0 0 var(--field-sm); margin-bottom: 0; }
.stock-row__field > span { font: var(--t-label); color: var(--text-2); margin-bottom: var(--sp-2); }

/* =========================================================================
   ONBOARDING
   ========================================================================= */
.onboarding-scrim {
  position: fixed; inset: 0; background: rgba(var(--scrim-rgb), .45); z-index: var(--z-scrim);
  display: flex; align-items: center; justify-content: center; padding: var(--sp-16);
  backdrop-filter: blur(var(--blur-scrim));
}
.onboarding {
  background: var(--paper); border: var(--hair) solid var(--hairline);
  border-radius: var(--r-12); box-shadow: var(--sh-overlay);
  width: 100%; max-width: var(--modal-max-w); max-height: var(--modal-max-h);
  overflow-y: auto; padding: var(--sp-20);
}
.onboarding__top { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-12); margin-bottom: var(--gap-section); }
.onboarding__steps { display: flex; gap: var(--sp-8); align-items: center; }
.onboarding__dot {
  width: var(--dot); height: var(--dot); border-radius: 50%; background: var(--ring); flex: 0 0 auto;
  transition: background var(--dur-med) var(--ease-out), width var(--dur-med) var(--ease-out);
}
.onboarding__dot.is-active { background: var(--ink); width: var(--sp-20); border-radius: var(--r-4); }
.onboarding__dot.is-done { background: var(--text-3); }
.onboarding__skip {
  font: var(--t-meta-strong); color: var(--text-2); background: none; border: 0; cursor: pointer;
  text-decoration: none; padding: 0 var(--sp-8); min-height: var(--ctrl-h); border-radius: var(--r-6);
}
.onboarding__skip:hover { color: var(--ink); text-decoration: underline; }
.onboarding__skip:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

.onboarding__eyebrow { font: var(--t-label); color: var(--text-2); margin-bottom: var(--sp-4); }
.onboarding__title { font: var(--t-title); letter-spacing: var(--ls-title); margin-bottom: var(--sp-12); text-wrap: balance; }
.onboarding__body { margin-bottom: var(--gap-section); }
.onboarding__body p { font: var(--t-body); color: var(--text-2); max-width: var(--measure); }
.onboarding__body p + p { margin-top: var(--sp-12); }
.onboarding__body b { color: var(--ink); font-weight: 600; }
.onboarding__actions { display: flex; gap: var(--sp-8); margin-top: var(--sp-16); }
.onboarding__actions > * { flex: 0 0 auto; }

.onboarding__choice-row { display: flex; gap: var(--sp-8); flex-wrap: wrap; margin: var(--sp-16) 0; }
.onboarding__choice {
  flex: 1 1 var(--tile-min); min-height: var(--row-h); border-radius: var(--r-6);
  border: var(--hair) solid var(--control-edge); background: var(--paper); color: var(--ink);
  font: var(--t-body); cursor: pointer; padding: var(--sp-8) var(--sp-12); text-align: left;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.onboarding__choice:hover { border-color: var(--ink); background: var(--hover); }
.onboarding__choice:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.onboarding__choice[aria-pressed="true"] {
  border-color: var(--ink); box-shadow: inset 0 0 0 var(--hair) var(--ink); background: var(--selected);
}
.onboarding__choice .n { display: block; font: var(--t-section); }
.onboarding__choice .u { display: block; font: var(--t-meta); color: var(--text-2); margin-top: var(--sp-2); }

/* progress is work already banked, so it is green: the same green that fills a
   finished step in the status tracker */
.onboarding__progress-track { background: var(--selected); border-radius: var(--r-pill); height: var(--sp-8); overflow: hidden; margin: var(--sp-16) 0; }
.onboarding__progress-bar { height: 100%; background: var(--track-done); border-radius: var(--r-pill); width: 0%; transition: width var(--dur-med) var(--ease-out); }
.onboarding__log { font: var(--t-meta); color: var(--text-2); min-height: var(--sp-32); }

.onboarding__bulk-row {
  display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap;
  margin-bottom: var(--sp-12); padding-bottom: var(--sp-12); border-bottom: var(--hair) solid var(--hairline);
}
.onboarding__bulk-row .onboarding__tally { margin-left: auto; margin-top: 0; }

.onboarding__shop-row {
  display: flex; align-items: center; gap: var(--sp-12);
  padding: var(--sp-8) 0; background: none; border-radius: 0;
  border-bottom: var(--hair) solid var(--rule);
}
.onboarding__shop-row .g { flex: 1; min-width: 0; }
.onboarding__shop-row .t { font: var(--t-row-name); }
.onboarding__shop-row .s { font: var(--t-meta); color: var(--text-2); }
.onboarding__shop-actions { display: flex; gap: var(--sp-8); flex: 0 0 auto; }
/* pressed is an Ink fill and a different glyph, so the choice is legible in
   greyscale and never mistaken for a credit answer */
.onboarding__pill-btn {
  width: var(--tap-min); height: var(--tap-min); min-width: var(--sp-32); min-height: var(--sp-32);
  border-radius: var(--r-6); flex: 0 0 auto;
  border: var(--hair) solid var(--control-edge); background: var(--paper); color: var(--text-2); cursor: pointer;
  display: flex; align-items: center; justify-content: center; padding: 0;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.onboarding__pill-btn:hover { border-color: var(--ink); color: var(--ink); }
.onboarding__pill-btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.onboarding__pill-btn.tick[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.onboarding__pill-btn.cross[aria-pressed="true"] { background: var(--paper); border-color: var(--ink); color: var(--ink); }
.onboarding__pill-btn svg {
  width: var(--icon-inline); height: var(--icon-inline); fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.onboarding__tally { font: var(--t-meta-strong); color: var(--text-2); margin-top: var(--sp-12); font-variant-numeric: var(--num); }

/* =========================================================================
   THE SALESMAN APP — a phone shape on purpose
   =========================================================================
   Salesman rethink 03j. The portal's younger sibling who lives on a phone:
   the same tokens, the same card language (.list-rows--cards, .row,
   .row__head, .row__sub, the credit chip, the fix sheet), with a phone's
   sense of where the thumb is. Every primary sits in a dock at the bottom of
   the screen; the mic is the primary on the Order screen; one filled
   primary per screen, never two.

   NOT ONE COLOUR IS SET HERE. Every colour below is a token from tokens.css,
   and the credit tones keep their meaning: Green is Good, Amber is Watch,
   Red is Stop, and a new store is the No source treatment with no colour.
   Sizes are composed from the spacing scale so the 72dp mic, the 56dp
   primary and the 44dp tile are tokens added together, not new numbers.

   Motion: one kind per event, 120 to 160ms, and all of it collapses under
   prefers-reduced-motion through the global rule at the top of this file. */
.app-shell--phone { max-width: var(--shell-max-phone); }
.sales-shell { padding-block: var(--sp-12) 0; }
.sales-page {
  display: flex; flex-direction: column; gap: var(--gap-card); min-width: 0;
  padding-bottom: calc(var(--sp-40) * 2 + var(--sp-16));
  animation: screen-in var(--dur-med) var(--ease-out);
}
@keyframes screen-in { from { opacity: 0; transform: translateX(var(--sp-24)); } to { opacity: 1; transform: none; } }
.sales-page--finder { padding-bottom: calc(var(--sp-40) * 3 + var(--sp-24)); }
.sales-page--store { padding-bottom: calc(var(--sp-40) * 3 + var(--sp-12)); }
.sales-page--order { padding-bottom: calc(var(--sp-40) * 5 + var(--sp-12)); }
.sales-page--phone { min-height: calc(100vh - var(--sp-40) * 5); }
.sales-page > .card { margin-bottom: 0; }
.sales-section { font: var(--t-card-title); color: var(--ink); margin: 0; }
.sales-title { display: flex; flex-direction: column; gap: var(--sp-2); }
.sales-title .page-title { margin-bottom: 0; }

/* English line, Hindi line, no toggle (N16) */
.hi-line { display: flex; flex-direction: column; gap: var(--sp-2); font: var(--t-meta); color: var(--text-2); margin: 0; }
.hi-line__hi { font: var(--t-meta); color: var(--text-3); font-style: italic; }
.btn .hi-line__hi { font: var(--t-label); color: inherit; opacity: .85; font-style: normal; }

/* the status line: one fact, one line, never a banner (2.1). It lives ON the top
   bar, so it takes the bar's ladder and not the page's greys: 80 percent white
   resting at 5.53:1, full white on hover, and a white focus ring, because the
   green --focus ring would have vanished into the green bar. */
.sales-bar { row-gap: 0; }
.sales-status {
  flex: 1 1 100%; display: flex; align-items: center; gap: var(--sp-8);
  min-height: var(--tap-min); padding: 0; margin: calc(var(--sp-4) * -1) 0 calc(var(--sp-8) * -1);
  background: none; border: 0; text-align: left; cursor: pointer;
  font: var(--t-meta-strong); color: var(--nav-ink-2);
}
.sales-status:hover { color: var(--nav-ink); }
.sales-status:focus-visible { outline: var(--focus-ring) solid var(--nav-focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
.sales-icon {
  width: var(--icon-nav); height: var(--icon-nav); flex: 0 0 var(--icon-nav);
  fill: none; stroke: currentColor; stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
}

/* a screen header: the chevron, the name, one chip. Nothing he must tap in a
   hurry lives up here (6.2) */
.sales-head { display: flex; align-items: center; gap: var(--sp-8); min-width: 0; }
.sales-head__title { flex: 1 1 auto; min-width: 0; }
.sales-head__title .page-title { margin-bottom: 0; overflow-wrap: anywhere; }
.sales-head__name { font: var(--t-title); letter-spacing: var(--ls-title); }
.sales-head__chips { display: flex; flex-wrap: wrap; gap: var(--sp-8); min-width: 0; }
.sales-back {
  flex: 0 0 var(--tap-min); width: var(--tap-min); height: var(--tap-min); margin-left: calc(var(--sp-8) * -1);
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 50%; color: var(--ink); cursor: pointer;
}
.sales-back:hover { background: var(--hover); }
.sales-back:focus-visible { outline: var(--focus-ring) solid var(--focus); }
.sales-chip { cursor: pointer; flex: 0 0 auto; }
.sales-chip:hover { background: var(--hover); border-color: var(--text-2); }
.sales-chip:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* the finder's groups: one quiet label at a time (2.1) */
.sales-list { display: flex; flex-direction: column; gap: var(--sp-8); }
.sales-group-label {
  display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap;
  font: var(--t-meta-strong); color: var(--text-2); margin: var(--sp-4) 0 0;
}
.sales-group-label--divider { margin-top: var(--sp-8); padding-top: var(--sp-12); border-top: var(--hair) solid var(--hairline); }
.sales-notice { font: var(--t-meta); color: var(--text-2); padding: var(--sp-8) var(--sp-12); background: var(--ground); border-radius: var(--r-8); }
.sales-cards { gap: var(--sp-12); }

/* a store card (6.3): monogram tile, name with the area beside it, the
   credit chip or the Waiting tag on the second line. Minimum 72dp tall, the
   whole card is the tap target. */
.store-card {
  display: flex; align-items: center; gap: var(--sp-12);
  min-height: calc(var(--sp-40) + var(--sp-32)); border-radius: var(--r-12);
}
.list-rows--cards .store-card { border-radius: var(--r-12); }
.store-card__tile {
  flex: 0 0 var(--tap-min); width: var(--tap-min); height: var(--tap-min);
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-10); background: var(--selected); color: var(--ink);
  font: var(--t-total); letter-spacing: var(--ls-title);
}
.store-card__body { flex: 1 1 auto; min-width: 0; }
.store-card__area { font: var(--t-meta); color: var(--text-2); white-space: nowrap; margin-left: auto; flex: 0 0 auto; }
.store-card .row__name { font: var(--t-section); }
.store-card .row__sub { margin-top: var(--sp-4); }

/* the dock: everything he must tap in a hurry, under his thumb (6.2) */
.sales-dock {
  position: fixed; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 100%; max-width: var(--shell-max-phone); z-index: var(--z-actionbar);
  display: flex; flex-direction: column; gap: var(--sp-8);
  padding: var(--sp-12) var(--gutter) calc(var(--sp-12) + env(safe-area-inset-bottom, 0px));
  background: var(--paper); border-top: var(--hair) solid var(--hairline);
  box-shadow: 0 calc(var(--sp-8) * -1) var(--sp-24) rgba(var(--sh-rgb), .06);
}
/* The dock is the salesman app's pinned bar: the one place a button fills
   its container (03n F4, the G6 exception). */
.sales-dock .btn { width: 100%; max-width: none; }
.sales-primary {
  width: 100%; min-height: calc(var(--sp-40) + var(--sp-16));
  flex-direction: column; gap: 0; line-height: 1.2; border-radius: var(--r-12);
}
.sales-newstore { min-height: var(--tap-min); flex-direction: column; gap: 0; line-height: 1.2; }
.sales-search { position: relative; display: flex; align-items: center; gap: var(--sp-8); }
.sales-search__field, input[type=search].sales-search__field {
  flex: 1 1 auto; min-width: 0; min-height: var(--mic-h);
  padding-left: calc(var(--sp-32) + var(--sp-8)); border-radius: var(--r-pill);
  font: var(--t-body); border-color: var(--control-edge);
}
.sales-search__icon {
  position: absolute; left: var(--sp-12); top: 50%; transform: translateY(-50%);
  width: var(--icon-nav); height: var(--icon-nav); color: var(--text-2);
  fill: none; stroke: currentColor; stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
  pointer-events: none;
}
.sales-search__note { font: var(--t-label); color: var(--text-2); max-width: 7rem; }

/* the round mic: the one action the finder's search and the Order screen
   lead with. The 48dp form sits in the search field; the 72dp form is the
   primary on the Order screen. Same primary finish as every action in the
   product; the ring while it listens is the theme's own pulse token, the
   button's edge rather than its fill, so it is seen in both themes. */
.mic-round {
  flex: 0 0 auto; width: var(--input-h); height: var(--input-h); padding: 0;
  display: inline-flex; align-items: center; justify-content: center; border-radius: 50%;
  border: var(--hair) solid var(--btn-primary-edge);
  background-color: var(--btn-primary-bg);
  background-image: linear-gradient(to bottom, var(--btn-primary-top), var(--btn-primary-bottom));
  color: var(--btn-primary-ink); cursor: pointer;
  box-shadow: inset 0 var(--hair) 0 var(--gloss-top), var(--sh-btn),
              0 var(--sp-2) var(--sp-6) calc(var(--hair) * -1) var(--gloss-bottom);
  transition: box-shadow var(--dur-fast) var(--ease-out);
}
.mic-round .voice__icon { width: var(--icon-credit-panel); height: var(--icon-credit-panel); flex-basis: var(--icon-credit-panel); }
.mic-round--big { width: calc(var(--sp-40) + var(--sp-32)); height: calc(var(--sp-40) + var(--sp-32)); }
.mic-round--big .voice__icon { width: var(--sp-32); height: var(--sp-32); flex-basis: var(--sp-32); }
.mic-round:hover { background-image: linear-gradient(to bottom, var(--btn-primary-hover-top), var(--btn-primary-hover-bottom)); }
.mic-round:active { transform: translateY(var(--hair)); }
.mic-round:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.mic-round[disabled] { opacity: .5; cursor: not-allowed; box-shadow: none; }
.mic-round.is-listening { animation: mic-pulse 1.8s var(--ease-out) infinite; }
@media (prefers-reduced-motion: reduce) {
  .mic-round.is-listening { animation: none; box-shadow: 0 0 0 var(--sp-4) rgba(var(--pulse-rgb), .9); }
}

/* the Order screen's dock: the send button above the mic row (5.2) */
.sales-dock__send { display: flex; flex-direction: column; gap: var(--sp-8); }
.sales-reassure { text-align: center; align-items: center; }
.sales-mic-row { display: flex; align-items: center; gap: var(--sp-12); }
.sales-type, input[type=text].sales-type { flex: 1 1 auto; min-width: 0; min-height: var(--mic-h); border-radius: var(--r-12); font: var(--t-body); }
.sales-mic-status { font: var(--t-meta); color: var(--text-2); margin: 0; min-height: 1.4em; }
.sales-mic-status.is-live { color: var(--ink); font-weight: 500; }
.sales-mic-status.is-error { color: var(--stop); font-weight: 500; }
.sales-mic-status:empty { display: none; }

/* the Order screen's body: the count line, then the line cards */
.sales-order-body { display: flex; flex-direction: column; gap: var(--sp-12); }
.sales-count { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-12); flex-wrap: wrap; font: var(--t-meta); color: var(--text-2); }
.sales-value { font: var(--t-total); color: var(--price); font-variant-numeric: var(--num); font-stretch: var(--wd-num); letter-spacing: var(--ls-figure); }
.sales-rates { font: var(--t-meta-strong); color: var(--ink); text-align: right; }
.sales-rates .hi-line__hi { display: block; }
.chips-row { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.chip--purchase, .unit-chips .chip { cursor: pointer; min-height: var(--tap-min); padding: 0 var(--sp-12); }
.chip--purchase:hover, .unit-chips .chip:hover { background: var(--hover); border-color: var(--text-2); }
.chip:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.chip.is-selected { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }

/* a line card (6.3): what was said, the quantity right aligned, the matched
   item on the quiet line. No red on a line: a line that needs a look carries
   the warn bar the owner's own list uses, never the stop treatment (5.3). */
.order-line { touch-action: pan-y; position: relative; }
.order-line .row__head { align-items: flex-start; gap: var(--sp-12); }
.order-line__said { font: var(--t-section); }
.order-line__tap { flex: 1 1 auto; min-width: 0; background: none; border: 0; padding: 0; margin: 0; text-align: left; color: inherit; font: inherit; cursor: pointer; }
.order-line__tap:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
.order-line__qty {
  flex: 0 0 auto; font: var(--t-total); color: var(--ink); white-space: nowrap;
  font-variant-numeric: var(--num); font-stretch: var(--wd-num); letter-spacing: var(--ls-figure);
}
.order-line__qty--tap {
  min-height: var(--tap-min); padding: 0 var(--sp-12); border-radius: var(--r-8);
  border: var(--hair) solid var(--control-edge); background: var(--paper); cursor: pointer;
}
.order-line__qty--tap:hover { border-color: var(--ink); }
.order-line__qty--tap:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.list-rows--cards .row.is-flagged {
  box-shadow: inset var(--bar-ink) 0 0 var(--warn), inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}
.order-line.is-new { animation: line-in var(--dur-med) var(--ease-out) both; animation-delay: calc(var(--i, 0) * 40ms); }
@keyframes line-in { from { opacity: 0; transform: translateY(var(--sp-8)); } to { opacity: 1; transform: none; } }
.order-line.is-swiping { transition: none; user-select: none; }
.order-line.is-leaving { transition: transform 150ms var(--ease-out), opacity 150ms var(--ease-out); transform: translateX(-100%); opacity: 0; }
.sales-undo {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--sp-40) * 5 + var(--sp-24)); z-index: var(--z-toast);
  display: flex; align-items: center; gap: var(--sp-12);
  background: var(--ink); color: var(--on-ink); border-radius: var(--r-pill);
  padding: var(--sp-6) var(--sp-8) var(--sp-6) var(--sp-16);
  font: var(--t-meta-strong); box-shadow: var(--sh-overlay);
}
.sales-undo .btn { min-height: var(--ctrl-h-sm); }

/* a meta line on a phone sized card has to wrap: the single ellipsed line the
   portal's wide rows use would hide the half of it that matters */
.row__meta--wrap { white-space: normal; overflow: visible; text-overflow: clip; }
.row__sub { flex-wrap: wrap; }

/* the credit answer on the store screen, and the three choices line for Red */
.sales-credit-card .credit.big { margin-bottom: 0; }
.sales-red-line { margin-top: var(--sp-12); font: var(--t-meta-strong); color: var(--ink); }

/* the new store chip in the Order header: the No source treatment, no colour */
.sales-newstore-chip .word { white-space: normal; }

/* The salesman app's dialogs are the same centred Dialog as the owner's
   (03n F6 AC4): no bottom sheet, no drag handle. */
.sales-sheet-primary { margin-top: var(--sp-16); min-height: calc(var(--sp-40) + var(--sp-16)); flex-direction: column; gap: 0; line-height: 1.2; }

/* Sheet B: quantity without a keyboard */
.qty-stepper { display: flex; align-items: center; justify-content: center; gap: var(--sp-16); margin: var(--sp-12) 0 var(--sp-16); }
.qty-stepper__btn {
  width: calc(var(--sp-40) + var(--sp-16)); height: calc(var(--sp-40) + var(--sp-16)); border-radius: 50%;
  border: var(--hair) solid var(--control-edge); background: var(--paper); color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.qty-stepper__btn:hover { border-color: var(--ink); background: var(--hover); }
.qty-stepper__btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.qty-stepper__btn .sales-icon { width: var(--icon-credit-panel); height: var(--icon-credit-panel); }
.qty-stepper__figure {
  min-width: 4rem; padding: var(--sp-4); text-align: center; background: none; border: 0;
  border-bottom: var(--hair-strong) solid var(--control-edge); color: var(--ink); cursor: pointer;
  font: var(--t-code); font-variant-numeric: var(--num);
}
.qty-stepper__figure:focus-visible { outline: var(--focus-ring) solid var(--focus); }
input[type=number].qty-stepper__input { width: 6rem; text-align: center; font: var(--t-code); min-height: var(--mic-h); }

/* S5 Who is this? */
.sales-who { display: flex; flex-direction: column; }
.sales-nomobile { margin-top: var(--sp-8); }
.sales-field-line { margin-top: var(--sp-4); }
.sales-note-row { display: flex; align-items: flex-end; gap: var(--sp-8); }
.sales-note-row .field { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
.sales-note-row .mic-round { margin-bottom: 0; }
.sales-soundalike {
  display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-4) 0; margin-top: var(--sp-4);
  background: none; border: 0; text-align: left; color: var(--pine); cursor: pointer; font: var(--t-meta-strong);
}
.sales-soundalike:hover { text-decoration: underline; }
.sales-dup {
  display: flex; flex-direction: column; gap: var(--sp-12); padding: var(--sp-16);
  border: var(--hair) solid var(--hairline); border-radius: var(--r-12); background: var(--ground);
}
.sales-dup__line { font: var(--t-body); color: var(--ink); }
/* a validation error is the one non credit use of red the direction allows */
input.is-invalid { border-color: var(--stop); }

/* S7: the tick draws itself once; the phone outline draws itself, no tick.
   The two never share an icon or a motion (6.4). */
.sent-hero { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--sp-8); padding: var(--sp-32) 0 var(--sp-8); }
.sent-hero .page-title { margin-bottom: 0; }
.sent-hero__hi { margin-top: calc(var(--sp-4) * -1); }
.sent-hero__store { font: var(--t-title); color: var(--ink); margin-top: var(--sp-8); overflow-wrap: anywhere; }
.sent-mark { width: calc(var(--sp-40) * 2); height: calc(var(--sp-40) * 2); color: var(--pine); }
.sent-mark--phone { color: var(--ink); }
.sent-mark__draw { stroke-dasharray: 200; stroke-dashoffset: 200; animation: mark-draw 300ms var(--ease-out) forwards; }
@keyframes mark-draw { to { stroke-dashoffset: 0; } }
.sent-line { text-align: center; align-items: center; }
.sales-maysay { margin-top: var(--sp-8); }
.sales-maysay__text { font: var(--t-body); color: var(--ink); gap: var(--sp-8); }
.sales-maysay__text .hi-line__hi { font: var(--t-meta); }

/* S6 On this phone */
.sales-lost-line { margin-top: auto; padding-top: var(--sp-16); }
.sales-willsend { text-align: center; align-items: center; min-height: var(--tap-min); justify-content: center; }

.code-input {
  text-transform: uppercase; letter-spacing: var(--ls-code); text-align: center;
  font: var(--t-code); font-variant-numeric: var(--num); min-height: var(--mic-h);
}
.plain-list { padding-left: var(--sp-20); margin-top: var(--sp-12); color: var(--text-2); font: var(--t-body); }
.plain-list li + li { margin-top: var(--sp-8); }
.plain-list li::marker { color: var(--text-3); }

/* =========================================================================
   THE BULK DESK — calm at two hundred rows
   =========================================================================
   One paper frame with a hairline, a sticky paper head, ruled register cells,
   and NO ZEBRA: alternate banding is a 2005 idiom and at this row count a
   single rule per row tracks the eye just as well for half the visual noise.
   Statuses are ink, a word and a bar — never a fill, never green, never red.
   Duplicate keeps Decide blue, the only blue in the product, because "you must
   choose" has to be unmistakable from "this is broken". */
.bulk-desk__subtitle { font: var(--t-body); color: var(--text-2); margin: var(--sp-8) 0 var(--sp-12); max-width: var(--measure-wide); }
.bulk-desk__counts {
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
  background: none; border-radius: 0; padding: 0 0 var(--sp-8);
  margin: var(--sp-12) 0; border-bottom: var(--hair) solid var(--hairline);
}
.bulk-desk__counts b { color: var(--ink); font-weight: 600; font-variant-numeric: var(--num); }

.bulk-grid-wrap {
  overflow: auto; overscroll-behavior: contain;
  max-height: var(--grid-max-h);
  border-radius: var(--r-12); background: var(--paper);
  border: var(--hair) solid var(--hairline);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
}
.bulk-grid { display: flex; flex-direction: column; gap: 0; min-width: var(--grid-min-w); }

.bulk-grid__headrow {
  position: sticky; top: 0; z-index: var(--z-sticky-sub);
  display: flex; gap: var(--sp-8); padding: 0 var(--sp-12);
  background: var(--paper);
  border-bottom: var(--hair) solid var(--hairline);
}
.bulk-grid__headrow > span {
  flex: 1 0 var(--cell-min); min-width: 0;
  font: var(--t-label); color: var(--text-2);
  padding: var(--sp-8); min-height: var(--table-row-h);
  display: flex; align-items: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bulk-grid__headrow > span.bulk-col--status { flex: 0 0 var(--col-status); }
.bulk-grid__headrow > span.bulk-col--actions { flex: 0 0 var(--tap-min); }

.bulk-row {
  background: var(--paper); border: 0; border-top: var(--hair) solid var(--rule);
  border-radius: 0; padding: 0 var(--sp-12);
}
.bulk-grid > .bulk-row:first-of-type { border-top: 0; }
.bulk-grid > .bulk-row:hover { background: var(--hover); }
.bulk-grid > .bulk-row:focus-within { background: var(--hover); }

.bulk-row__cells { display: flex; gap: var(--sp-8); align-items: center; }
.bulk-row__cells > * { flex: 1 0 var(--cell-min); min-width: 0; }

/* a cell, not a form field: transparent and ruled, with the sticky column head
   above acting as its visible label */
.bulk-grid .bulk-row__cells input {
  width: 100%; min-height: var(--table-row-h); padding: 0 var(--sp-8);
  background: transparent; color: var(--ink); font: var(--t-body);
  font-variant-numeric: var(--num);
  border: 0; border-bottom: var(--hair) solid var(--control-edge); border-radius: 0;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.bulk-grid .bulk-row__cells input:hover { border-bottom-color: var(--ink); background: transparent; }
.bulk-grid .bulk-row__cells input:focus-visible {
  outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-offset) * -1);
  background: var(--paper); border-bottom-color: var(--ink);
}
.bulk-grid .bulk-row__cells input::placeholder { color: var(--text-3); }

.bulk-row__status {
  flex: 0 0 var(--col-status);
  display: flex; align-items: center; gap: var(--sp-8);
  font: var(--t-meta-strong); color: var(--ink);
  min-height: var(--table-row-h); padding: 0 var(--sp-4); white-space: nowrap;
}
.bulk-row__status::before {
  content: ''; flex: 0 0 var(--dot); width: var(--dot); height: var(--dot);
  border-radius: 50%; background: currentColor;
}
/* new and update are the calm majority and commit together, so they share Ink
   and are told apart by the word itself. Skipped goes quiet. */
.bulk-row__status.is-new, .bulk-row__status.is-update { color: var(--ink); }
.bulk-row__status.is-problem { color: var(--warn); }
.bulk-row__status.is-update::before, .bulk-row__status.is-problem::before {
  background: transparent; box-shadow: inset 0 0 0 var(--hair-strong) currentColor;
}
.bulk-row__status.is-duplicate { color: var(--decide); }
.bulk-row__status.is-skip { color: var(--text-3); }
.bulk-row__status.is-skip::before { background: transparent; box-shadow: inset 0 0 0 var(--hair) currentColor; }

.bulk-row__del {
  flex: 0 0 var(--tap-min); width: var(--tap-min); min-height: var(--table-row-h);
  border: 0; background: transparent; color: var(--text-3);
  cursor: pointer; font: var(--t-section); border-radius: var(--r-6); padding: 0;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.bulk-row__del:hover { background: var(--selected); color: var(--ink); }
.bulk-row__del:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-offset) * -1); }

.bulk-row__note { font: var(--t-label); font-variant-numeric: var(--num); color: var(--ink); padding: 0 var(--sp-8) var(--sp-8); }
.bulk-row__note.is-info { color: var(--text-2); }

/* the row's own bar: an inset shadow, so flagging never shifts a cell */
.bulk-row--problem { box-shadow: inset var(--bar-ink) 0 0 var(--warn); }
.bulk-row--duplicate { box-shadow: inset var(--bar-ink) 0 0 var(--decide); }
.bulk-row--skip .bulk-row__cells input {
  color: var(--text-3); text-decoration: line-through; text-decoration-color: var(--ring);
}

.bulk-row__resolve {
  margin: 0 0 var(--sp-12); padding: var(--sp-12) var(--sp-16);
  background: var(--paper); border-radius: var(--r-12);
  border: var(--hair) solid var(--hairline);
  box-shadow: inset var(--bar-ink) 0 0 var(--decide);
}
.bulk-row__compare { font: var(--t-meta); color: var(--text-2); margin-bottom: var(--sp-12); }
.bulk-row__compare b { color: var(--ink); font-weight: 600; font-variant-numeric: var(--num); }

.bulk-paste-target {
  border: var(--hair) dashed var(--control-edge); border-radius: var(--r-8); padding: var(--sp-16);
  background: none; margin-bottom: var(--sp-16);
}
.bulk-paste-target .field { margin-bottom: var(--sp-12); }

.bulk-mapping-row {
  display: flex; gap: var(--sp-12); align-items: center; padding: var(--sp-8) 0; flex-wrap: wrap;
  border-bottom: var(--hair) solid var(--rule);
}
.bulk-mapping-row:last-of-type { border-bottom: 0; }
.bulk-mapping-row > span:first-child { flex: 0 0 var(--label-col); font: var(--t-meta-strong); }
.bulk-mapping-row > select { flex: 1 1 var(--select-min); }
.bulk-mapping-row .sample { flex: 1 1 100%; font: var(--t-meta); color: var(--text-2); }
.bulk-unmapped { font: var(--t-meta); color: var(--text-2); margin-top: var(--sp-16); }
.bulk-unmapped .chip { margin: 0 var(--sp-8) var(--sp-8) 0; }

.bulk-desk__commitbar { position: static; margin-top: var(--sp-16); }
.bulk-desk__commitbar > .btn { flex: 0 0 auto; margin-left: auto; }
.bulk-desk__commitbar > .blockmsg { flex: 1 1 auto; }

/* =========================================================================
   THE PHONE LINE
   =========================================================================
   Layout only: every size and target on a phone comes from the touch density
   set in tokens.css, so nothing here drops below 44px. */
@media (max-width: 560px) {
  .card-top { flex-direction: column; gap: var(--sp-8); }
  .card-top .right { margin-left: 0; text-align: left; }
  .limit-row__field { flex: 1 1 100%; }
  .scheme-row { flex-direction: column; }
  .stock-row { flex-wrap: wrap; }
  .stock-row__field { flex: 1 1 100%; }
  .order-header__meta { margin-left: 0; }
  .invoice__head { gap: var(--sp-16); }

  /* the grid becomes a one-row card stack: same fields, same statuses, same
     errors. A phone shows five rows, not two hundred, so each row goes back to
     being its own card and the internal scroll is released. */
  .bulk-grid-wrap {
    max-height: none; overflow: visible;
    background: transparent; border: 0; border-radius: 0;
  }
  .bulk-grid { min-width: 0; gap: var(--sp-8); }
  .bulk-grid__headrow { display: none; }
  .bulk-row {
    position: relative;
    background: var(--paper); border-radius: var(--r-12);
    border: var(--hair) solid var(--hairline);
    padding: var(--sp-12) var(--sp-16);
    box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
  }
  /* the row's state bar has to survive becoming a card, so it is restated here
     WITH the card shadow rather than being overwritten by it */
  .bulk-row--problem { box-shadow: inset var(--bar-ink) 0 0 var(--warn), var(--sh-card); }
  .bulk-row--duplicate { box-shadow: inset var(--bar-ink) 0 0 var(--decide), var(--sh-card); }
  .bulk-grid > .bulk-row:first-of-type { border-top: var(--hair) solid var(--hairline); }
  .bulk-grid > .bulk-row:hover,
  .bulk-grid > .bulk-row:focus-within { background: var(--paper); }
  .bulk-row__cells { flex-direction: column; align-items: stretch; gap: var(--sp-8); }
  .bulk-row__cells > * { flex: 0 0 auto; width: 100%; }
  .bulk-row__status { order: -1; flex: 0 0 auto; min-height: var(--sp-24); padding: 0 var(--tap-min) 0 0; }
  .bulk-row__del { position: absolute; top: var(--sp-8); right: var(--sp-8); width: var(--tap-min); height: var(--tap-min); }
  .bulk-row__note { padding-inline: 0; }
  /* the sticky column head labels a cell on a wide screen and is gone here, so
     a chromeless cell would be an unlabelled box. Stacked, each cell becomes a
     fully drawn field again. A visible per-cell LABEL needs a markup change. */
  .bulk-grid .bulk-row__cells input {
    background: var(--paper); border: var(--hair) solid var(--control-edge);
    border-radius: var(--r-6); padding: 0 var(--sp-8); min-height: var(--input-h);
  }
  .bulk-grid .bulk-row__cells input:hover { border-color: var(--ink); }
  .bulk-mapping-row > span:first-child { flex: 1 1 100%; }
}

/* =============================================================================
   NEW, PLAIN. Picasso re-skinned everything above; nothing below borrows a
   token that did not already exist, and every class here is new and will be
   restyled rather than fought with. Layout only - no colour opinions beyond
   the literal swatch previews, which are drawn from each theme's own hex
   values on purpose (see public/js/common.js themePreview()).
   ============================================================================= */

/* ---- theme picker (Setup + onboarding) ---- */
.theme-picker { display: flex; gap: var(--sp-16); flex-wrap: wrap; }
.theme-picker__option {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-4);
  width: 220px; max-width: 100%; padding: var(--sp-12); border-radius: var(--r-12);
  border: var(--hair) solid var(--hairline); background: var(--paper); cursor: pointer; text-align: left;
}
.theme-picker__option.is-selected { border-color: var(--pine); border-width: 2px; }
.theme-picker__label { font-weight: 700; margin-top: var(--sp-8); }
.theme-picker__blurb { color: var(--text-2); font-size: var(--fs-meta); }
.theme-preview { width: 100%; border-radius: var(--r-8); overflow: hidden; border: var(--hair) solid var(--hairline); }
.theme-preview__nav { height: 18px; }
.theme-preview__card { padding: var(--sp-12); display: flex; flex-direction: column; gap: var(--sp-6); }
.theme-preview__line { height: 8px; border-radius: 4px; width: 100%; }
.theme-preview__btn {
  align-self: flex-start; margin-top: var(--sp-4); padding: var(--sp-4) var(--sp-12);
  border-radius: var(--r-6); font-size: 12px; font-weight: 700;
}
.onboarding__theme-note { color: var(--text-2); font-size: var(--fs-meta); margin-top: var(--sp-8); }

/* ---- logo-as-home (5.5) ---- */
/* the wordmark on the phone strip, which goes home. It had no focus ring of its
   own and fell back to the browser's, which was readable on the white bar and is
   not something to gamble on now the bar is green. */
.logo-home-btn { background: none; border: none; padding: 0; margin: 0; cursor: pointer; text-align: left; }
.logo-home-btn:focus-visible { outline: var(--focus-ring) solid var(--nav-focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }

/* ---- dashboard (03f section 5, 8.9) ---- */
/* LD, on his laptop: "the whole screen is not fully visible in one go, I have
   to scroll down to see those buttons, but when I open the same page in my
   connected monitor I am able to see everything. I think some of it should be
   scalable automatically depending on the screen size."

   So nothing on this page is a fixed size any more. Three mechanisms do the
   work, and they are the only ones:

     1 The gap and the card padding are clamp()ed against the VIEWPORT HEIGHT.
       A short laptop screen tightens them; a tall monitor relaxes them back to
       exactly the values the page has today. Nothing ever gets bigger than it
       is now.
     2 The big figures are clamp()ed against the viewport WIDTH, so a number
       that reads at 1280 is not the same number stretched at 1900.
     3 Both grids are auto-fit, so the column count follows the room available
       instead of a breakpoint guessing at it. auto-fit collapses the tracks it
       cannot fill, which is why four tiles always span the full row and three
       cards always do too, at any width, with no dead air on a wide monitor.

   The quick actions used to be a band at the BOTTOM, which is the part he had
   to scroll for. They are now on the header row beside the title, which is
   both what he asked for and the single biggest reason the page now fits.

   Not one colour changed here. Every value below is a token that already
   existed before this pass. */
.dashboard {
  container-type: inline-size; container-name: dash;
  --d-gap: clamp(var(--sp-20), 1.7vw, var(--sp-24));
  --d-pad: clamp(var(--sp-16), 1.4vw, var(--sp-24));
  display: flex; flex-direction: column; gap: var(--d-gap);
  padding: 0;
  max-width: var(--shell-max); margin-inline: auto;
}

/* the header row: title, a quieter line under it, actions pushed right */
.dashboard-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-12) var(--sp-16); flex-wrap: wrap;
}
.dashboard-head__text { min-width: 0; }
.dashboard-head__title {
  font-size: clamp(1.25rem, 1rem + 0.6vw, 1.625rem);
  margin-bottom: 0;
}
.dashboard-head__meta {
  font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
  margin: var(--sp-2) 0 0;
}
.dashboard-head__actions { display: flex; gap: var(--sp-8); flex-wrap: wrap; margin-left: auto; }

/* the two grids. The lower cards row places its cards against explicit
   container query breakpoints rather than leaning on auto-fit, and the figures
   row now does the same, for the same reason.

   WHY auto-fit had to go here. The row went from four figure cards to five
   when "Past their days" was added, and auto-fit picks a column count from the
   track minimum alone - so at any width that fits 4, 3 or 2 across it left ONE
   card alone on the last row at its natural width, which is the exact kind of
   untidiness LD has twice rejected this dashboard for. Placing the count
   explicitly, and spanning the fifth card across whatever remains of its row,
   means every row is full at every width:

     4 across  row of 4, then the 5th spanning all 4 columns
     3 across  row of 3, then the 5th spanning the remaining 2 columns
     2 across  rows of 2 and 2, then the 5th spanning both columns
     1 across  5 stacked, nothing to strand

   The breakpoints are the tile's own 12.5rem minimum times the column count
   plus the gaps, rounded up, so a column is only ever added once it genuinely
   fits. There is deliberately no 5 across rule: five 12.5rem tiles in one row
   squeezes every figure, and the money and count figures are the whole point
   of the row. The spanning card is still the same card with the same headline
   number - it does not become a banner and it gains no extra content to
   justify the width. Not one colour, token or spacing value changed here: the
   page is allowed to be taller rather than give back any of the whitespace
   LD just approved. */
.dashboard-tiles {
  display: grid; gap: var(--d-gap);
  grid-template-columns: minmax(0, 1fr);
}
@container dash (min-width: 28rem) {
  .dashboard-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dashboard-tiles > .dtile:nth-child(5) { grid-column: 1 / -1; }
}
@container dash (min-width: 42rem) {
  .dashboard-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dashboard-tiles > .dtile:nth-child(5) { grid-column: span 2; }
}
@container dash (min-width: 56rem) {
  .dashboard-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .dashboard-tiles > .dtile:nth-child(5) { grid-column: 1 / -1; }
}
.dashboard-cards {
  display: grid; gap: var(--d-gap); align-items: start;
  grid-template-columns: minmax(0, 1fr);
}
.dashboard-cards > .card { margin-bottom: 0; }
@container dash (min-width: 46rem) {
  .dashboard-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dashboard-cards > .dashboard-card--since { grid-column: 1 / -1; }
}
@container dash (min-width: 82rem) {
  .dashboard-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dashboard-cards > .dashboard-card--since { grid-column: auto; }
}

/* ---- a figure tile ----
   An icon chip beside a small label, the figure, an optional split bar, a
   supporting line that says something the figure does not, and a link where
   there is somewhere to go. */
.dtile {
  container-type: inline-size; container-name: dtile;
  background: var(--paper); border: var(--hair) solid var(--hairline);
  border-radius: var(--r-12); padding: var(--d-pad);
  box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
  display: flex; flex-direction: column; gap: var(--sp-8); min-width: 0;
}

/* THE WHOLE CARD IS THE TARGET (LD: "if we make the whole card clickable I
   think that will be nice"). A tile with somewhere to go is rendered as a real
   <button>, so it is in the tab order, announces itself as a button and fires
   on Enter and Space without a line of our own code. These four lines only
   undo what a browser does to a button - the look, the alignment and the
   inherited type are exactly the .dtile above. No colour, no shadow, no size
   moved; the hover is the same --hover every other clickable surface uses. */
.dtile--go {
  width: 100%; text-align: left; font: inherit; color: inherit;
  -webkit-appearance: none; appearance: none; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.dtile--go:hover { background-color: var(--hover); }
.dtile--go:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* the unit line: what the figure above it is measured in. It is what keeps the
   two credit tiles apart without a colour - one says "over the limit, in
   total" under an amount, the other "past the N you gave" under a span of
   days. */
.dtile__unit {
  margin: calc(var(--sp-8) * -1) 0 0; font: var(--t-meta-strong); color: var(--text-2);
  font-variant-numeric: var(--num);
}
/* THE SECOND, SHAPE LEVEL CARRIER. The money card keeps the rounded square
   chip every other tile has; the days card's chip is a circle, a clock face,
   so the two are separable from across a desk before a single word is read.
   Shape only - the fill, the edge, the icon colour and the size are the same
   tokens as every other chip on the row. */
.dtile--days .dtile__chip { border-radius: 50%; }
.dtile--money .dtile__figure,
.dtile--days .dtile__figure { letter-spacing: var(--ls-figure); }
.dtile__top { display: flex; align-items: center; gap: var(--sp-8); min-width: 0; }
.dtile__chip {
  flex: 0 0 auto; border-radius: var(--r-8);
  width: clamp(1.5rem, 3.6vh, var(--sp-32)); height: clamp(1.5rem, 3.6vh, var(--sp-32));
  background: var(--ground); border: var(--hair) solid var(--hairline);
  display: inline-flex; align-items: center; justify-content: center; color: var(--pine);
}
.dtile__icon {
  width: var(--icon-inline); height: var(--icon-inline); fill: none; stroke: currentColor;
  stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
}
.dtile__label { font: var(--t-label); color: var(--text-2); min-width: 0; }
.dtile__figure {
  font-family: var(--font-latin); font-weight: 800; color: var(--ink);
  letter-spacing: var(--ls-figure); font-variant-numeric: var(--num); font-stretch: var(--wd-num);
  font-size: clamp(1rem, min(0.8rem + 0.85vw, 12cqi), 1.5rem); line-height: 1.1;
}
.dtile__support {
  margin: 0; font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4);
}
/* the overlap line on the two credit tiles: "3 of these are also past their
   days". A third line, quieter than the support line above it, so the two
   counts are never read as adding up. Same two tokens the support line already
   uses - no new colour, no new size. */
.dtile__note {
  margin: 0; font: var(--t-meta); color: var(--text-3);
  font-variant-numeric: var(--num); font-style: italic;
}
/* a text link, Pine, underlined on hover only — the same affordance the rest
   of the product already uses for "there is more of this somewhere else" */
.dlink {
  align-self: flex-start; margin-top: auto; border: 0; background: none; padding: 0;
  cursor: pointer; font: var(--t-meta-strong); color: var(--pine);
  text-decoration: none; text-underline-offset: var(--nudge-underline);
}
.dlink:hover { text-decoration: underline; }
.dlink:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
/* the same words, in the same place, on a tile whose whole surface is now the
   button. It stays as the visible affordance and stops being a control, because
   a button inside a button is not valid markup. */
.dlink--static { cursor: inherit; }
.dtile--go:hover .dlink--static { text-decoration: underline; }

/* the two segment bar on the outstanding tile: within terms against late */
.dsplit {
  display: flex; height: var(--sp-6); border-radius: var(--r-pill);
  overflow: hidden; background: var(--ground);
}
.dsplit__seg { display: block; height: 100%; }
.dsplit__seg--ontime { background: var(--ok); }
.dsplit__seg--late { background: var(--stop); }

/* ---- how old is the money owed ----
   One stacked bar across the top, then the four buckets as tiles beneath it.
   The four bucket colours are the product's four existing status inks read in
   severity order — nothing new, and nothing that moves the credit answer's own
   green / yellow / red meanings, which are untouched. */
.dstack {
  display: flex; height: var(--sp-8); border-radius: var(--r-pill);
  overflow: hidden; background: var(--ground);
}
.dstack__seg { display: block; height: 100%; }
.dstack__seg--a1 { background: var(--ok); }
.dstack__seg--a2 { background: var(--warn); }
.dstack__seg--a3 { background: var(--edge-overdue); }
.dstack__seg--a4 { background: var(--stop); }
.dbuckets { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-12); }
@container dcard (min-width: 17rem) { .dbuckets { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* No four across rule: the dashboard's own cards top out around 430px wide
   (the shell caps at 90rem however wide the monitor is), and four money figures
   in 430px leaves each one about 85px for an 85px figure. That is the exact
   squeeze that was cutting the amounts off, so the buckets stay a 2x2 block,
   which fits with room at every width the product can actually produce. */
.dbucket {
  display: flex; flex-direction: column; gap: var(--sp-4); text-align: left;
  background: var(--ground); border: var(--hair) solid var(--hairline);
  border-radius: var(--r-8); padding: var(--sp-12); cursor: pointer;
  font: inherit; color: inherit; min-width: 0;
  border-left: var(--bar-ink) solid var(--ring);
}
.dbucket:hover { background: var(--hover); }
.dbucket--a1 { border-left-color: var(--ok); }
.dbucket--a2 { border-left-color: var(--warn); }
.dbucket--a3 { border-left-color: var(--edge-overdue); }
.dbucket--a4 { border-left-color: var(--stop); }
.dbucket__label { font: var(--t-label); color: var(--text-2); }
.dbucket__fig {
  font: var(--t-money); color: var(--ink); font-variant-numeric: var(--num);
  font-stretch: var(--wd-num);
  font-size: clamp(0.75rem, min(var(--fs-money), 15cqi), var(--fs-money));
}

/* ---- needs your call today ---- */
.dashboard-card {
  container-type: inline-size; container-name: dcard;
  padding: var(--d-pad); display: flex; flex-direction: column; gap: var(--sp-12); min-width: 0;
}
.dashboard-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-8); }
.dashboard-card__head .dlink { margin-top: 0; align-self: baseline; }
.dashboard-card__title { font: var(--t-card-title); color: var(--ink); margin: 0; }
.dcalls { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.dcall {
  display: grid; grid-template-columns: minmax(0, 1fr) max-content max-content;
  align-items: center; gap: var(--sp-4) var(--sp-8);
  padding: var(--sp-12) 0; border-top: var(--hair) solid var(--rule);
}
/* below this the name and the money cannot share a line without one of them
   giving way, so the money takes its own line instead of being squeezed */
@container dcard (max-width: 15rem) {
  .dcall { grid-template-columns: minmax(0, 1fr) max-content; }
  .dcall__name { grid-column: 1 / -1; }
}
.dcall:first-child { border-top: 0; padding-top: 0; }
.dcall__name {
  display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0;
  border: 0; background: none; padding: 0; text-align: left; cursor: pointer; font: inherit; color: inherit;
}
.dcall__name:hover .dcall__store { text-decoration: underline; text-underline-offset: var(--nudge-underline); }
.dcall__store { font: var(--t-row-name); color: var(--ink); overflow-wrap: anywhere; }
.dcall__why { font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num); }
.dcall__amount {
  font: var(--t-money); color: var(--price); font-variant-numeric: var(--num);
  font-stretch: var(--wd-num); white-space: nowrap;
}
.dcall__ring {
  flex: 0 0 auto; width: var(--sp-40); height: var(--sp-40); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background-color: var(--btn-primary-bg); color: var(--btn-primary-ink);
  background-image: linear-gradient(to bottom, var(--btn-primary-top), var(--btn-primary-bottom));
  box-shadow: inset 0 0 0 var(--hair) var(--btn-primary-edge), var(--sh-btn);
  text-decoration: none;
}
.dcall__ringicon {
  width: var(--icon-inline); height: var(--icon-inline); fill: none; stroke: currentColor;
  stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
}

/* ---- since you last looked, unchanged in substance ---- */
.dashboard-since-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-8); }
.dashboard-since-item {
  display: flex; align-items: center; gap: var(--sp-8); text-align: left; background: none; border: none;
  padding: var(--sp-4); width: 100%; border-radius: var(--r-6); cursor: pointer; font: var(--t-meta); color: inherit;
  font-variant-numeric: var(--num);
}
button.dashboard-since-item:hover { background: var(--hover); }
.dashboard-since-item.is-static { cursor: default; }
.dashboard-since-item.is-quiet { color: var(--text-2); }
.dashboard-since-item__dot { width: var(--dot); height: var(--dot); border-radius: 50%; flex: 0 0 auto; background: var(--muted); }
.dashboard-since-item__dot--stop { background: var(--stop); }
.dashboard-since-item__dot--warn { background: var(--warn); }
.dashboard-since-item__dot--ok { background: var(--ok); }
.dashboard-since-item__dot--muted { background: var(--text-3); }
.dashboard-quiet-line {
  display: block; width: 100%; text-align: left; background: none; border: none; border-top: var(--hair) solid var(--hairline);
  margin-top: var(--sp-8); padding-top: var(--sp-8); cursor: pointer; font: var(--t-meta); color: var(--text-2);
}
.dashboard-checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-6); }
.dashboard-checklist li::before { content: '\25CB'; margin-right: var(--sp-8); color: var(--text-2); }
.dashboard-checklist li.is-done::before { content: '\25CF'; color: var(--ok); }

/* a phone gets a real tap target on the call button, where a pointer does not
   need one */
@media (max-width: 999px) {
  .dcall__ring { width: var(--tap-min); height: var(--tap-min); }
}


/* ---- Chotu (03f section 8) ----
   LD: "the main thing is the Chotu section, the most dullest part of the
   dashboard, it needs to be redone." It is now a SOLID card in the theme's own
   deep field, with light text on it — the same weight as the side menu, which
   is the one surface in this product that already carries that treatment.

   Which is also why every colour here is a --nav-* token. Those five
   (--nav-bg, --nav-ink, --nav-ink-2, --nav-edge, --nav-active-bg /
   --nav-active-ink) are the only set in tokens.css that holds the same
   dark-field relationship in Eucalyptus, Charcoal AND both dark modes, so the
   card and the drawn face keep their contrast everywhere without one new
   colour being defined. The side menu itself is untouched.
   Clean Cobalt is the one palette whose side menu is WHITE, so the brief,
   the launcher, the panel head and the face read --brief-* and --chotu-*
   first. Only Cobalt declares those; everywhere else each falls straight
   back to the --nav-* token it always used, so the other palettes compute
   exactly what they did before. */
/* A THIRD SHORTER THAN IT WAS (LD: "that banner is too huge, I think it has to
   reduce by at least 30%"). Measured at 1280x529 it came down from 218px to
   ~140px, and nothing was taken out of it to get there: the padding, the row
   gap, the badge, the brief's own size and leading each gave a little, and the
   ask field and the chips now share one row instead of taking a row each. The
   brief is still the largest type on the page, which is the one thing about
   this block that may not change. Colours untouched: still the same five
   --nav-* tokens and nothing else. */
.chotu-card {
  background: var(--brief-bg, var(--nav-bg)); color: var(--brief-ink, var(--nav-ink));
  border: var(--hair) solid var(--nav-edge); border-radius: var(--r-12);
  box-shadow: var(--sh-card);
  padding: clamp(var(--sp-12), 1.1vw, var(--sp-16));
  display: flex; flex-direction: column; gap: var(--sp-12);
  min-width: 0;
}
.chotu-card__head { display: flex; align-items: center; gap: var(--sp-12); min-width: 0; }
.chotu-card .chotu-face { width: 2.5rem; height: 2.5rem; }
.chotu-card__headtext { min-width: 0; }
.chotu-card__label {
  font: var(--t-label); color: var(--brief-ink-2, var(--nav-ink-2)); margin: 0 0 var(--sp-2);
}
.chotu-card__brief {
  margin: 0; color: var(--brief-ink, var(--nav-ink)); font-family: var(--font-latin); font-weight: 500;
  /* The floor is 1.55rem on purpose: --fs-store, the page title, is 1.5rem, and
     the brief has to stay the largest type on the page at EVERY width, not just
     on a wide monitor. The upper bound came down from 1.75rem and the leading
     from 1.35 to 1.25, which is where a good part of the 30% came from. */
  font-size: clamp(1.55rem, 0.86rem + 0.78vw, 1.75rem); line-height: 1.25;
  max-width: var(--measure-wide); text-wrap: pretty;
}
/* the ask field and the chips on ONE row where there is room for both, and
   back to their own rows the moment there is not. The field keeps a 15rem
   floor, which is the width its placeholder needs before it starts eating its
   own words. */
.chotu-card__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-8) var(--sp-12); min-width: 0;
}
.chotu-card__foot > .chotu-card__ask { flex: 1 1 15rem; min-width: 0; }
.chotu-card__foot > .chotu-card__chips { flex: 0 1 auto; }
/* the figures, picked out. Gold on the deep field is the one emphasis this
   product already uses on a dark surface (the side menu's own pill). */
.chotu-card__fig {
  color: var(--brief-hl, var(--nav-active-bg)); font-weight: 800; font-variant-numeric: var(--num);
  font-stretch: var(--wd-num);
}

/* the ask field, with the mic inside it on the right */
.chotu-card__ask {
  display: flex; align-items: center; gap: var(--sp-6);
  background: var(--brief-field, var(--nav-hover)); border: var(--hair) solid var(--nav-edge);
  border-radius: var(--r-pill); padding: var(--sp-4) var(--sp-6) var(--sp-4) var(--sp-16);
  min-width: 0;
}
.chotu-card__ask:focus-within { box-shadow: 0 0 0 var(--focus-ring) var(--brief-focus, var(--nav-focus)); }
.chotu-card .chotu-card__input {
  flex: 1 1 auto; min-width: 0; margin: 0;
  background: none; border: 0; border-radius: 0; box-shadow: none;
  color: var(--brief-ink, var(--nav-ink)); font: var(--t-body);
  min-height: clamp(2rem, 4vh, 2.5rem); height: clamp(2rem, 4vh, 2.5rem);
  padding: 0;
}
.chotu-card .chotu-card__input::placeholder { color: var(--brief-ink-2, var(--nav-ink-2)); }
.chotu-card .chotu-card__input:hover { border-color: transparent; }
.chotu-card .chotu-card__input:focus,
.chotu-card .chotu-card__input:focus-visible { outline: none; box-shadow: none; border-color: transparent; }
.chotu-card__send, .chotu-card__mic {
  flex: 0 0 auto; border-radius: 50%;
  width: clamp(1.75rem, 4.2vh, var(--sp-32)); height: clamp(1.75rem, 4.2vh, var(--sp-32));
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  font: var(--t-button); padding: 0;
}
.chotu-card__send { background: none; border: var(--hair) solid var(--nav-edge); color: var(--brief-ink, var(--nav-ink)); }
.chotu-card__send:hover { background: var(--brief-field, var(--nav-hover)); }
.chotu-card__mic {
  border: 0; background-color: var(--nav-active-bg); color: var(--nav-active-ink);
  background-image: linear-gradient(to bottom, var(--nav-active-top), var(--nav-active-bg));
  box-shadow: var(--sh-btn);
}
.chotu-micglyph { width: var(--icon-inline); height: var(--icon-inline); }

/* ---- the drawn face (replaces the emoji) ---- */
.chotu-avatar { display: inline-flex; flex: 0 0 auto; line-height: 0; }
.chotu-face { display: block; }
.chotu-face__badge { fill: var(--chotu-badge, var(--nav-active-bg)); }
.chotu-face__kurta { fill: var(--chotu-kurta, var(--nav-bg)); }
.chotu-face__collar { fill: var(--chotu-collar, var(--nav-ink-2)); }
.chotu-face__skin { fill: var(--chotu-skin, var(--nav-ink)); }
.chotu-face__cap { fill: var(--chotu-cap, var(--nav-bg)); }
.chotu-face__ink { fill: var(--chotu-ink, var(--nav-active-ink)); }
.chotu-face__line {
  fill: none; stroke: var(--chotu-ink, var(--nav-active-ink)); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---- chips ---- */
.chotu-chips { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.chotu-card__chips { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.chotu-chip {
  border-radius: var(--r-pill); border: var(--hair) solid var(--control-edge); background: var(--paper);
  padding: var(--sp-6) var(--sp-12); font: var(--t-meta-strong); cursor: pointer; color: inherit;
}
.chotu-chip:hover { background: var(--hover); }
.chotu-chip--on-dark { background: var(--brief-field, var(--nav-hover)); border-color: var(--nav-edge); color: var(--brief-ink, var(--nav-ink)); }
.chotu-chip--on-dark:hover { background: var(--nav-edge); }

/* ---- the launcher, and the state it changes into ----
   LD, first: "When you click on the icon, the icon itself should change... I
   think a popup on the right side of the screen, a small popup like standard
   chat icon popups, works." LD, now: "it's a very small floating icon in the
   bottom right. The icon needs to be slightly bigger so that they know that
   something is there, and also that icon does not really communicate that it's
   Chotu."

   CLOSED it is a pill carrying Chotu's own drawn face at 40px with his name
   beside it - 56px tall against the 44px tap target it used to be, so it is
   noticeably there, and it now says who it is in words rather than asking
   anybody to decode a spark. OPEN it collapses to a round yellow badge with a
   chevron: a different shape, a different fill, a different glyph and a
   different accessible name, so the two states can never be confused. Every
   colour here is a --nav-* token the side menu already defines; none is new,
   and the side menu itself is untouched.

   The height is written once, here, because the docked panel has to sit
   directly on top of the launcher and the two may never disagree about how
   tall it is. It is a size, not a colour, so it belongs with the component
   rather than in tokens.css. */
:root { --chotu-launcher-h: 3.5rem; }
.chotu-launcher {
  position: fixed; right: var(--sp-16); bottom: var(--sp-16); z-index: 41;
  min-height: var(--chotu-launcher-h); border-radius: var(--r-pill);
  padding: var(--sp-6) var(--sp-16) var(--sp-6) var(--sp-6); gap: var(--sp-8);
  display: flex; align-items: center; justify-content: center;
  background: var(--brief-bg, var(--nav-bg)); color: var(--brief-ink, var(--nav-ink));
  border: var(--hair) solid var(--nav-edge); box-shadow: var(--sh-overlay); cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.chotu-launcher:hover { background: var(--brief-field, var(--nav-hover)); }
.chotu-launcher:focus-visible { outline: var(--focus-ring) solid var(--brief-focus, var(--nav-focus)); outline-offset: var(--focus-offset); }
.chotu-launcher .chotu-face { width: var(--sp-40); height: var(--sp-40); }
.chotu-launcher__name { font: var(--t-button); color: var(--brief-ink, var(--nav-ink)); white-space: nowrap; }
.chotu-launcher__mark {
  width: var(--sp-24); height: var(--sp-24); display: block;
  fill: none; stroke: currentColor; stroke-width: var(--stroke-icon);
  stroke-linecap: round; stroke-linejoin: round;
}
.chotu-launcher.is-open {
  width: var(--chotu-launcher-h); height: var(--chotu-launcher-h); padding: 0;
  border-radius: 50%;
  background-color: var(--nav-active-bg); color: var(--nav-active-ink);
  border-color: var(--nav-active-bg);
}
.chotu-launcher.is-open:hover { background-color: var(--nav-active-top); }
.chotu-launcher.is-open .chotu-launcher__mark { stroke-width: var(--hair-strong); }

/* ---- the docked panel ----
   A small popup anchored to the bottom right corner, sitting directly above the
   launcher. It never covers the screen on a desktop, which is the whole point:
   Chotu can take the page behind him to the right screen AND leave his steps up
   beside it. Focus is trapped inside while it is open and Escape closes it -
   that part is in owner.js, because it is behaviour and not paint. */
.chotu-panel {
  position: fixed; z-index: 42;
  right: var(--sp-16); bottom: calc(var(--chotu-launcher-h) + var(--sp-16) + var(--sp-8));
  width: min(22rem, calc(100vw - var(--sp-32)));
  max-height: min(32rem, calc(100vh - var(--chotu-launcher-h) - var(--sp-40)));
  display: flex; flex-direction: column; min-height: 0;
  background: var(--paper); color: var(--ink);
  border: var(--hair) solid var(--hairline); border-radius: var(--r-12);
  box-shadow: var(--sh-overlay);
  overflow: hidden;
}
.chotu-panel__head {
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-8) var(--sp-12);
  background: var(--brief-bg, var(--nav-bg)); color: var(--brief-ink, var(--nav-ink));
  border-bottom: var(--hair) solid var(--nav-edge);
  flex: 0 0 auto;
}
.chotu-panel__head .chotu-face { width: 1.75rem; height: 1.75rem; }
.chotu-panel__headtext { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.chotu-panel__headtext b { font: var(--t-section); color: var(--brief-ink, var(--nav-ink)); }
.chotu-panel__sub { font: var(--t-label); color: var(--brief-ink-2, var(--nav-ink-2)); }
.chotu-panel__close {
  flex: 0 0 auto; width: var(--sp-32); height: var(--sp-32); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: var(--hair) solid var(--nav-edge); color: var(--brief-ink, var(--nav-ink));
  cursor: pointer; padding: 0;
}
.chotu-panel__close:hover { background: var(--brief-field, var(--nav-hover)); }
.chotu-panel__close:focus-visible { outline: var(--focus-ring) solid var(--brief-focus, var(--nav-focus)); outline-offset: var(--focus-offset); }
.chotu-panel__closeglyph { width: var(--icon-inline); height: var(--icon-inline); display: block; }
.chotu-panel__body {
  padding: var(--sp-12); overflow-y: auto; overscroll-behavior: contain;
  flex: 1 1 auto; min-height: 0;
}
.chotu-panel .btn { min-height: var(--ctrl-h-sm); }

/* On a phone a 22rem panel IS the screen, so it spans the width and sits on the
   bottom edge like every other sheet in the product. The launcher would land on
   top of it, so it steps aside while the panel is open; the panel has its own
   close button and Escape still works. */
@media (max-width: 34rem) {
  .chotu-panel {
    left: var(--sp-8); right: var(--sp-8); bottom: var(--sp-8);
    width: auto; max-height: min(80vh, calc(100vh - var(--sp-40)));
  }
  body.is-chotu-open .chotu-launcher { display: none; }
  /* On a phone the name comes off and he is a round badge again - still his
     own face, still bigger than the old mark, but no wider than it is tall, so
     a floating control never takes a quarter of the row it sits over (LD: "it
     must not cover content or block anything at phone width"). */
  .chotu-launcher {
    width: var(--chotu-launcher-h); padding: 0; border-radius: 50%;
  }
  .chotu-launcher__name { display: none; }
}

/* ---- the ask sheet: a question, then an answer ---- */
.chotu-ask { display: flex; flex-direction: column; gap: var(--sp-12); }
.chotu-ask__bar { display: flex; align-items: center; gap: var(--sp-8); }
.chotu-ask__bar input { flex: 1 1 auto; }
.chotu-mic-btn { border: none; background: none; cursor: pointer; padding: var(--sp-4); border-radius: 50%; color: var(--pine); }
/* M6, D18: listening is Ink, never the credit red. See the phase F block (W23). */
.chotu-bubble {
  border-radius: var(--r-12); padding: var(--sp-12); font: var(--t-body);
  font-variant-numeric: var(--num); margin: 0;
}
/* what he asked, set as his own line */
.chotu-bubble--ask {
  background: var(--selected); color: var(--ink);
  width: fit-content; max-width: 90%; margin-left: auto;
}
/* what Chotu answered: the figure, a status pill, then the two things to do */
.chotu-bubble--answer {
  background: var(--ground); border: var(--hair) solid var(--hairline); color: var(--ink);
  display: flex; flex-direction: column; gap: var(--sp-8); align-items: flex-start;
}
.chotu-bubble--answer .btn-row { margin: 0; }
.chotu-answer__line { margin: 0; font: var(--t-body); color: var(--ink); }
.chotu-answer__fig {
  font: var(--t-total); color: var(--price); font-variant-numeric: var(--num);
  font-stretch: var(--wd-num); letter-spacing: var(--ls-figure);
}
.chotu-transcript { color: var(--text-2); font: var(--t-meta); font-style: italic; }
.chotu-result { min-height: 1px; }
.chotu-help ol { padding-left: var(--sp-20); display: flex; flex-direction: column; gap: var(--sp-6); margin: var(--sp-8) 0 0; }
.chotu-help h3 { font: var(--t-card-title); color: var(--ink); }
/* "I took you there. Here is how it is done." - said once, above the steps, so
   the navigation and the instructions read as one answer and not two. */
.chotu-help__took { margin: var(--sp-4) 0 0; font: var(--t-meta); color: var(--text-2); font-style: italic; }
.chotu-help .btn { margin-top: var(--sp-8); }
.chotu-filled-note {
  background: var(--warn-bg); color: var(--warn); border-radius: var(--r-8);
  padding: var(--sp-8) var(--sp-12); font: var(--t-meta); display: flex; flex-direction: column; gap: var(--sp-8); align-items: flex-start;
}

/* ---- Schemes in trade language (03f section 2) ---- */
.offer-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-12); margin-bottom: var(--gap-card); }
.offer-card { display: flex; flex-direction: column; gap: var(--sp-6); }
.offer-card__title { font: var(--t-card-title); color: var(--ink); }
.offer-card__example { color: var(--pine); font-weight: 600; }
.offer-card__blurb { color: var(--text-2); font: var(--t-meta); }

/* ---- the filling in screen, grouped ----
   LD: "I like the first page of the new offer section... But once you select an
   option, the page where you actually enter all the information, this seems
   bad." It was one long column of loosely grouped controls. It is four named
   steps now, each a framed block with a numbered heading in trade language, in
   the order a distributor thinks about an offer: what the offer is, who gets
   it, try it, then the quiet settings. Every field and every behaviour is the
   one that was already there - this block only draws the groups. */
.scheme-form { display: flex; flex-direction: column; gap: var(--gap-card); }
.scheme-form__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-12); flex-wrap: wrap; }
.scheme-form__head h2 { font: var(--t-title); letter-spacing: var(--ls-title); }

.scheme-step {
  background: var(--paper); border: var(--hair) solid var(--hairline);
  border-radius: var(--r-12); padding: var(--pad-card);
}
.scheme-step__head { display: flex; align-items: flex-start; gap: var(--sp-12); margin-bottom: var(--sp-12); }
/* the step number: the same round ink chip the order timeline uses for a done
   step, so the product has one way of numbering a sequence */
.scheme-step__n {
  flex: 0 0 auto; width: var(--sp-24); height: var(--sp-24); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--track-done); color: var(--track-done-ink);
  font: var(--t-label); font-variant-numeric: var(--num);
}
.scheme-step__title { font: var(--t-card-title); color: var(--ink); margin: 0; }
.scheme-step__blurb { font: var(--t-meta); color: var(--text-2); margin: var(--sp-2) 0 0; max-width: var(--measure-sub); }
.scheme-step__body { display: flex; flex-direction: column; gap: var(--sp-12); min-width: 0; }
.scheme-step__body > * { margin: 0; }
/* a labelled thing inside a step: the label hugs its own control rather than
   floating a full step gap above it */
.scheme-subfield { display: flex; flex-direction: column; gap: var(--sp-6); min-width: 0; }
.scheme-field-head { font: var(--t-label); color: var(--text-2); margin: 0; }
/* the read back, labelled so there is no doubt that THIS is the offer being
   built rather than one more control */
.scheme-readback__label { font: var(--t-label); color: var(--text-2); margin: 0; }
.scheme-dates { display: flex; flex-wrap: wrap; gap: var(--sp-12); }
.scheme-dates > .field { flex: 1 1 var(--field-min); min-width: 0; }

.sentence-box {
  font: var(--t-card-title); color: var(--ink); background: var(--ground);
  border: var(--hair) solid var(--hairline); border-radius: var(--r-8);
  padding: var(--sp-12); line-height: 1.5;
  border-left: var(--bar-ink) solid var(--pine);
}
/* outside the grouped form (nothing uses it there today, but the rule it
   replaced had the margins) it keeps its old spacing */
:not(.scheme-step__body) > .sentence-box { margin: var(--sp-8) 0 var(--gap-section); }
.sentence-fill { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-8); margin-bottom: var(--sp-8); }
.sentence-fill input[type=number], .sentence-fill input[type=text],
.sentence-step input[type=number], .sentence-step input[type=text] { width: 6.5rem; display: inline-block; }
.sentence-fill select, .sentence-step select { display: inline-block; width: auto; }
.sentence-step { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-8); padding: var(--sp-8) 0; border-top: var(--hair) solid var(--rule); }
.sentence-step:first-child { border-top: none; }
.sentence-step__remove { margin-left: auto; }

.scope-editor { margin-bottom: var(--sp-8); }
.scope-editor input[type=search] { width: 100%; }
.scope-results { position: relative; }
.scope-results .pick { border-top: none; border-bottom: var(--hair) solid var(--rule); }

.chip-row.scope-chips { margin: var(--sp-8) 0; min-height: var(--chip-h); }
.chip--removable { gap: var(--sp-6); padding-right: var(--sp-4); }
.chip__x {
  border: none; background: none; cursor: pointer; color: var(--text-2); font-size: 1rem; line-height: 1;
  width: 1.1rem; height: 1.1rem; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%;
}
.chip__x:hover { background: var(--hover); color: var(--ink); }

.tryit-box {
  background: var(--paper); border: var(--hair) solid var(--hairline); border-radius: var(--r-8);
  padding: var(--sp-12); margin-bottom: var(--gap-section); display: flex; flex-direction: column; gap: var(--sp-8);
}
.tryit-box__row { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; }
.tryit-box__result { font: var(--t-meta-strong); color: var(--pine); }

.reveal-toggle { color: var(--pine); background: none; border: none; cursor: pointer; font: var(--t-meta-strong); padding: var(--sp-4) 0; text-decoration: underline; }
.scheme-code-line { color: var(--text-2); font: var(--t-meta); display: flex; align-items: center; gap: var(--sp-8); margin-top: var(--sp-4); }
.scheme-readback-list { font: var(--t-body); color: var(--ink); margin-top: var(--sp-2); }

/* ---- stores page: quick actions up, one clear add control, a real search (LD) ---- */
.stores-toolbar { display: flex; align-items: center; gap: var(--sp-12); flex-wrap: wrap; margin-bottom: var(--sp-12); }
/* ONE control, not two buttons on their own (LD, via XO: "should not be two
   buttons and two lines, it is not that prominent"). Add a store is the wide
   primary half; Bulk add is a quiet second segment fused to its right edge,
   same height, one shared outline, no gap between them - so bulk always
   reads as the secondary path off the main action, never a second, equally
   weighted choice beside it. */
.add-store-group { display: inline-flex; align-items: stretch; }
.add-store-group__main {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
  padding-inline: var(--sp-16);
}
.add-store-group__bulk {
  border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: 0;
  color: var(--text-2); font-weight: 600;
}
.add-store-group__bulk:hover { color: var(--ink); }
.list-search { margin-bottom: var(--gap-card); }
.list-search__label {
  display: block; font: var(--t-meta-strong); color: var(--ink); margin-bottom: var(--sp-4);
}
.list-search__field { position: relative; }
.list-search input[type=search] {
  width: 100%; min-height: 2.75rem; padding-left: 2.5rem; border-radius: var(--r-pill);
  font-size: 1rem; border-color: var(--control-edge);
}
.list-search input[type=search]:hover { border-color: var(--ink); }
.list-search__icon {
  position: absolute; left: var(--sp-12); top: 50%; transform: translateY(-50%);
  width: var(--icon-nav); height: var(--icon-nav); color: var(--text-2);
  fill: none; stroke: currentColor; stroke-width: var(--stroke-icon); stroke-linecap: round; stroke-linejoin: round;
  pointer-events: none;
}

/* G3 phone Stores views: one joined row that wraps (All / Overdue credit /
   Healthy / Waiting for you). Current view is Ink fill with Paper text.
   Hidden on desktop — the sidebar already owns those subs. */
.list-views { display: none; }
@media (max-width: 999px) {
  .list-views {
    display: flex; flex-wrap: wrap; align-items: stretch;
    margin-bottom: var(--sp-12);
    border: var(--hair) solid var(--control-edge); border-radius: var(--r-12);
    background: var(--paper); overflow: hidden;
    box-shadow: inset 0 var(--hair) 0 var(--ctrl-gloss-top), var(--sh-btn);
  }
  .list-views__btn {
    flex: 1 1 40%; min-height: var(--tap-min); min-width: 0;
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-6);
    padding: 0 var(--sp-12);
    border: 0; border-right: var(--hair) solid var(--hairline);
    border-bottom: var(--hair) solid var(--hairline);
    background: none; color: var(--text-2); font: var(--t-button); cursor: pointer;
  }
  .list-views__btn:nth-child(2n) { border-right: 0; }
  .list-views__btn:nth-last-child(-n+2) { border-bottom: 0; }
  .list-views__btn:hover { color: var(--ink); background: var(--hover); }
  .list-views__btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: calc(var(--focus-offset) * -1); }
  .list-views__btn[aria-current] {
    background: var(--ink); color: var(--on-ink); font-weight: 700;
  }
  .list-views__count {
    font-variant-numeric: var(--num); font-weight: 700;
  }
}

/* =========================================================================
   WhatsApp intake (spec 03i): the play button on a flagged line's own few
   seconds, the whole note player, the messages behind a card, the Unknown
   senders strip, the simulator log. Every value is a token.
   ========================================================================= */
.play-btn {
  min-height: var(--ctrl-h-sm); padding: 0 var(--sp-12); border-radius: var(--r-pill);
  border: var(--hair) solid var(--control-edge); background-color: var(--paper); color: var(--ink);
  font: var(--t-meta-strong); font-variant-numeric: var(--num); cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--sp-6); white-space: nowrap;
  box-shadow: var(--sh-btn);
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.play-btn:hover { background-color: var(--hover); border-color: var(--text-2); }
.play-btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
/* Sounding, and stopped part way through. Both are carried by the glyph and
   the accessible name as well, so the fill is a third carrier, never the only
   one. No new colour: --selected and --hover are the same two surfaces every
   other selected or held control in the product uses. */
.play-btn.is-playing { background-color: var(--selected); border-color: var(--ink); }
.play-btn.is-paused { background-color: var(--hover); border-color: var(--text-2); }
.play-btn.is-broken { color: var(--stop); border-color: var(--stop); }
.play-btn__icon { width: var(--sp-16); height: var(--sp-16); flex: 0 0 auto; }
.play-btn--small { min-height: var(--sp-32); padding: 0 var(--sp-8); }
.linerow__play { margin-top: var(--sp-6); display: flex; align-items: center; gap: var(--sp-8); }
.linerow__evidence { font: var(--t-meta); color: var(--text-2); font-style: italic; }
.sheet-evidence { display: flex; align-items: center; gap: var(--sp-8); }

.order-intake-card .hi + .notes { margin-top: var(--sp-12); }
.intake-msg { padding: var(--sp-12) 0; border-top: var(--hair) solid var(--hairline); }
.intake-msg:first-of-type { border-top: 0; }
.intake-msg__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-8); flex-wrap: wrap; }
.intake-msg__kind { font: var(--t-label); color: var(--text-2); }
.intake-msg__gone { font: var(--t-meta); color: var(--text-2); font-style: italic; }
.intake-msg__text { margin-top: var(--sp-6); font: var(--t-body); color: var(--ink); overflow-wrap: anywhere; }
.intake-photo {
  margin-top: var(--sp-8); padding: 0; border: var(--hair) solid var(--hairline); border-radius: var(--r-8);
  background: var(--paper); cursor: pointer; display: block; max-width: 12rem; overflow: hidden;
}
.intake-photo img { display: block; width: 100%; height: auto; }
.intake-photo:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.intake-photo__full img { display: block; width: 100%; height: auto; border-radius: var(--r-8); }

.unknown-strip { margin-bottom: var(--sp-16); }
.unknown-strip__head { font: var(--t-meta-strong); color: var(--ink); }
.sim-log { display: flex; flex-direction: column; gap: var(--sp-8); margin-bottom: var(--sp-16); }

/* =========================================================================
   THE PHONE ORDER CARD
   =========================================================================
   On a phone the order page is ONE card, drawn the way the website's own
   mockup draws it (site/index.html, .phone): the store and the time, one
   line per item with the money right aligned and a quiet second line under
   each, the order total, the credit answer as a strip, then the actions,
   with Confirm visibly blocked by a red credit answer. The pieces are the
   very elements the wide page uses; only the arrangement changes, in
   paintOrder (owner.js), so nothing here is a second home for a fact.
   .order-page--phone is set by the app at paint time, at the same 1000px
   line the workspace splits on, not by a media query of its own. Colours:
   tokens only, as everywhere; the red top edge comes from the card rule
   that already draws it on any card carrying an overdue credit answer. */
.linerow__brief { display: none; }

.order-page--phone { max-width: none; }
.order-page--phone .order-card { padding: 0; }

.order-card__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-8);
  padding: var(--sp-16) var(--sp-16) var(--sp-8);
}
.order-card__head .order-title { font: var(--t-card-title); letter-spacing: var(--ls-normal); min-width: 0; overflow-wrap: anywhere; }
.order-card__when { font: var(--t-meta); color: var(--text-2); white-space: nowrap; font-variant-numeric: var(--num); }
.order-card .banner { margin: var(--sp-8) var(--sp-16); }

/* the lines: name left, money right, the brief beneath. Never the four column
   grid, whatever the card's width, which is why these outrank the container
   query on .lines by one class. */
.order-page--phone .linehead { display: none; }
.order-page--phone .linerow {
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-2) var(--sp-12);
  padding: var(--sp-12) var(--sp-16); margin: 0;
  border-top: var(--hair) solid var(--rule); border-bottom: 0; border-radius: 0;
}
.order-page--phone .linerow__item { grid-column: 1; grid-row: 1; gap: var(--sp-2); }
.order-page--phone .linerow__cell--amount { grid-column: 2; grid-row: 1; align-self: start; }
.order-page--phone .linerow .line-amount { min-height: 0; line-height: 1.3; }
.order-page--phone .linerow__name { font: var(--t-row-name); }
.order-page--phone .linerow__said { display: none; }
.order-page--phone .linerow--unnamed .linerow__said { display: block; font: var(--t-row-name); font-style: normal; color: var(--ink); }
.order-page--phone .linerow__brief { display: block; font: var(--t-meta); color: var(--text-2); overflow-wrap: anywhere; }
.order-page--phone .linerow.flagged .linerow__brief { color: var(--ink); }
.order-page--phone .linerow__meta,
.order-page--phone .linerow__note { display: none; }
.order-page--phone .linerow__play { margin-top: var(--sp-4); }
.order-page--phone .linerow.flagged {
  padding: var(--sp-12) var(--sp-16); margin: 0; border-radius: 0;
  border-top: var(--hair) solid var(--rule);
}

/* the fields, folded until the brief is tapped. The brief is the tap: a text
   button with a small chevron, its hit area grown by negative margin so the
   line itself does not get taller. Open, the row shows the quantity and rate
   fields side by side and the meta line with Change item. */
.linerow__brief--fold,
.order-page--phone .linerow__brief--fold {
  background: none; border: 0; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-8) 0; margin: calc(var(--sp-8) * -1) 0;
  font: inherit; color: inherit; width: 100%;
}
.linerow__brief--fold::after {
  content: ''; display: block; flex: 0 0 auto; width: 0.45em; height: 0.45em;
  border-right: var(--hair-strong) solid currentColor; border-bottom: var(--hair-strong) solid currentColor;
  transform: translateY(-0.15em) rotate(45deg); transition: transform var(--dur-fast) var(--ease-out);
}
.linerow__brief--fold[aria-expanded="true"]::after { transform: translateY(0.1em) rotate(-135deg); }
.linerow__brief--fold:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
.order-page--phone .linerow__cell--qty,
.order-page--phone .linerow__cell--rate { display: none; }
/* a fixed second column while open: an input's own intrinsic width would
   otherwise widen it and squeeze the brief above onto three lines */
.order-page--phone .linerow.is-open { grid-template-columns: minmax(0, 1fr) 7.5rem; }
.order-page--phone .linerow.is-open .linerow__cell--qty { display: flex; grid-column: 1; grid-row: 2; margin-top: var(--sp-8); }
.order-page--phone .linerow.is-open .linerow__cell--rate { display: flex; grid-column: 2; grid-row: 2; margin-top: var(--sp-8); }
.order-page--phone .linerow.is-open .linerow__meta { display: flex; margin-top: var(--sp-4); }
/* open, the meta line shows only what the brief does not already say: the
   pack and the Change item link */
.order-page--phone .linerow.is-open .linerow__meta > :not(.linerow__pack):not(.line-change-link) { display: none; }

/* the total, the credit strip, the notes and the actions, each a section
   behind a hairline, exactly as the mockup stacks them */
.order-card__total {
  display: flex; justify-content: space-between; gap: var(--sp-8); margin: 0;
  padding: var(--sp-12) var(--sp-16);
  border-top: var(--hair) solid var(--hairline);
  font: var(--t-section); font-weight: var(--fw-heavy); color: var(--ink);
}
.order-card__figure { font-variant-numeric: var(--num); font-stretch: var(--wd-num); white-space: nowrap; }
.order-card .credit.block {
  width: 100%; max-width: none; border-radius: 0;
  border-left: 0; border-right: 0; border-bottom: 0; border-top-width: var(--hair);
  padding: var(--sp-12) var(--sp-16);
}
.order-card .blockmsg { padding: var(--sp-8) var(--sp-16) 0; }
.order-card .order-actions-row {
  margin: 0; padding: var(--sp-12) var(--sp-16);
  border-top: var(--hair) solid var(--hairline);
}
.order-card .order-actions-row > .btn { flex: 0 0 auto; }

/* THE PHONE LIST ROW. A row's facts line wraps to a second line on a phone
   instead of ending in an ellipsis: on a 360px screen the ellipsis was eating
   the item count on an order and the overshoot on a store, the two facts the
   row is there to give. Three lines at most: a store row on a 360px screen
   needs the third to finish with who to ring. Last in the file on purpose, so
   it outranks the nowrap the row rule sets for a wide list. */
@media (max-width: 999px) {
  /* Salesman and other row meta: may wrap up to three lines. */
  .row__meta {
    white-space: normal; display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
  }
  /* Owner list rows: greys wrap with no ellipsis on a phone. Badge-under-greys
     column layout is set for every width above. */
  .list-rows--cards .row:not(.store-card) .row__meta {
    white-space: normal; overflow: visible; text-overflow: clip;
    display: block; -webkit-line-clamp: unset; line-clamp: unset;
  }
}

/* =========================================================================
   05g: THE CREDIT FIGURE STAYING TRUE PAST WEEK ONE
   Payment allocation, credit notes, the undated opening balance, figure 6.

   ONE RULE GOVERNS EVERY COLOUR BELOW: nothing here uses --ok, --warn or
   --stop. Those three are the credit answer's own colours and the product's
   single promise is that a distributor never confuses interface colour with
   a credit answer. "We were never given a date for this money" is a
   statement about OUR data, not a verdict on his customer, so it is drawn in
   --muted and --text-2 with a hairline rule and carries no credit colour at
   all. Borrowing amber for it would put a credit colour on a store whose
   credit we are explicitly declining to judge, which is the exact confusion
   those three tokens exist to prevent.
   ====================================================================== */

/* the two gap sentences: what the figure cannot yet answer */
.credit-gaps { border-left: var(--hair-strong) solid var(--muted); padding-left: var(--sp-12); }
.gap-line {
  margin: var(--sp-8) 0 var(--sp-6); font: var(--t-meta);
  color: var(--muted); background: var(--muted-bg);
  border-radius: var(--r-8); padding: var(--sp-8) var(--sp-10);
}
.gap-row {
  display: flex; flex-wrap: wrap; gap: var(--sp-8);
  align-items: center; justify-content: space-between;
  padding: var(--sp-6) 0; border-top: var(--hair) solid var(--hairline);
  font: var(--t-meta);
}

/* "which bills does this pay": one row per open bill with its own box */
.alloc-list { margin: var(--sp-8) 0; }
.alloc-row {
  display: flex; flex-wrap: wrap; gap: var(--sp-8); align-items: center;
  padding: var(--sp-8) 0; border-top: var(--hair) solid var(--hairline);
}
.alloc-bill { flex: 1 1 12rem; min-width: 0; font: var(--t-meta); }
.alloc-amount { flex: 0 0 7rem; max-width: 100%; }
.bucket-row { display: flex; flex-wrap: wrap; gap: var(--sp-8); margin-top: var(--sp-8); }

/* an undated bill row: the date cell says so and offers the one tap */
td.undated { display: flex; flex-direction: column; gap: var(--sp-6); align-items: flex-start; }
td.undated > span { color: var(--muted); font: var(--t-meta); }

/* the statement: every movement, its reason, a running balance */
.statement .tbl { width: 100%; border-collapse: collapse; }
.statement .tbl th, .statement .tbl td {
  text-align: left; padding: var(--sp-6) var(--sp-8);
  border-bottom: var(--hair) solid var(--hairline); font: var(--t-meta);
  white-space: nowrap;
}
.statement .tbl th { color: var(--text-2); }
.statement .tbl td:nth-child(4), .statement .tbl td:nth-child(5), .statement .tbl td:nth-child(6),
.statement .tbl th:nth-child(4), .statement .tbl th:nth-child(5), .statement .tbl th:nth-child(6) { text-align: right; }
.statement .tbl tr[data-kind="opening"] td { font-weight: 600; }
/* a table may scroll sideways in its own box; the page body never does */
.statement { overflow-x: auto; }

/* figure 6: the one line that answers "what am I paying for", in his rupees */
.figure6-line { margin: 0 0 var(--sp-6); font: var(--t-body); }

/* =========================================================================
   PUT YOUR RATES (04m W16). A real table on a desktop, one card per item on
   a phone, with the Save bar pinned under the thumb (5.3). A suggested cell
   wears a dashed edge until it is saved; an error is Ink with the warn edge.
   ========================================================================= */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.rate-grid-page { display: flex; flex-direction: column; gap: var(--gap-section); padding-bottom: var(--sp-16); }
.rg-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-12); }
.rg-filters .chip-row { margin: 0; }
.rg-brand { margin: 0; min-width: var(--select-min); }
.rg-helpers__row { display: flex; flex-wrap: wrap; gap: var(--sp-12) var(--sp-24); }
.rg-helper { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-8); font: var(--t-body); color: var(--ink); }
.rg-helper .rg-pct { width: var(--field-tiny); min-width: 0; flex: 0 0 auto; }
.rg-helper .btn { flex: 0 0 auto; }
.rg-summary { font: var(--t-meta); color: var(--ink); margin: 0; }
.rg-summary:empty { display: none; }
.rate-grid-wrap { padding: 0; overflow: visible; }
.rate-grid { width: 100%; border-collapse: collapse; font-variant-numeric: var(--num); }
.rate-grid-wrap .rate-grid thead th {
  position: sticky; top: 0; z-index: 1; background: var(--paper);
  font: var(--t-label); color: var(--text-2); text-align: left;
  padding: var(--sp-8) var(--sp-12); border-bottom: var(--hair) solid var(--hairline);
}
.rate-grid th:nth-child(1) { width: 40%; }
.rate-grid th:nth-child(2) { width: 12%; }
.rate-grid th:nth-child(3), .rate-grid th:nth-child(4) { width: 24%; }
.rate-grid-wrap .rate-grid td, .rate-grid-wrap .rate-grid tbody th { padding: var(--sp-8) var(--sp-12); border-bottom: var(--hair) solid var(--hairline); vertical-align: top; text-align: left; font-weight: 400; }
.rate-grid tbody tr:last-child td, .rate-grid tbody tr:last-child th { border-bottom: 0; }
.rg-item__name { display: block; font: var(--t-row-name); color: var(--ink); }
.rg-item__pack { display: block; font: var(--t-meta); color: var(--text-2); }
.rg-mrp { font: var(--t-money); color: var(--ink); }
.rg-input { width: 100%; }
.rg-input.is-suggested { border-style: dashed; border-color: var(--control-edge); }
.rg-cell-label { display: none; }
.rg-saved { display: block; font: var(--t-meta-strong); color: var(--ok); margin-top: var(--sp-4); }
.rg-saved[hidden] { display: none; }
.rg-bar { justify-content: flex-start; }
.rg-bar__line { font: var(--t-meta); color: var(--ink); margin: 0; flex: 1 1 auto; }

@media (max-width: 999px) {
  /* one card per item: name, then pack and MRP, then the two fields side by
     side, each with its own visible label */
  .rate-grid, .rate-grid tbody { display: block; }
  .rate-grid thead { display: none; }
  .rate-grid tbody tr {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4) var(--sp-12);
    padding: var(--sp-12); border-bottom: var(--hair) solid var(--hairline);
  }
  .rate-grid tbody th, .rate-grid td { display: block; padding: 0; border: 0; }
  .rate-grid .rg-item, .rate-grid .rg-mrp { grid-column: 1 / -1; }
  .rate-grid .rg-mrp { font: var(--t-meta); color: var(--text-2); }
  .rg-cell-label { display: inline; font: var(--t-label); color: var(--text-2); }
  .rate-grid .rg-sell .rg-cell-label, .rate-grid .rg-cost .rg-cell-label { display: block; margin-bottom: var(--sp-4); }
  /* the pinned bar (5.3): Save rates alone, full width, under the thumb */
  .rate-grid-page { padding-bottom: calc(var(--sp-40) * 3); }
  .rg-bar {
    position: fixed; left: 0; right: 0; bottom: 0; margin: 0; border-radius: 0;
    flex-direction: column; align-items: stretch; gap: var(--sp-8);
    padding: var(--sp-8) var(--sp-16) calc(var(--sp-8) + env(safe-area-inset-bottom, 0px));
    border: 0; border-top: var(--hair) solid var(--hairline);
  }
  .rg-bar .btn { width: 100%; max-width: none; min-height: var(--input-h); }
  /* the launcher lifts above the pinned bar so it never covers Save rates (5.3) */
  body:has(.rg-bar) .chotu-launcher { bottom: calc(var(--input-h) + var(--sp-40) + var(--sp-24)); }
}
/* W17: the one line under the Catalogue views that leads to Put your rates. */
.rate-link-line { font: var(--t-meta); color: var(--ink); margin: 0 0 var(--sp-8); }

/* =========================================================================
   RECORDS AND LINKS (04m W10 to W14, 03n F5 and F7)
   =========================================================================
   Goods came back and credit notes on the store page, the statement's two line
   rows, the bill record's items, the credit note record. Tokens only. A link
   in a record is a real button at least --tap-min tall. */
.cn-row { padding: var(--sp-8) 0; border-top: var(--hair) solid var(--hairline); }
.cn-row:first-of-type { border-top: 0; }
.cn-row__main {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2);
  width: 100%; min-height: var(--tap-min); justify-content: center;
  border: 0; background: none; padding: 0; text-align: left; cursor: pointer; color: var(--ink);
}
.cn-row__main:hover .cn-row__line1 { text-decoration: underline; }
.cn-row__main:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--r-4); }
.cn-row__line1 { font: var(--t-row-name); font-variant-numeric: var(--num); }
.cn-row__grey { font: var(--t-meta); color: var(--text-2); }
.cn-row__trail {
  display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--sp-4);
  font: var(--t-meta); color: var(--text-2);
}
.cn-row__trail .line-change-link,
.stmt-row .line-change-link,
.cn-record .line-change-link,
.order-bill-link .line-change-link { min-height: var(--tap-min); display: inline-flex; align-items: center; }

.stmt-list { list-style: none; margin: 0 0 var(--sp-12); padding: 0; }
.stmt-row { padding: var(--sp-8) 0; border-top: var(--hair) solid var(--hairline); }
.stmt-row:first-child { border-top: 0; }
.stmt-line { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-12); flex-wrap: wrap; font: var(--t-body); }
.stmt-line--grey { font: var(--t-meta); color: var(--text-2); }
.stmt-what { flex: 1 1 12rem; min-width: 0; display: inline-flex; flex-wrap: wrap; align-items: center; column-gap: var(--sp-4); }
.stmt-amt, .stmt-bal { white-space: nowrap; font-variant-numeric: var(--num); font-stretch: var(--wd-num); }
.stmt-amt { font-weight: 600; }

.cn-record dd { overflow-wrap: anywhere; }

/* The bill record's items: a table in a wide dialog, two line rows in a narrow
   one (item and amount, then quantity and rate), as the dialog itself narrows
   under 480 px (5.1). */
@media (max-width: 480px) {
  .bill-items thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .bill-items, .bill-items tbody { display: block; }
  .bill-items tr {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--sp-12);
    padding: var(--sp-8) var(--sp-12); border-top: var(--hair) solid var(--hairline);
  }
  .bill-items td { display: block; padding: 0; border: 0; }
  .bill-items .bi-item { grid-column: 1; grid-row: 1; }
  .bill-items .bi-amt { grid-column: 2; grid-row: 1; text-align: right; }
  .bill-items .bi-qty { grid-column: 1; grid-row: 2; color: var(--text-2); font: var(--t-meta); }
  .bill-items .bi-rate { grid-column: 2; grid-row: 2; text-align: right; color: var(--text-2); font: var(--t-meta); }
  .bill-items tr.bi-discount { border-top: 0; padding-top: 0; }
}
/* A scheme's discount sits under the line it came off (06o BUG-E01). */
.bill-items tr.bi-discount td { border-top: 0; padding-top: 0; color: var(--text-2); font: var(--t-meta); }
.dialog__body p.hi + .field { margin-top: var(--sp-12); }

/* =====================================================================
   Phase F. Voice operations (04m W20 to W25; 03o F01 to F05)
   ===================================================================== */

/* ---- W23: the Chotu panel, a thread with a pinned composer ----
   Desktop: docked bottom right as before, 400 px wide and up to 640 px tall,
   never taller than the window less the launcher and 40 px (D1). */
.chotu-panel {
  width: min(25rem, calc(100vw - var(--sp-32)));
  max-height: min(40rem, calc(100vh - var(--chotu-launcher-h) - var(--sp-40)));
  height: min(40rem, calc(100vh - var(--chotu-launcher-h) - var(--sp-40)));
}
.chotu-thread { display: flex; flex-direction: column; gap: var(--sp-8); }
.chotu-thread > * { flex: 0 0 auto; }
.chotu-bubble__lead { font: var(--t-meta-strong); color: var(--text-2); }
.chotu-bubble--ask .voice__mode { margin: var(--sp-4) 0 0; }
.chotu-bubble--working p { color: var(--text-2); font-style: italic; }
.chotu-panel__chips { flex: 0 0 auto; padding: var(--sp-8) var(--sp-12) 0; }
.chotu-panel__chips[hidden] { display: none; }
.chotu-composer {
  flex: 0 0 auto; padding: var(--sp-8) var(--sp-12) var(--sp-12);
  border-top: var(--hair) solid var(--hairline); background: var(--paper);
}
.chotu-composer__label { display: block; font: var(--t-label); color: var(--text-2); margin-bottom: var(--sp-4); }
.chotu-composer__row { display: flex; align-items: center; gap: var(--sp-8); }
.chotu-composer__row input { flex: 1 1 auto; min-width: 0; min-height: var(--tap-min); }
.chotu-composer__row .btn { min-height: var(--tap-min); }

/* The mic: outline and Ink glyph when idle; Ink fill, paper glyph and three
   level bars while listening. The bars are the one moving thing (M16). */
.chotu-mic-btn {
  position: relative; flex: 0 0 auto;
  width: var(--tap-min); height: var(--tap-min); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: var(--hair-strong) solid var(--ink); background: var(--paper); color: var(--ink);
}
.chotu-mic-btn .chotu-micglyph { width: 1.25rem; height: 1.25rem; }
.chotu-mic-btn[aria-pressed="true"] { background: var(--ink); color: var(--on-ink); }
.chotu-mic-btn[aria-pressed="true"] .chotu-micglyph { display: none; }
.chotu-mic-bars { display: none; align-items: center; gap: 3px; height: 1.25rem; }
.chotu-mic-btn[aria-pressed="true"] .chotu-mic-bars { display: inline-flex; }
.chotu-mic-bars i { display: block; width: 3px; height: 40%; border-radius: 2px; background: currentColor; animation: chotu-level 0.9s ease-in-out infinite; }
.chotu-mic-bars i:nth-child(2) { animation-delay: 0.15s; }
.chotu-mic-bars i:nth-child(3) { animation-delay: 0.3s; }
@keyframes chotu-level { 0%, 100% { height: 30%; } 50% { height: 95%; } }
@media (prefers-reduced-motion: reduce) {
  .chotu-mic-bars i { animation: none; height: 60%; }
}
.chotu-mic-btn:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* Phone, under 544 px: full screen inside the safe areas (D1). With the
   keyboard up the composer sits on it and the thread shrinks (US11 AC4). */
@media (max-width: 34rem) {
  .chotu-panel {
    top: 0; left: 0; right: 0; bottom: 0;
    width: auto; height: auto; max-height: none; border-radius: 0; border: 0;
    padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  }
  .chotu-panel__close { width: var(--tap-min); height: var(--tap-min); }
}

/* ---- W24: answer and draft cards ---- */
.chotu-card-a { display: flex; flex-direction: column; gap: var(--sp-8); align-items: flex-start; width: 100%; }
.chotu-card-a > p { margin: 0; }
.chotu-card-a__title { font: var(--t-card-title); color: var(--ink); margin: 0; }
.chotu-card-a .btn-row { margin: 0; }
.chotu-card-a .btn { min-height: var(--tap-min); }
.chotu-card-a__status { font: var(--t-meta-strong); color: var(--ink); }
.chotu-card-a__note { font: var(--t-meta); color: var(--text-2); }
.chotu-card-a__lines, .chotu-card-a__rows { list-style: none; margin: 0; padding: 0; width: 100%; display: flex; flex-direction: column; gap: var(--sp-6); }
.chotu-card-a__lines li { display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-8); font: var(--t-meta); color: var(--ink); }
.chotu-card-a__rows li { display: flex; flex-direction: column; gap: var(--sp-4); }
.chotu-card-a__row {
  display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-8); align-items: baseline; width: 100%;
  background: none; border: 0; padding: var(--sp-4) 0; text-align: left; cursor: pointer; color: inherit; font: var(--t-meta);
  min-height: var(--tap-min);
}
.chotu-card-a__rowname { font: var(--t-meta-strong); color: var(--ink); }
.chotu-card-a__rowfig { margin-left: auto; font-variant-numeric: var(--num); }
.chotu-card-a__row:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.chotu-chip { min-height: var(--tap-min); }

/* ---- W25: the drafts line on Home ---- */
.chotu-card__drafts { margin: var(--sp-6) 0 0; font: var(--t-meta-strong); }
.chotu-card__draftslink { color: var(--brief-ink, var(--nav-ink)); text-decoration: underline; text-underline-offset: 3px; }
.chotu-card__draftslink:focus-visible { outline: var(--focus-ring) solid var(--brief-focus, var(--nav-focus)); outline-offset: var(--focus-offset); }

/* ---- W20: Drafts list ---- */
.state-tag.discarded { color: var(--text-2); }
.state-tag.discarded::before { display: none; }
.state-tag.draft-added { color: var(--ink); }
.state-tag.draft-added::before { background: transparent; box-shadow: inset 0 0 0 var(--hair-strong) currentColor; }
.draft-row .row__sub { display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-12); align-items: center; justify-content: space-between; }

/* ---- W21, W22: the draft page ---- */
.draft-head .page-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-12); }
.draft-head .state-tag { font: var(--t-meta-strong); }
.draft-page .card { margin-bottom: var(--sp-16); }
.draft-quote { margin: 0 0 var(--sp-12); }
.draft-quote__text { margin: var(--sp-4) 0 0; font: var(--t-body); font-style: italic; color: var(--ink); }
.draft-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--sp-12); }
.draft-said-cell { font-style: italic; color: var(--text-2); }
.draft-table-wrap { overflow-x: auto; }
.draft-table { width: 100%; }
.draft-table th, .draft-table td { text-align: left !important; vertical-align: top; padding: var(--sp-8) var(--sp-8) var(--sp-8) 0; }
.draft-table td { font-variant-numeric: var(--num); }
.draft-status { display: flex; flex-direction: column; gap: var(--sp-4); }
.draft-item { display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-8); align-items: center; }
.draft-item__name { font: var(--t-meta-strong); color: var(--ink); }
/* BUG16 (06p): the unit chips wrap inside the Item column, on their own row under
   the name, so they never widen the table and push Status out of view. */
.draft-item .unit-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4) var(--sp-8); flex: 1 1 100%; min-width: 0; max-width: 20rem; }
.draft-item .btn, .draft-line-actions .btn { min-height: var(--tap-min); }
.draft-move { margin: var(--sp-4) 0 0; }
.draft-rate { width: 7rem; min-height: var(--tap-min); }
.draft-rate-wrap .field-error { margin-top: var(--sp-4); }
.draft-untouched { margin-top: var(--sp-12); }
.draft-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.draft-line { padding: var(--sp-12) 0; border-top: var(--hair) solid var(--rule); display: flex; flex-direction: column; gap: var(--sp-8); }
.draft-line:first-child { border-top: 0; padding-top: 0; }
.draft-line .draft-said-cell { margin: 0; }
.draft-facts { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2) var(--sp-12); margin: 0; font: var(--t-meta); font-variant-numeric: var(--num); }
.draft-facts dt { color: var(--text-2); }
.draft-facts dd { margin: 0; color: var(--ink); }
.draft-before .draft-before__sub { font: var(--t-meta-strong); margin: var(--sp-16) 0 var(--sp-8); }
.draft-choice { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-8); }
.draft-choice legend { font: var(--t-body); color: var(--ink); margin-bottom: var(--sp-8); padding: 0; max-width: var(--measure); }
.radio-row, .check-row { display: flex; align-items: center; gap: var(--sp-8); min-height: var(--tap-min); cursor: pointer; }
.radio-row input, .check-row input { width: 1.25rem; height: 1.25rem; flex: 0 0 auto; }
.draft-result { list-style: none; padding: 0; margin: var(--sp-8) 0 0; }
.draft-actions { padding: var(--sp-12) 0; }
.draft-actions .order-actions-row { margin-bottom: 0; }
.btn__hi { font-weight: 400; }
/* Phone: the action bar is pinned at the foot of the page (W21 mobile). */
@media (max-width: 999px) {
  .draft-actions {
    position: sticky; bottom: 0; z-index: 5; background: var(--paper);
    border-top: var(--hair) solid var(--hairline);
    margin: 0; padding: var(--sp-12) 0 calc(var(--sp-12) + env(safe-area-inset-bottom));
  }
  .draft-actions .order-actions-row .btn { flex: 1 1 0; }
  body:has(.draft-actions) .chotu-launcher { bottom: calc(var(--sp-16) + 6rem); }
}

/* =========================================================================
   ADD FROM OUR LIST (04m W15, W19). Three steps in one column: chips, then a
   tick list under brand headings. Tokens only. A row is a real label at least
   --tap-min tall. On a phone the page's bar is pinned under the thumb with
   Back and Next as equal halves (5.3); in the onboarding overlay (compact)
   the list scrolls inside itself so the buttons stay in view.
   ========================================================================= */
.sp-stepper { list-style: none; display: flex; flex-wrap: wrap; gap: var(--sp-8) var(--sp-16); margin: var(--sp-8) 0 var(--sp-16); padding: 0; font: var(--t-meta); color: var(--text-2); }
.sp-stepper__item { display: inline-flex; align-items: center; gap: var(--sp-6); }
.sp-stepper__n {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: var(--sp-24); height: var(--sp-24); border-radius: 50%;
  border: var(--hair) solid var(--control-edge); font: var(--t-meta-strong); color: var(--text-2);
}
.sp-stepper__item.is-done .sp-stepper__n { background: var(--text-3); border-color: var(--text-3); color: var(--on-ink); }
.sp-stepper__item.is-current { color: var(--ink); font: var(--t-meta-strong); }
.sp-stepper__item.is-current .sp-stepper__n { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.sp-mrpnote { margin: var(--sp-4) 0 0; }
.sp-incomplete { margin: var(--sp-8) 0 0; }
/* 03n 9b.3: the paise line sits under the status pill, not beside it. */
.kv dd:has(> .bill-paise-left) { flex-wrap: wrap; row-gap: var(--sp-4); }
.bill-paise-left { flex-basis: 100%; text-align: right; }
.sp-q { font: var(--t-section); margin: 0 0 var(--sp-8); }
.sp-tools { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-8) var(--sp-16); margin: 0 0 var(--sp-8); }
.sp-search { margin: 0; flex: 1 1 var(--select-min); }
.sp-tickbtns { display: flex; gap: var(--sp-8); flex: 0 0 auto; }
.sp-counter { font: var(--t-meta-strong); color: var(--ink); margin: var(--sp-8) 0; }
.sp-list { border: var(--hair) solid var(--hairline); border-radius: var(--r-12); background: var(--paper); padding: 0 var(--sp-12) var(--sp-8); }
.sp-brand { font: var(--t-label); color: var(--text-2); margin: var(--sp-12) 0 var(--sp-4); }
.sp-row {
  display: flex; align-items: center; gap: var(--sp-12); min-height: var(--tap-min);
  padding: var(--sp-6) 0; border-top: var(--hair) solid var(--hairline); cursor: pointer;
}
.sp-brand + .sp-row { border-top: 0; }
.sp-row input[type="checkbox"] { flex: 0 0 auto; width: var(--sp-20); height: var(--sp-20); margin: 0; accent-color: var(--ink); }
.sp-row__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.sp-row__name { font: var(--t-row-name); color: var(--ink); overflow-wrap: anywhere; }
.sp-row__meta { font: var(--t-meta); color: var(--text-2); font-variant-numeric: var(--num); }
.sp-row.is-have { cursor: default; }
.sp-row.is-have .sp-row__name { color: var(--text-2); }
.sp-row__tag { flex: 0 0 auto; text-align: right; }
.sp-end { display: block; min-height: var(--tap-min); margin: var(--sp-8) 0 0; padding: 0; text-align: left; }
.sp-stop { margin: var(--sp-8) 0 0; }
.sp-bar { justify-content: flex-start; }
.sp-skip { margin: var(--sp-12) 0 0; }
.sp--done + .sp-skip { display: none; }
.sp-done { font: var(--t-body); color: var(--ink); margin: 0 0 var(--sp-12); }
.sp-unrated { font: var(--t-body); color: var(--ink); }
/* inside the onboarding overlay the buttons stay at the foot of the overlay's own scroll */
.sp--compact .sp-barwrap { position: sticky; bottom: calc(var(--sp-20) * -1); background: var(--paper); padding: var(--sp-8) 0 var(--sp-20); margin-top: var(--sp-8); z-index: 1; }
.sp--compact .sp-barwrap .onboarding__actions { margin-top: 0; }

@media (max-width: 999px) {
  /* the pinned bar (5.3): the stop sentence over Back and Next (or Add N
     items) as equal halves, fixed to the foot of the screen */
  :not(.sp--compact) > .sp-body { padding-bottom: calc(var(--sp-40) * 3); }
  .sp-barwrap--page {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-actionbar);
    background: var(--paper); border-top: var(--hair) solid var(--hairline);
    padding: var(--sp-8) var(--sp-16) calc(var(--sp-8) + env(safe-area-inset-bottom, 0px));
  }
  .sp-barwrap--page .sp-stop { margin: 0 0 var(--sp-8); }
  .sp-barwrap--page .sp-bar.actionbar {
    position: static; margin: 0; padding: 0; border: 0; border-radius: 0; box-shadow: none; background: none;
    flex-wrap: nowrap; gap: var(--sp-8);
  }
  .sp-barwrap--page .sp-bar .btn { flex: 1 1 0; width: auto; max-width: none; min-height: var(--tap-min); }
  body:has(.sp-barwrap--page) .chotu-launcher { bottom: calc(var(--input-h) + var(--sp-40) + var(--sp-24)); }
  .sp-tickbtns { width: 100%; }
}

/* BUG-R07 (06_qa_report): text buttons were 20 px tall at 360 with touch
   ("Change item", the rep "Change", "Update limit", the Catalogue
   breadcrumb). On a touch screen every text button gets the 44 px target
   (03p section 8) from its box, not a bigger font, so the words and the line
   they sit in look the same. A mouse keeps them as they are. */
@media (pointer: coarse) {
  .line-change-link,
  .order-breadcrumb__link {
    min-height: var(--tap-min);
    display: inline-flex;
    align-items: center;
  }
  /* The R07 residual (06_qa_report, release retest 1): the dashboard's
     "All 9 →" and "See the flagged orders →" links (20 px), the "since you
     were last here" rows (28 px), and Chotu's send and mic on the dashboard
     card (32 px). The text link inside a tile that is itself the button
     (.dlink--static) is not a control and keeps its size. */
  .dlink:not(.dlink--static) {
    min-height: var(--tap-min);
    display: inline-flex;
    align-items: center;
  }
  button.dashboard-since-item { min-height: var(--tap-min); }
  /* BUG-R20: the dashboard's "Ask Chotu anything" box (32 px), the remove
     cross on a scheme chip (18 px) and the "Add a limit" and "Add a note"
     links (about 28 px) get the 44 px target from their own box. */
  .chotu-card .chotu-card__input { min-height: var(--tap-min); height: var(--tap-min); }
  .chip__x { width: var(--tap-min); height: var(--tap-min); margin: 0 calc(var(--sp-8) * -1) 0 calc(var(--sp-4) * -1); }
  .reveal-toggle { min-height: var(--tap-min); display: inline-flex; align-items: center; }
  .chotu-card__send,
  .chotu-card__mic {
    width: var(--tap-min);
    height: var(--tap-min);
  }
}

/* =========================================================================
   W26 TO W28: SALESMEN AND BEATS (03n F10)
   =========================================================================
   The list page is two list sections (Salesmen, Beats) in the row cards the
   other lists use. The beat page is full width: name, days, then two columns,
   Find stores and In this beat, then the salesman, then the bar. On a phone
   it is one column and Save beat is pinned under the thumb (5.3). No new
   colour: chips, rows, fields and the action bar are the existing ones. */
.salesmen-page .list-section { margin-top: var(--gap-section); }
.salesmen-page .empty { margin-top: var(--sp-8); }
.sm-dialog .sm-field + .sm-field { margin-top: var(--sp-12); }
.sm-dialog .sm-field .hi { margin: var(--sp-4) 0 0; }
.sm-beat-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-4); }

.beat-page { max-width: 72rem; }
.beat-field { margin-top: var(--sp-16); max-width: 32rem; }
.beat-field .hi { margin: var(--sp-4) 0 0; }
.field-label { font: var(--t-label); color: var(--text-2); margin: 0; }
.beat-days { margin-bottom: 0; }
.beat-cols { display: grid; grid-template-columns: 1fr; gap: var(--sp-20); margin-top: var(--gap-section); }
@media (min-width: 1000px) { .beat-cols { grid-template-columns: 1fr 1fr; align-items: start; } }
.beat-col {
  background: var(--paper); border: var(--hair) solid var(--hairline); border-radius: var(--r-12);
  padding: var(--sp-12) var(--sp-16); box-shadow: inset 0 var(--hair) 0 var(--card-gloss), var(--sh-card);
  min-width: 0;
}
.beat-find { display: flex; flex-wrap: wrap; gap: var(--sp-8); align-items: flex-end; margin: var(--sp-8) 0; }
.beat-find input[type="search"] { flex: 1 1 14rem; min-width: 0; }
.beat-area { flex: 0 1 12rem; margin-bottom: 0; }
.beat-results { max-height: 26rem; overflow-y: auto; margin: 0 calc(var(--sp-8) * -1); }
.beat-pick { cursor: pointer; padding: var(--sp-8); }
.beat-pick input[type="checkbox"] { width: var(--tap-min); height: var(--tap-min); max-width: 22px; max-height: 22px; flex: 0 0 auto; margin: 0; }
.beat-pick .t, .beat-pick .s { display: block; }
.beat-results__note { margin: var(--sp-8) 0 0; }
.beat-results__note:empty { display: none; }
.beat-order { list-style: none; margin: var(--sp-8) 0 0; padding: 0; }
.beat-order__row {
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-8) 0; border-top: var(--hair) solid var(--rule);
}
.beat-order__row:first-child { border-top: 0; }
.beat-order__n { font: var(--t-meta-strong); color: var(--text-2); min-width: 2ch; text-align: right; font-variant-numeric: var(--num); }
.beat-order__name { flex: 1 1 auto; min-width: 0; }
.beat-order__name .t { display: block; font: var(--t-row-name); color: var(--ink); overflow-wrap: anywhere; }
.beat-order__name .s { display: block; font: var(--t-meta); color: var(--text-2); }
.beat-order__btns { display: flex; gap: var(--sp-4); flex: 0 0 auto; }
.beat-order__btn { min-width: var(--tap-min); min-height: var(--tap-min); padding: 0 var(--sp-8); }
.beat-order__empty { font: var(--t-body); color: var(--text-2); padding: var(--sp-8) 0; }
.beat-bar { justify-content: flex-start; }
@media (max-width: 999px) {
  .beat-page { padding-bottom: calc(var(--sp-40) * 3); }
  .beat-results { max-height: 18rem; }
  .beat-bar {
    position: fixed; left: 0; right: 0; bottom: 0; margin: 0; border-radius: 0;
    flex-wrap: nowrap; gap: var(--sp-8);
    padding: var(--sp-8) var(--sp-16) calc(var(--sp-8) + env(safe-area-inset-bottom, 0px));
    border: 0; border-top: var(--hair) solid var(--hairline);
  }
  .beat-bar .btn { flex: 1 1 0; max-width: none; min-height: var(--input-h); }
  body:has(.beat-bar) .chotu-launcher { bottom: calc(var(--input-h) + var(--sp-40) + var(--sp-24)); }
}
@media (max-width: 480px) {
  .beat-order__row { flex-wrap: wrap; }
  .beat-order__btns { width: 100%; padding-left: calc(2ch + var(--sp-8)); }
  .beat-order__btn { flex: 1 1 0; }
}

/* =========================================================================
   W29: DESCRIBE IT (03n F9)
   =========================================================================
   The card on top of the Schemes list, his words quoted above the filled
   card, and the Not said blank (5.13): a dashed edge and the words, never a
   colour. Nothing here is red: a blank is not an error. */
.scheme-describe { margin: 0 0 var(--sp-12); }
.scheme-describe__title { display: block; margin-bottom: var(--sp-8); }
.scheme-describe textarea { width: 100%; min-height: calc(var(--input-h) * 2); resize: vertical; }
.scheme-describe__row { margin-top: var(--sp-8); }
.scheme-describe__speak { display: inline-flex; align-items: center; gap: var(--sp-4); }
.scheme-describe__speak .voice__icon { width: 18px; height: 18px; }
.scheme-describe__speak.is-listening { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }
.scheme-describe__working { margin: var(--sp-8) 0 0; }
.scheme-describe__working:empty { display: none; }
.scheme-words {
  margin: var(--sp-8) 0 var(--sp-4); padding: var(--sp-8) var(--sp-12);
  border-left: var(--sp-4) solid var(--hairline); background: var(--paper);
  font: var(--t-body); color: var(--ink); overflow-wrap: anywhere;
}
.scheme-words__label { font-weight: 700; }
.scheme-words__note { margin: 0 0 var(--sp-12); }
input.is-notsaid { border-style: dashed; border-color: var(--control-edge); }
input.is-notsaid::placeholder { color: var(--text-2); opacity: 1; }
.notsaid-note { font: var(--t-meta); color: var(--text-2); margin-left: var(--sp-4); }
p.notsaid-note { margin: 0 0 var(--sp-4); }
/* A date his words named but did not settle: Ink text with the --warn field edge (04m D19), never the credit red. */
input.is-check { border-style: solid; border-color: var(--warn); box-shadow: 0 0 0 var(--hair) var(--warn); }
.notsaid-note.is-check { color: var(--ink); font-weight: 700; }
.scheme-words__check { color: var(--ink); padding-left: var(--sp-12); box-shadow: inset var(--bar-ink) 0 0 var(--warn); }
.scheme-itempick__q { font: var(--t-meta-strong); color: var(--ink); margin: var(--sp-4) 0; }
.scheme-itempick__rows { border: var(--hair) solid var(--hairline); border-radius: var(--r-8); margin-bottom: var(--sp-8); }

/* =========================================================================
   ITEM LEVEL RETURNS (03r Part 1, RT01 to RT05)
   =========================================================================
   The item list inside Goods came back, the lines on the credit note record,
   the Returned card on the order page and the damaged stock line on Stock.
   Tokens only, no colour of its own: a return is not a credit state, so the
   credit colours never appear here. Every control keeps --tap-min. */
.rt-items { margin: var(--sp-4) 0 var(--sp-16); padding-top: var(--sp-12); border-top: var(--hair) solid var(--hairline); }
.rt-items[hidden] { display: none; }
.rt-items__title { font: var(--t-meta-strong); color: var(--ink); margin: 0 0 var(--sp-4); }
.rt-items__total { font: var(--t-meta-strong); color: var(--ink); font-variant-numeric: var(--num); text-align: right; margin: var(--sp-8) 0 0; }
.rt-list { display: flex; flex-direction: column; }
.rt-line { padding: var(--sp-12) 0; border-top: var(--hair) solid var(--hairline); min-width: 0; }
.rt-line:first-child { border-top: 0; }
.rt-line__head { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-8); }
.rt-line__name { font: var(--t-row-name); color: var(--ink); overflow-wrap: anywhere; }
.rt-line__inputs { display: flex; flex-wrap: wrap; gap: var(--sp-8) var(--sp-12); align-items: flex-end; }
.rt-field { display: flex; flex-direction: column; gap: var(--sp-4); flex: 1 1 9rem; min-width: 0; }
.rt-field__label { font: var(--t-label); color: var(--text-2); }
.rt-qty { display: flex; gap: var(--sp-6); }
.rt-qty input { flex: 1 1 4rem; min-width: 0; }
.rt-qty select { flex: 1 1 5rem; min-width: 0; }
.rt-line__preview { margin-top: var(--sp-6); }
.rt-line__preview:empty { display: none; }
.rt-record { margin-top: var(--sp-16); }
.rt-damaged { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-8); }
.rt-damaged .btn { min-height: var(--tap-min); }
.order-returned-card ul { list-style: none; margin: var(--sp-8) 0 0; padding: 0; }
.order-returned-card li { font: var(--t-meta); color: var(--ink); font-variant-numeric: var(--num); padding: var(--sp-4) 0; }
.rt-returned__lines li { display: flex; justify-content: space-between; gap: var(--sp-12); border-top: var(--hair) solid var(--hairline); }
.rt-returned__lines li > span:first-child { min-width: 0; overflow-wrap: anywhere; }
.rt-returned__qty { white-space: nowrap; }
@media (max-width: 480px) {
  .rt-field { flex-basis: 100%; }
}
/* The record's lines wrap rather than scroll sideways, and "Goes to" is a
   word, not money: it does not take the price colour of a third column. */
.rt-record td { white-space: normal; overflow-wrap: anywhere; }
.rt-record table:has(thead) td:nth-child(3) { font: var(--t-meta); color: var(--ink); text-align: left; }
.rt-record table:has(thead) th:nth-child(3) { text-align: left; }
.rt-record td.bi-amt { white-space: nowrap; }
@media (max-width: 480px) {
  .rt-record table:has(thead) td.bi-rate { text-align: right; color: var(--text-2); }
}

/* =========================================================================
   VOICE MODE (voice slice 2; 04n VM04 to VM09)
   =========================================================================
   Full screen on a phone, a centred 480 px panel on a desktop, side by side
   when the screen is short, and the strip at the top of a page it opened.
   NO NEW COLOUR (04n 4.1, K10): the band reads the Home brief's own tokens in
   the Field look and the page's ground in the Daylight look, through the
   five local names below, set once per look. Cards, his words and the
   controls sit on --ground and --paper in both. No credit tone is read here,
   and no rule here touches a credit pill or block (04n 4.1, credit colours).
   The yellow primary is never a Voice mode control (VD12).
   Contrast: 04n 4.2, lowest anywhere 4.80:1 (Cobalt Field hint text).
   The only thing that moves is the wave (and its timer line); a card rises
   12 px once, while the wave is still (VD10). Reduced motion stills all of it. */
:root {
  --z-vm: 45;
  --z-vm-strip: 55;
  --vm-topbar-h: 56px;
  --vm-band-h: 244px;
  --vm-btn-main: 72px;
  --vm-btn-side: 56px;
  --vm-r-band: 24px;
  --vm-panel-w: 480px;
  --vm-panel-w-side: 760px;
  --vm-side-hero-w: 300px;
  --vm-content-max: 560px;
  --vm-strip-h: 88px;
  --t-vm-state: 700 max(22px, 1.375rem)/1.2 var(--font-latin);
  --t-vm-heard: 600 max(20px, 1.25rem)/1.35 var(--font-latin);
  --t-vm-hint: 500 max(14px, 0.875rem)/1.35 var(--font-latin);
  --t-vm-card-title: 700 max(18px, 1.125rem)/1.25 var(--font-latin);
  --t-vm-fig: 800 max(28px, 1.75rem)/1.1 var(--font-latin);
  --t-vm-btn: 600 max(14px, 0.875rem)/1.2 var(--font-latin);
}
@media (pointer: fine) {
  :root {
    --t-vm-state: 700 max(20px, 1.25rem)/1.2 var(--font-latin);
    --t-vm-heard: 600 max(18px, 1.125rem)/1.35 var(--font-latin);
    --t-vm-hint: 500 max(13px, 0.8125rem)/1.35 var(--font-latin);
    --t-vm-card-title: 700 max(16px, 1rem)/1.25 var(--font-latin);
    --t-vm-fig: 800 max(24px, 1.5rem)/1.1 var(--font-latin);
    --t-vm-btn: 600 max(13px, 0.8125rem)/1.2 var(--font-latin);
  }
}
@media (min-width: 1000px) { :root { --vm-strip-h: 56px; } }

/* The two looks (VD1), as five local names. */
.vm, .vm-strip {
  --vm-band-bg: var(--brief-bg, var(--nav-bg));
  --vm-band-ink: var(--brief-ink, var(--nav-ink));
  --vm-band-ink-2: var(--brief-ink-2, var(--nav-ink-2));
  --vm-curve: var(--brief-hl, var(--gold));
  --vm-band-focus: var(--brief-focus, var(--nav-focus));
}
.vm[data-vm-look="daylight"], .vm-strip[data-vm-look="daylight"] {
  --vm-band-bg: var(--ground);
  --vm-band-ink: var(--ink);
  --vm-band-ink-2: var(--text-2);
  --vm-curve: var(--pine);
  --vm-band-focus: var(--focus);
}
.vm-strip[data-vm-look="daylight"] { --vm-band-bg: var(--paper); }

/* ---- the shell (5.1) ---- */
.vm-scrim {
  position: fixed; inset: 0; z-index: var(--z-vm);
  display: flex; align-items: stretch; justify-content: center;
  animation: vm-fade var(--dur-med) var(--ease-out);
}
.vm-scrim[hidden] { display: none; }
@keyframes vm-fade { from { opacity: 0; } to { opacity: 1; } }
.vm {
  position: relative; width: 100%; height: 100%;
  display: flex; flex-direction: column; min-height: 0;
  background: var(--ground); color: var(--ink);
  padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) 0 env(safe-area-inset-left, 0px);
  overflow: hidden;
}
.vm.is-leaving { opacity: 0; transition: opacity var(--dur-fast) var(--ease-out); }
.vm__stage { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.vm__stage[hidden] { display: none; }
.vm__side { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

/* ---- the band (5.3) and the top bar (5.2) ---- */
.vm__band {
  flex: 0 0 auto; height: var(--vm-band-h);
  display: flex; flex-direction: column;
  background: var(--vm-band-bg); color: var(--vm-band-ink);
  border-radius: 0 0 var(--vm-r-band) var(--vm-r-band);
}
.vm[data-vm-look="daylight"] .vm__band { border-radius: 0; }
.vm__top {
  flex: 0 0 auto; min-height: var(--vm-topbar-h); padding: 0 var(--sp-12);
  display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap;
}
.vm__topgap { flex: 1 1 auto; }
.vm-chip {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  min-height: var(--tap-min); padding: 0 var(--sp-12); border-radius: var(--r-pill);
  font: var(--t-vm-btn); color: var(--vm-band-ink); background: transparent;
  border: 2px solid var(--vm-band-ink); cursor: pointer;
}
.vm-chip .vm-glyph { width: var(--sp-16); height: var(--sp-16); }
.vm-chip[aria-pressed="true"] { background: var(--vm-band-ink); color: var(--vm-band-bg); }
.vm-chip[aria-disabled="true"] { opacity: .5; cursor: default; }
.vm-chip:focus-visible { outline: var(--focus-ring) solid var(--vm-band-focus); outline-offset: var(--focus-offset); }
.vm__hero {
  flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-8); padding: var(--sp-8) var(--sp-16) var(--sp-12);
}
.vm[data-tap="1"] .vm__hero { cursor: pointer; }
.vm__who { display: flex; align-items: center; justify-content: center; gap: var(--sp-8); max-width: 100%; }
.vm__who .chotu-face { width: 40px; height: 40px; flex: 0 0 auto; }
.vm__word { font: var(--t-vm-state); color: var(--vm-band-ink); text-align: center; }
.vm__hint { font: var(--t-vm-hint); color: var(--vm-band-ink-2); min-height: 20px; text-align: center; }

/* ---- the wave (5.4): dots, bars, curve; aria-hidden, the word carries the state ---- */
.vm-wave {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 170px; height: 96px; flex: 0 0 auto;
  --vm-bar: 10px; --vm-gap: 10px;
}
.vm-wave--strip { width: 36px; height: 32px; --vm-bar: 4px; --vm-gap: 4px; }
.vm-wave__bars { display: inline-flex; align-items: center; justify-content: center; gap: var(--vm-gap); height: 100%; }
.vm-wave__bar {
  display: block; width: var(--vm-bar); height: var(--vm-bar);
  border-radius: var(--r-pill); background: var(--vm-band-ink-2);
}
.vm-wave[data-form="bars"] .vm-wave__bar { background: var(--vm-band-ink); }
.vm-wave__curve { display: none; position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.vm-wave__path { fill: none; stroke: var(--vm-curve); stroke-linecap: round; stroke-linejoin: round; }
.vm-wave[data-form="curve"] .vm-wave__bars { visibility: hidden; }
.vm-wave[data-form="curve"] .vm-wave__curve { display: block; }
.vm-wave__timer { position: absolute; left: 0; right: 0; bottom: -6px; height: 2px; }
.vm-wave__timerline { display: block; height: 2px; background: var(--vm-band-ink-2); transform-origin: center; }
.vm-wave__segs { display: none; gap: 4px; height: 2px; }
.vm-wave__seg { flex: 1 1 0; height: 2px; background: var(--vm-band-ink-2); }
.vm-wave__seg[data-on="0"] { background: transparent; }
.vm-wave__timer[data-reduced="1"] .vm-wave__timerline { display: none; }
.vm-wave__timer[data-reduced="1"] .vm-wave__segs { display: flex; }

/* ---- the turn column (5.5) ---- */
.vm__turns { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: var(--sp-16); background: var(--ground); }
.vm__turnsin { max-width: var(--vm-content-max); margin: 0 auto; display: flex; flex-direction: column; gap: var(--sp-12); }
.vm-said { font: var(--t-vm-heard); color: var(--ink); overflow-wrap: anywhere; }
.vm-said__lead { color: var(--text-2); }
.vm-said__interim { color: var(--ink); }
.vm-mode { font: var(--t-vm-hint); color: var(--text-2); margin: 0; }
.vm-line { font: var(--t-vm-hint); color: var(--ink); }
.vm-empty__lead { font: var(--t-meta); color: var(--text-2); margin-bottom: var(--sp-8); }
.vm-choices { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.vm-choice {
  min-height: var(--tap-min); padding: 0 var(--sp-16); border-radius: var(--r-pill);
  font: var(--t-vm-btn); color: var(--ink); background: var(--paper);
  border: var(--hair) solid var(--control-edge); cursor: pointer;
}
.vm-choice:hover { background: var(--hover); }
.vm-choice:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* ---- the voice card (5.6): the W24 card, read at arm's length ----
   It IS a .card (paper, the card edge, the card shadow, the overdue top edge
   on a STOP credit answer), as on the store page; this only sizes it. */
.card.vm-card {
  padding: var(--sp-16); margin-bottom: 0; width: 100%; max-width: var(--vm-content-max);
  display: flex; flex-direction: column; gap: var(--sp-8); color: var(--ink);
}
.vm-card p { font: var(--t-body); }
.vm-card__title, .vm-card .chotu-card-a__title { font: var(--t-vm-card-title); color: var(--ink); }
.vm-card .chotu-answer__fig { font: var(--t-vm-fig); font-variant-numeric: tabular-nums; color: var(--ink); }
.vm-card .chotu-card-a { border: 0; box-shadow: none; padding: 0; background: none; }
.vm-rise { animation: vm-rise var(--dur-med) var(--ease-out); }
@keyframes vm-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* ---- the control bar and round buttons (5.7) ---- */
.vm__controls {
  flex: 0 0 auto; display: flex; align-items: flex-end; justify-content: center; gap: 40px;
  padding: var(--sp-8) var(--sp-16) calc(var(--sp-8) + env(safe-area-inset-bottom, 0px));
  background: var(--ground);
}
.vm__ctl { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: var(--vm-btn-side); }
.vm-round {
  width: var(--vm-btn-side); height: var(--vm-btn-side); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  background: var(--paper); color: var(--ink); border: 2px solid var(--ink); cursor: pointer;
}
.vm-round .vm-glyph { width: 24px; height: 24px; }
.vm-round--main { width: var(--vm-btn-main); height: var(--vm-btn-main); }
.vm-round--main .vm-glyph { width: 30px; height: 30px; }
.vm-round--small { width: var(--tap-min); height: var(--tap-min); flex: 0 0 auto; }
.vm-round.is-live { background: var(--ink); color: var(--on-ink); }
.vm-round[aria-disabled="true"] { opacity: .5; cursor: default; }
.vm-round:hover:not([aria-disabled="true"]):not(.is-live) { background: var(--hover); }
.vm-round:active:not([aria-disabled="true"]) { transform: translateY(1px); }
.vm-round:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }
.vm-round__label { font: var(--t-vm-btn); color: var(--ink); text-align: center; }

/* ---- Baatein (5.11, VM07) ---- */
.vm-chat { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; background: var(--ground); }
.vm-chat[hidden] { display: none; }
.vm-chat__head {
  flex: 0 0 auto; min-height: var(--vm-topbar-h); padding: 0 var(--sp-12);
  display: flex; align-items: center; gap: var(--sp-8);
  background: var(--vm-band-bg); color: var(--vm-band-ink);
}
.vm-chat__title { font: var(--t-vm-card-title); flex: 1 1 auto; text-align: center; color: var(--vm-band-ink); }
.vm-chat__log { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: var(--sp-16); display: flex; flex-direction: column; gap: var(--sp-8); }
.vm-chat__composer {
  flex: 0 0 auto; background: var(--paper); border-top: var(--hair) solid var(--hairline);
  padding: var(--sp-8) var(--sp-12) calc(var(--sp-8) + env(safe-area-inset-bottom, 0px));
}
.vm-chat__label { display: block; font: var(--t-label); color: var(--text-2); margin-bottom: var(--sp-4); }
.vm-chat__row { display: flex; align-items: center; gap: var(--sp-8); }
.vm-chat__input { flex: 1 1 auto; min-width: 0; min-height: var(--input-h); }

/* ---- Voice settings (VM08) ---- */
.vm-settings { display: flex; flex-direction: column; gap: var(--sp-16); }
.vm-settings__row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-12); }
.vm-settings__label { font: var(--t-section); color: var(--ink); }
.vm-settings__group { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--sp-8); }
.vm-settings__group legend { font: var(--t-section); color: var(--ink); margin-bottom: var(--sp-4); }
.vm-radio { display: flex; align-items: flex-start; gap: var(--sp-8); min-height: var(--tap-min); }
.vm-radio input { width: 20px; height: 20px; margin-top: 2px; accent-color: var(--ink); }
.vm-radio label { display: flex; flex-direction: column; color: var(--ink); cursor: pointer; }
.vm-switch {
  position: relative; flex: 0 0 auto; width: 52px; height: 32px; border-radius: var(--r-pill);
  background: var(--paper); border: 2px solid var(--ink); cursor: pointer; padding: 0;
}
.vm-switch__knob { position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: var(--ink); }
.vm-switch[aria-checked="true"] { background: var(--ink); }
.vm-switch[aria-checked="true"] .vm-switch__knob { left: 23px; background: var(--on-ink); }
.vm-switch[aria-disabled="true"] { opacity: .5; cursor: default; }
.vm-switch:focus-visible { outline: var(--focus-ring) solid var(--focus); outline-offset: var(--focus-offset); }

/* ---- the strip (5.8, VM09): pushes the page down by its own height, covers nothing ---- */
.vm-strip {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-vm-strip);
  min-height: var(--vm-strip-h);
  padding: calc(6px + env(safe-area-inset-top, 0px)) var(--sp-12) 6px;
  background: var(--vm-band-bg); color: var(--vm-band-ink);
  display: flex; flex-direction: column; justify-content: center;
}
.vm-strip[hidden] { display: none; }
.vm-strip[data-vm-look="daylight"] { border-bottom: var(--hair) solid var(--hairline); }
.vm-strip__row { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; }
.vm-strip__row > .vm-strip__talk { flex: 1 1 auto; min-width: 0; }
.vm-strip__talk {
  display: inline-flex; align-items: center; gap: var(--sp-8); min-height: 44px; padding: 0 var(--sp-8);
  border-radius: var(--r-pill); border: 2px solid var(--vm-band-ink); background: transparent; color: var(--vm-band-ink);
  cursor: pointer; font: var(--t-vm-btn); flex: 0 0 auto;
}
.vm-strip__talk .chotu-face { width: 24px; height: 24px; }
.vm-strip__talk[aria-disabled="true"] { opacity: .5; cursor: default; }
.vm-strip__word { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.vm-strip__text { flex: 1 1 100%; order: 3; min-width: 0; display: flex; align-items: flex-start; gap: var(--sp-8); }
.vm-strip__line {
  font: var(--t-vm-hint); color: var(--vm-band-ink); flex: 1 1 auto; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.vm-strip__more, .vm-strip__btn {
  min-height: 44px; padding: 0 var(--sp-12); border-radius: var(--r-pill); flex: 0 0 auto;
  font: var(--t-vm-btn); color: var(--vm-band-ink); background: transparent; border: 2px solid var(--vm-band-ink); cursor: pointer;
}
.vm-strip__btn { padding: 0 var(--sp-8); }
.vm-strip__more { min-height: 32px; border-width: 1px; }
.vm-strip :focus-visible { outline: var(--focus-ring) solid var(--vm-band-focus); outline-offset: var(--focus-offset); }
body.has-vm-strip { padding-top: var(--vm-strip-h); }
body.has-vm-strip .app-bar, body.has-vm-strip .mobile-bar { top: var(--vm-strip-h); }
body.has-vm-strip .scrim { padding-top: calc(var(--dialog-inset) + var(--vm-strip-h)); }
@media (min-width: 1000px) {
  body.has-vm-strip .sidebar { top: var(--vm-strip-h); height: calc(100vh - var(--vm-strip-h)); }
  .vm-strip { flex-direction: row; align-items: center; padding-top: 6px; }
  .vm-strip__row { flex-wrap: nowrap; flex: 1 1 auto; }
  .vm-strip__text { order: 0; flex: 1 1 auto; align-items: center; }
  .vm-strip__line { -webkit-line-clamp: 1; }
  .vm-strip__talk .chotu-face { width: 32px; height: 32px; }
}

/* ---- entry points (5.12) ---- */
.vm-glyph { display: block; flex: 0 0 auto; }
.chotu-card__talk {
  width: auto; border-radius: var(--r-pill); padding: 0 var(--sp-12); gap: var(--sp-6);
  font: var(--t-button);
}
.chotu-card__talk .vm-glyph { width: var(--icon-inline); height: var(--icon-inline); }
.chotu-panel__talk {
  display: inline-flex; align-items: center; gap: var(--sp-6); min-height: 32px; padding: 0 var(--sp-12);
  border-radius: var(--r-pill); border: var(--hair) solid var(--nav-edge); background: transparent;
  color: var(--brief-ink, var(--nav-ink)); font: var(--t-button); cursor: pointer; flex: 0 0 auto;
}
.chotu-panel__talk .vm-glyph { width: var(--icon-inline); height: var(--icon-inline); }
.chotu-panel__talk:focus-visible { outline: var(--focus-ring) solid var(--brief-focus, var(--nav-focus)); outline-offset: var(--focus-offset); }
.chotu-launcher-voice { display: none; }
.chotu-pill.has-voice {
  position: fixed; right: var(--sp-16); bottom: var(--sp-16); z-index: 41;
  display: flex; align-items: stretch; border-radius: var(--r-pill);
  background: var(--brief-bg, var(--nav-bg)); border: var(--hair) solid var(--nav-edge); box-shadow: var(--sh-overlay);
}
.chotu-pill.has-voice .chotu-launcher { position: static; border: 0; box-shadow: none; border-radius: var(--r-pill) 0 0 var(--r-pill); }
.chotu-pill.has-voice .chotu-launcher-voice {
  display: inline-flex; align-items: center; gap: var(--sp-8); min-width: 44px; padding: 0 var(--sp-16) 0 var(--sp-12);
  border: 0; border-left: var(--hair) solid var(--nav-edge); border-radius: 0 var(--r-pill) var(--r-pill) 0;
  background: transparent; color: var(--brief-ink, var(--nav-ink)); font: var(--t-button); cursor: pointer;
}
.chotu-pill.has-voice .chotu-launcher-voice:hover { background: var(--brief-field, var(--nav-hover)); }
.chotu-pill.has-voice .chotu-launcher-voice:focus-visible { outline: var(--focus-ring) solid var(--brief-focus, var(--nav-focus)); outline-offset: var(--focus-offset); }
.chotu-launcher-voice .vm-glyph { width: var(--icon-inline); height: var(--icon-inline); }
.has-dialog .chotu-pill.has-voice { display: none; }

/* ---- sizes by screen (4.4) ---- */
/* Compact phone band: 480 to 699 px tall. */
@media (max-width: 999px) and (min-height: 480px) and (max-height: 699px) {
  :root { --vm-band-h: 192px; }
  .vm__who .chotu-face { width: 32px; height: 32px; }
  .vm-wave:not(.vm-wave--strip) { width: 136px; height: 64px; --vm-bar: 8px; --vm-gap: 8px; }
  .vm__hero { gap: var(--sp-4); padding-bottom: var(--sp-8); }
}
/* The desktop panel: centred, 480 wide, on the W01 scrim. */
@media (min-width: 1000px) {
  .vm-scrim {
    align-items: center; padding: 24px;
    background: rgba(var(--scrim-rgb), .45); backdrop-filter: blur(var(--blur-scrim));
  }
  .vm {
    width: var(--vm-panel-w); height: min(800px, calc(100vh - 48px)); padding: 0;
    border-radius: var(--r-12); box-shadow: var(--sh-overlay); border: var(--hair) solid var(--hairline);
  }
}
/* Side by side: a phone held sideways, or a desktop window under 700 px tall. */
@media (max-height: 479px), (min-width: 1000px) and (max-height: 699px) {
  .vm__stage { flex-direction: row; }
  .vm__band { width: var(--vm-side-hero-w); height: auto; border-radius: 0; }
  .vm__side { flex: 1 1 auto; }
}
@media (min-width: 1000px) and (max-height: 699px) {
  .vm { width: var(--vm-panel-w-side); }
}

@media (prefers-reduced-motion: reduce) {
  .vm-scrim, .vm-rise { animation: none; }
  .vm.is-leaving { transition: none; }
}
@media print {
  .vm-scrim, .vm-strip, .chotu-pill { display: none !important; }
  body.has-vm-strip { padding-top: 0; }
}
