/* ============================================================================
   APP.CSS — chrome + the generic component layer.
   ----------------------------------------------------------------------------
   Every .cd-* class here carries the SAME NAME as Pulse's nordic.css and is
   re-implemented at the new token values. Nothing is renamed, so the port is a
   file swap and no markup changes.

   RULE: app.css may not define a .pr-* rule (that is procurement.css's job),
   and procurement.css may not define a .cd-* rule. check-portability.sh
   enforces both. That is what keeps the two layers separable at port time.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { font-size: 16px; -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--page); color: var(--ink);
  font-family: var(--font); font-size: var(--t-body); line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
::selection { background: var(--accent-tint); }

/* CJK: glyphs fill the em box, so a 1.35 line-height that suits Latin clips
   中文 and makes stacked bilingual cells touch. 1.4 is the floor everywhere.
   Tracking on CJK reads as a spacing bug and text-transform is a no-op that
   only shouts at the Latin half of a mixed string — neither may appear on any
   element that can hold Chinese. */
:lang(zh), [lang="zh"] { line-height: var(--lh-cjk); }
.cd-table td, .cd-table th, .cd-badge { letter-spacing: 0; text-transform: none; }

/* Tabular figures by scope, not globally — global tnum makes prose numbers
   look mechanical, and SF Pro's proportional figures read better in a sentence.
   The second declaration is belt-and-braces for the older Chromium in the PDF
   Lambda, which honours font-feature-settings but not the shorthand. */
.cd-table tbody, .cd-table tfoot,
input[type="number"], input[type="date"] {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* ══════════════════════════════════════════════════════════════════════════
   CHROME — live Pulse's header, verbatim.

   The old 264px accordion sidebar is gone. Live Pulse does not have one: it
   has a 60px sticky header carrying a HORIZONTAL top-nav, each group opening
   a dropdown panel that can hold a third level of uppercase section headings.
   Group -> section -> page, three levels, no more.

   The header is joined above 768px by a DOCKED LEFT PANEL — see THE DOCKED
   PANEL below. It is not the old sidebar coming back: it is the mobile sheet
   this file already styles, re-posed by one media query, because a top-nav is
   the wrong instrument for moving within a twenty-nine-page module.

   Every class name below is live Pulse's own (`app-header-menu__*`,
   `top-nav__*`, `mobile-nav__*`, `mt3-dock__*`, `nav-badge*`) at live Pulse's
   own values, so porting the module back is a file swap and not one line of
   markup changes. Colour comes from the `--sh-*` tokens in shell-themes.css —
   nothing here hardcodes the gradient except as a fallback for the case where
   that file is missing.

   NOTE on the prefix: tokens.css has used `--sh-pop` / `--sh-modal` /
   `--sh-stick` / `--sh-col` for SHADOWS since the first commit. The shell
   theme's tokens are all `--sh-<role>` names that do not collide with those
   four; the overlap is in the prefix only and is called out here so nobody
   "tidies" one set into the other.

   DELIBERATE EXCEPTION to §10 ("colour/size literals live in tokens.css"):
   this block alone keeps live Pulse's literals — 60px, 13px, #374151,
   rgba(255,255,255,.85), 0 10px 40px rgba(0,0,0,.15). They are here BECAUSE
   they are the other app's values: the whole point of the change is that this
   file can be dropped into Pulse and the chrome comes out identical. Routing
   them through the prototype's own token names would re-skin the chrome, which
   is exactly what was NOT asked for. Everything a theme can change still goes
   through --sh-*, and every z-index, radius and weight that the module already
   names still uses the token — the exception is the colour/size of the bar,
   not a licence for the file.
   ══════════════════════════════════════════════════════════════════════════ */

/* The footer is the last thing in the body and has margin-top:auto, so the
   body has to be the column it sits at the bottom of — otherwise a short page
   floats its footer half way up the viewport. */
body { display: flex; flex-direction: column; min-height: 100vh; }

.app-header-menu {
  background: var(--sh-header-bg, linear-gradient(135deg, #667eea 0%, #764ba2 100%));
  color: var(--sh-fg, #fff);
  position: sticky; top: 0; z-index: var(--z-header);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .1);
  flex: none;
}
.app-header-menu__main {
  display: flex; align-items: center; gap: 16px; padding: 0 20px; height: 60px;
}

/* ---- Hamburger (mobile only) --------------------------------------------- */
.app-header-menu__hamburger {
  display: none; flex-direction: column; justify-content: center; gap: 5px;
  width: 36px; height: 36px; padding: 6px; border: none;
  background: rgba(255, 255, 255, .1); border-radius: 6px; cursor: pointer;
  transition: background .2s ease;
}
.app-header-menu__hamburger:hover { background: rgba(255, 255, 255, .2); }
.app-header-menu__hamburger-line {
  display: block; width: 100%; height: 2px; background: #fff; border-radius: 1px;
}

/* ---- Logo. An inline SVG wordmark, never an <img src>: the whole module has
   to open off file:// with no request leaving the machine. ----------------- */
.app-header-menu__logo-link {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: 5px 7px; border-radius: 10px; background: transparent;
  transition: background .15s ease;
}
.app-header-menu__logo-link:hover { background: var(--sh-hover, rgba(255,255,255,.12)); text-decoration: none; }
/* AN ANCHOR IN THE CHROME TAKES THE CHROME'S COLOUR, NOT THE LINK COLOUR.
   `a { color: var(--accent) }` at the top of this file is right for a page and
   wrong for the header, and it wins on specificity over the colour the header
   sets on itself. The wordmark is an inline SVG whose text is
   fill="currentColor", so on 30 Aug — the day the accent moved back to
   #667eea, which IS the first stop of the header gradient — the wordmark was
   painted #667eea on #667eea and simply disappeared. It had been legible
   before only because the accent happened to be a dark indigo.
   Scoped to the header and the dock so every anchor in the chrome inherits
   the shell's own foreground, whatever theme is on. */
.app-header-menu a, .app-header-menu a:hover,
.mt3-dock a, .mt3-dock a:hover { color: inherit; }
/* The dropdown panels are white sheets, not the bar: they set their own ink
   and must not inherit the header's white.

   AND SO ARE THE POPOVERS. This list was two selectors long and missed
   .cd-search-pop / .uv-quick-pop, which are also white cards hanging off the
   header — so the quick-print panel drew its "Batch 355465" link in white on
   white and the title simply was not there. Same fix, same reason, wider net:
   anything inside the header that is a SHEET rather than the BAR opts back out
   of the bar's foreground. */
.app-header-menu__dropdown a, .top-nav__dropdown a { color: #374151; }
.cd-search-pop a, .uv-quick-pop a { color: var(--link); }
.app-header-menu__logo { height: 36px; width: auto; display: block; }

/* ---- DEMO pill. Live Pulse's DEV pill, at the one job this module has for
   it: saying out loud that every number on screen is seed data, and whether
   the browser is now holding edits on top of it. -------------------------- */
.app-header-menu__dev {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  height: 26px; padding: 0 10px; border: none; border-radius: var(--r-pill);
  background: linear-gradient(90deg, #f59e0b, #ef4444); color: #fff;
  font: inherit; font-size: 11px; font-weight: var(--w-bold); line-height: 1;
  white-space: nowrap; cursor: pointer;
}
.app-header-menu__dev.is-collapsed { width: 26px; padding: 0; justify-content: center; }
.app-header-menu__dev:focus-visible { outline: 2px solid var(--sh-fg, #fff); outline-offset: 1px; }

/* ---- Title section ------------------------------------------------------- */
.app-header-menu__title-section { display: flex; flex-direction: column; flex-shrink: 0; }
.app-header-menu__title {
  font-size: 16px; font-weight: var(--w-semi); color: var(--sh-fg, #fff);
  margin: 0; white-space: nowrap; letter-spacing: -.01em;
}
.app-header-menu__subtitle {
  font-size: 11.5px; color: var(--sh-fg-dim, rgba(255,255,255,.75));
  margin-top: 1px; white-space: nowrap;
}

.app-header-menu__spacer { flex: 1; min-width: 0; }
.app-header-menu__extra-controls { display: flex; align-items: center; gap: 10px; flex: none; }
.app-header-menu__search { display: flex; align-items: center; margin-right: 12px; position: relative; flex: none; }
/* Everything except the nav holds its size. Flex was shrinking the hamburger
   from 36px to 12px on a phone — three lines squeezed into a sliver — while
   the row still overflowed, so nothing was gained and the one control a phone
   user needs was the one that got eaten. The nav is the shrinkable element;
   that is what the fold is for. */
.app-header-menu__hamburger, .app-header-menu__logo-link,
.app-header-menu__dev, .app-header-menu__user { flex: none; }

/* ══════════════════════════════════════════════════════════════════════════
   TOP NAV — the horizontal bar. Real values from TopNavMenu.css.
   ══════════════════════════════════════════════════════════════════════════ */
.top-nav { display: flex; align-items: center; flex: 0 1 auto; min-width: 0; }
.top-nav__list { display: flex; align-items: center; gap: 4px; list-style: none; margin: 0; padding: 0; }
.top-nav__item { position: relative; }
.top-nav__link {
  display: flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: 6px;
  color: rgba(255, 255, 255, .85); text-decoration: none;
  font-family: inherit; font-size: 13px; font-weight: var(--w-med);
  background: transparent; border: none; cursor: pointer;
  transition: all .15s ease; white-space: nowrap;
}
.top-nav__link:hover { background: rgba(255, 255, 255, .1); color: #fff; text-decoration: none; }
.top-nav__link--active, .top-nav__link--open { background: rgba(255, 255, 255, .15); color: #fff; }
.top-nav__icon { flex: none; }
.top-nav__chevron { margin-left: 2px; transition: transform .2s ease; opacity: .7; }
.top-nav__link--open .top-nav__chevron { transform: rotate(180deg); }

.top-nav__dropdown {
  position: absolute; top: 100%; left: 0;
  width: max-content; min-width: 216px; max-width: 340px;
  margin-top: 4px; padding: 6px; background: #fff; border-radius: 8px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .15), 0 2px 10px rgba(0, 0, 0, .1);
  list-style: none; z-index: var(--z-navpanel);
  animation: dropdownFadeIn .15s ease;
  max-height: calc(100vh - var(--header-height, 60px) - 24px); overflow-y: auto;
}
@keyframes dropdownFadeIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.top-nav__section { list-style: none; }
.top-nav__section + .top-nav__section { margin-top: 2px; border-top: 1px solid #f1f5f9; padding-top: 4px; }
.top-nav__section-label {
  padding: 7px 12px 3px; font-size: 10.5px; font-weight: var(--w-bold);
  letter-spacing: .07em; text-transform: uppercase; color: #94a3b8; user-select: none;
}
.top-nav__section-items { list-style: none; margin: 0; padding: 0; }
.top-nav__dropdown-link {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 6px;
  color: #374151; text-decoration: none; font-size: 13px; font-weight: var(--w-med);
  transition: all .15s ease;
}
.top-nav__dropdown-link:hover { background: #f3f4f6; color: #111827; text-decoration: none; }
.top-nav__dropdown-link--active, .top-nav__dropdown-link--active:hover {
  position: relative; background: var(--nav-active-bg);
  color: var(--nav-active-fg); font-weight: var(--w-semi);
}
.top-nav__dropdown-link--active::before {
  content: ''; position: absolute; left: 0; top: 6px; bottom: 6px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--nav-active-rail);
}
.top-nav__dropdown-link .top-nav__icon { width: 20px; text-align: center; color: var(--ink-4); }
.top-nav__dropdown-link--active .top-nav__icon { color: var(--nav-active-fg); }
/* Sized to the text, NOT to the row: the badge belongs immediately after the
   word it counts, and the pin takes the slack after it. */
.top-nav__dropdown-label { flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; }
.top-nav__pin {
  flex: none; margin-left: auto; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-left: 6px; border-radius: 5px;
  color: #cbd5e1; opacity: 0; cursor: pointer;
}
.top-nav__dropdown-link:hover .top-nav__pin,
.top-nav__dropdown-link:focus-visible .top-nav__pin,
.top-nav__pin.is-on { opacity: 1; }
.top-nav__pin:hover { background: #f1f5f9; color: #64748b; }
.top-nav__pin.is-on { color: #f59e0b; }

/* A neighbour module's row. It is REAL — the group, the section and the page
   are what the live tree holds — and it is INERT, because this prototype does
   not ship that screen. A dead link is worse than an honest one. */
.top-nav__dropdown-link.is-elsewhere { color: var(--ink-4); cursor: default; }
.top-nav__dropdown-link.is-elsewhere:hover { background: var(--sunken); color: var(--ink-3); }
.top-nav__dropdown-note {
  padding: 8px 12px 4px; margin-top: 2px; border-top: 1px solid #f1f5f9;
  font-size: var(--t-micro); line-height: 1.45; color: var(--ink-4);
}
.top-nav__dropdown--more { left: auto; right: 0; min-width: 460px; max-width: min(720px, 90vw); columns: 2; column-gap: 18px; }
.top-nav__dropdown--more .top-nav__section { break-inside: avoid; page-break-inside: avoid; }

/* Live count on a nav row. Which queue has work in it is what decides where a
   buyer goes next, so it sits ON the link — 7px after the label, not floated
   to the row's edge where the eye has to travel to pair the two. */
.nav-badge {
  flex: none; min-width: 20px; margin-left: 7px; padding: 1px 6px; border-radius: var(--r-pill);
  font-size: 10.5px; font-weight: var(--w-bold); line-height: 1.5; text-align: center;
  font-variant-numeric: tabular-nums;
}
.nav-badge--open { background: #e0e7ff; color: #3730a3; }
.nav-badge--warn { background: #fef3c7; color: #92400e; }

/* ══════════════════════════════════════════════════════════════════════════
   PINNED CHIPS
   ══════════════════════════════════════════════════════════════════════════ */
.app-header-menu__favs {
  display: none; align-items: center; gap: 6px; margin-right: 10px;
  flex: 0 0 auto; overflow: hidden;
}
@media (min-width: 1280px) {
  .app-header-menu__favs { display: flex; }
  /* Collapse to zero width rather than display:none — an unrendered box
     reports a width of 0, the measurement then concludes the chips are free,
     shows them again, and the two states flip forever. */
  :root[data-nav-chips='off'] .app-header-menu__favs { max-width: 0; margin-right: 0; }
}
.app-header-menu__fav-wrap { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; }
.app-header-menu__fav {
  display: inline-flex; flex: 0 0 auto; align-items: center; gap: 5px; max-width: 132px;
  padding: 4px 9px; border-radius: var(--r-pill);
  background: var(--sh-chip-bg, rgba(255,255,255,.12)); color: var(--sh-fg, #fff);
  font-size: 12px; font-weight: var(--w-semi); text-decoration: none; white-space: nowrap;
}
.app-header-menu__fav span { overflow: hidden; text-overflow: ellipsis; }
.app-header-menu__fav:hover { background: var(--sh-hover, rgba(255,255,255,.2)); text-decoration: none; }
.app-header-menu__fav--more { flex: none; cursor: default; opacity: .85; }
.app-header-menu__fav-remove {
  position: absolute; right: 1px; display: grid; place-items: center;
  width: 17px; height: 17px; padding: 0; border: none; border-radius: 50%;
  background: var(--sh-fg, #fff); color: var(--sh-accent, #4f46e5);
  cursor: pointer; opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.app-header-menu__fav-wrap:hover .app-header-menu__fav-remove,
.app-header-menu__fav-remove:focus-visible { opacity: 1; pointer-events: auto; }
.app-header-menu__fav-wrap:hover .app-header-menu__fav { padding-right: 20px; }

/* ══════════════════════════════════════════════════════════════════════════
   HEADER CONTROLS — company scope, role, search trigger, user menu.
   The company switcher is NOT in a menu: ?company= scopes every register, the
   numbering preview and the statutory block, and the wrong company is the
   wrong legal document.
   ══════════════════════════════════════════════════════════════════════════ */
.app-co {
  display: inline-flex; align-items: center; gap: var(--s2); height: 34px;
  padding: 0 10px 0 12px; border: 1px solid var(--sh-search-border, rgba(255,255,255,.28));
  border-radius: var(--r-md); background: var(--sh-search-bg, rgba(255,255,255,.14));
  font-family: inherit; font-size: var(--t-meta); font-weight: var(--w-semi);
  color: var(--sh-fg, #fff); cursor: pointer; white-space: nowrap;
}
.app-co:hover { background: var(--sh-hover, rgba(255,255,255,.2)); }
.app-co .nm { font-weight: var(--w-med); opacity: .9; }
.app-co .ccy { font-weight: var(--w-med); font-family: var(--mono); font-size: .9em; opacity: .8; }
.app-co .chev { opacity: .7; display: inline-flex; }
.app-rolechip {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 10px;
  border-radius: var(--r-md); background: var(--sh-chip-bg, rgba(255,255,255,.12));
  border: 1px solid transparent; font-family: inherit; font-size: var(--t-meta);
  font-weight: var(--w-semi); color: var(--sh-fg, #fff); cursor: pointer;
}
.app-rolechip:hover { background: var(--sh-hover, rgba(255,255,255,.2)); }
.app-iconbtn {
  width: 30px; height: 30px; display: grid; place-items: center; border: 0;
  border-radius: var(--r-ctl); background: none; color: var(--sh-fg-dim, rgba(255,255,255,.82)); cursor: pointer;
}
.app-iconbtn:hover { background: var(--sh-hover, rgba(255,255,255,.2)); color: var(--sh-fg, #fff); }

/* ---- The acceptance strip and the simulation play control -------------------
   Two chrome controls, sitting in the extra-controls slot beside the company
   and role chips. The score is not decoration: green says every one of the 25
   checks passed ON THIS PAGE LOAD, and red carries the name of the first
   failure in its title so the answer is one hover away rather than one click.
   They follow .app-rolechip's shape so the row reads as one set of controls,
   and they take their colour from the SHELL tokens rather than the page
   palette — the header is a different surface in all three themes and a
   status hue picked for a white card is unreadable on the aurora gradient. */
.app-accept, .app-simplay {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; border-radius: var(--r-md); border: 1px solid transparent;
  background: var(--sh-chip-bg, rgba(255,255,255,.12)); color: var(--sh-fg, #fff);
  font-family: var(--mono); font-size: var(--t-micro); font-weight: var(--w-bold);
  letter-spacing: .02em; cursor: pointer; white-space: nowrap;
}
.app-accept { padding: 0 10px; max-width: 260px; overflow: hidden; text-overflow: ellipsis; }
.app-simplay { width: 30px; padding: 0; font-family: inherit; font-size: 11px; }
.app-accept:hover, .app-simplay:hover { background: var(--sh-hover, rgba(255,255,255,.2)); }
/* Green is quiet — a passing strip should not compete with the page. Red is
   not: a wrong demo is the thing this control exists to prevent. */
.app-accept.is-ok::before { content: '●'; color: var(--ok-dot); font-size: 9px; }
.app-accept.is-bad {
  background: var(--bad-fg); border-color: var(--bad-dot); color: #fff;
}
.app-accept.is-bad::before { content: '●'; color: #fff; font-size: 9px; }
@media (max-width: 1024px) { .app-accept { max-width: 74px; } }
@media (max-width: 768px)  { .app-accept, .app-simplay { display: none; } }

/* (.cd-search-header is gone with the header's compact search trigger — the
   hero bar in the content region is the search now. .cd-search-trigger stays:
   the quick print-labels button wears it.) */
.cd-search-trigger {
  width: 38px; height: 38px; display: grid; place-items: center;
  border: 1px solid var(--sh-search-border, rgba(255,255,255,.28)); border-radius: 10px;
  background: var(--sh-search-bg, rgba(255,255,255,.14)); color: var(--sh-fg, #fff);
  cursor: pointer; transition: background .15s ease;
}
.cd-search-trigger:hover, .cd-search-trigger.active { background: var(--sh-hover, rgba(255,255,255,.2)); }
.cd-search-pop {
  position: absolute; top: calc(100% + 10px); right: 0; width: min(560px, 92vw);
  z-index: var(--z-searchpop);
}

.app-header-menu__user { position: relative; }
.app-header-menu__user-btn {
  display: flex; align-items: center; gap: 8px; padding: 6px 12px; border: none;
  background: rgba(255, 255, 255, .1); border-radius: 8px; cursor: pointer;
  font-family: inherit; transition: background .2s ease;
}
.app-header-menu__user-btn:hover { background: rgba(255, 255, 255, .2); }
.app-header-menu__user-avatar {
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  background: rgba(255, 255, 255, .2); color: #fff;
  font-weight: var(--w-semi); font-size: 14px;
  display: flex; align-items: center; justify-content: center;
}
.app-header-menu__user-name {
  font-size: 13px; font-weight: var(--w-med); color: #fff;
  max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-header-menu__chevron { color: rgba(255, 255, 255, .7); flex: none; }

.app-header-menu__dropdown {
  position: absolute; top: 100%; right: 0; width: 260px; margin-top: 8px;
  background: #fff; border-radius: var(--r-surface);
  box-shadow: 0 10px 40px rgba(0, 0, 0, .15), 0 2px 10px rgba(0, 0, 0, .1);
  overflow-y: auto; overflow-x: hidden; max-height: calc(100vh - 72px);
  z-index: var(--z-navpanel); animation: dropdownSlideIn .2s ease; color: var(--ink);
}
@keyframes dropdownSlideIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.app-header-menu__dropdown-header { padding: 16px; background: #f9fafb; border-bottom: 1px solid #e5e7eb; }
.app-header-menu__dropdown-name  { display: block; font-size: 14px; font-weight: var(--w-semi); color: #111827; }
.app-header-menu__dropdown-email { display: block; font-size: 12px; color: #6b7280; margin-top: 2px; }
.app-header-menu__dropdown-role {
  display: inline-block; margin-top: 8px; padding: 3px 8px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff;
  font-size: 10px; font-weight: var(--w-semi); text-transform: uppercase; border-radius: 4px;
}
.app-header-menu__dropdown-divider { height: 1px; background: #e5e7eb; }
.app-header-menu__dropdown-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 12px 16px;
  border: none; background: transparent; color: #374151;
  font-family: inherit; font-size: 13px; font-weight: var(--w-med);
  text-decoration: none; cursor: pointer; text-align: left; transition: background .15s ease;
}
.app-header-menu__dropdown-item:hover { background: #f3f4f6; text-decoration: none; }
.app-header-menu__dropdown-item--danger { color: #dc2626; border-top: 1px solid #e5e7eb; }
.app-header-menu__dropdown-item--danger:hover { background: #fef2f2; }
.app-header-menu__dropdown-item-text {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-width: 0; flex: 1; line-height: 1.2; white-space: nowrap;
}
.app-header-menu__dropdown-item-value {
  font-family: var(--mono); font-size: 10.5px; font-weight: 400; color: #9ca3af;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.app-header-menu__appearance { padding: 8px 14px 10px; border-top: 1px solid #f1f5f9; }
.app-header-menu__appearance-label {
  display: block; font-size: 11px; font-weight: var(--w-semi); letter-spacing: .04em;
  text-transform: uppercase; color: #94a3b8; margin-bottom: 6px;
}
.app-header-menu__appearance-options { display: flex; gap: 4px; }
.app-header-menu__appearance-btn {
  flex: 1 1 0; padding: 6px 4px; font-family: inherit; font-size: 11.5px;
  font-weight: var(--w-semi); color: #475569; background: #f1f5f9;
  border: 1px solid transparent; border-radius: 6px; cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.app-header-menu__appearance-btn:hover { background: #e2e8f0; }
.app-header-menu__appearance-btn.is-on { background: #eef2ff; border-color: #c7d2fe; color: #4338ca; }
.app-header-menu__appearance-btn:focus-visible { outline: 2px solid #6366f1; outline-offset: 1px; }

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE SHEET — full-height left panel, not a 280px drawer with the page
   showing through. Picking a destination is a whole task.
   ══════════════════════════════════════════════════════════════════════════ */
.mobile-nav__overlay {
  display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, .5);
  z-index: var(--z-scrim); animation: fadeIn .2s ease;
}
.mobile-nav__overlay.is-open { display: block; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.mobile-nav {
  position: fixed; top: 0; left: 0; width: min(420px, 100vw); height: 100dvh;
  background: #fff; transform: translateX(-100%);
  transition: transform .3s cubic-bezier(.4, 0, .2, 1);
  z-index: var(--z-navpanel); display: flex; flex-direction: column; overflow: hidden;
}
.mobile-nav--open { transform: translateX(0); }
.mobile-nav__head {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; background: #fff; border-bottom: 1px solid #e2e8f0;
}
.mobile-nav__filter {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  padding: 0 10px; height: 42px; border-radius: 10px; background: #f1f5f9; color: #64748b;
}
.mobile-nav__filter input {
  flex: 1 1 auto; min-width: 0; border: none; background: none; outline: none;
  font: inherit; font-size: 15px; color: #0f172a;
}
.mobile-nav__filter input::-webkit-search-cancel-button { display: none; }
.mobile-nav__filter-clear, .mobile-nav__close, .mobile-nav__dock {
  flex: none; display: grid; place-items: center; width: 40px; height: 40px;
  border: none; border-radius: 10px; background: none; color: #64748b; cursor: pointer;
}
.mobile-nav__filter-clear { width: 26px; height: 26px; }
/* Rendered on every viewport, shown only where it means something — the sheet
   has nothing to collapse to. */
.mobile-nav__dock { display: none; }
.mobile-nav__close:hover, .mobile-nav__filter-clear:hover, .mobile-nav__dock:hover { background: #e2e8f0; }
.mobile-nav__content {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 12px;
  background: #f8fafc; display: flex; flex-direction: column; gap: 10px;
}
.mobile-nav__empty { margin: 24px 8px; color: #64748b; font-size: 14px; text-align: center; }
/* Cards keep their natural height; the COLUMN scrolls. Without flex:0 0 auto
   every card squashes to make the set fit and overflow:hidden clips it. */
.mobile-nav__card { flex: 0 0 auto; background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; }
.mobile-nav__card--pins { border-color: #fde68a; background: #fffbeb; }
.mobile-nav__card--pins .mobile-nav__group-header--static { color: #92400e; }
.mobile-nav__group-header {
  display: flex; align-items: center; gap: 12px; width: 100%; padding: 14px 16px;
  border-radius: 8px; background: transparent; border: none; color: #6b7280;
  font-family: inherit; font-size: 12px; font-weight: var(--w-semi);
  text-transform: uppercase; letter-spacing: .5px; cursor: pointer; transition: all .15s ease;
}
.mobile-nav__group-header:hover { background: #f9fafb; color: #374151; }
.mobile-nav__group-header--active { color: #667eea; }
.mobile-nav__group-header--static {
  display: flex; align-items: center; gap: 12px; padding: 13px 16px;
  font-size: 12px; font-weight: var(--w-bold); letter-spacing: .06em; text-transform: uppercase;
}
.mobile-nav__group-label { flex: 1 1 auto; min-width: 0; text-align: left; }
.mobile-nav__count {
  flex: none; min-width: 22px; padding: 2px 7px; border-radius: var(--r-pill);
  background: #f1f5f9; color: #64748b; font-size: 11px; font-weight: var(--w-bold); letter-spacing: 0;
}
.mobile-nav__icon { width: 24px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; }
.mobile-nav__chevron { margin-left: auto; transition: transform .22s ease; }
.mobile-nav__chevron--expanded { transform: rotate(180deg); }
/* grid-template-rows 0fr -> 1fr animates to the content's natural height with
   no JS measurement and no max-height guess that clips a long group. The body
   stays mounted so CLOSING animates too; visibility is delayed to the end so a
   collapsed link leaves the tab order instead of being merely clipped. */
.mobile-nav__group-body {
  display: grid; grid-template-rows: 0fr; visibility: hidden;
  transition: grid-template-rows .22s ease, visibility 0s linear .22s;
}
.mobile-nav__card.is-open > .mobile-nav__group-body {
  grid-template-rows: 1fr; visibility: visible;
  transition: grid-template-rows .22s ease, visibility 0s;
}
.mobile-nav__group-body > * { overflow: hidden; min-height: 0; }
.mobile-nav__group-items { min-height: 0; padding: 0 8px 8px; }
.mobile-nav__section-label {
  padding: 10px 12px 4px; font-size: 10.5px; font-weight: var(--w-bold);
  letter-spacing: .07em; text-transform: uppercase; color: #94a3b8;
}
.mobile-nav__item {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px; margin: 1px 0;
  border-radius: 8px; color: #374151; text-decoration: none;
  font-size: 14px; font-weight: var(--w-med); transition: all .15s ease;
}
.mobile-nav__item:hover { background: #f3f4f6; text-decoration: none; }
.mobile-nav__item--child { padding-left: 12px; gap: 10px; }
.mobile-nav__item--active, .mobile-nav__item--active:hover {
  position: relative; background: var(--nav-active-bg); color: var(--nav-active-fg); font-weight: var(--w-semi);
}
.mobile-nav__item--active::before {
  content: ''; position: absolute; left: 0; top: 6px; bottom: 6px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--nav-active-rail);
}
.mobile-nav__item-label { flex: 0 1 auto; min-width: 0; }
.mobile-nav__item.is-elsewhere { color: var(--ink-4); cursor: default; }
.mobile-nav__note { padding: 4px 12px 10px; font-size: var(--t-micro); color: var(--ink-4); }

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE DOCK — 4 tabs + Menu. Hides on scroll-down, returns on scroll-up.
   ══════════════════════════════════════════════════════════════════════════ */
.mt3-dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-dock);
  display: none; align-items: stretch;
  background: var(--sh-dock-bg, #fff); border-top: 1px solid var(--sh-dock-line, #e2e8f0);
  padding-bottom: env(safe-area-inset-bottom, 0); transition: transform .22s ease;
}
.mt3-dock--off { transform: translateY(calc(100% + env(safe-area-inset-bottom, 0px))); }
.mt3-dock__tab {
  flex: 1 1 0; min-width: 0; min-height: var(--h-dock);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: 6px 2px; background: none; border: none; font: inherit;
  color: var(--sh-dock-fg, #64748b); text-decoration: none; cursor: pointer;
}
.mt3-dock__tab:visited { color: var(--sh-dock-fg, #64748b); }
.mt3-dock__tab:hover { text-decoration: none; }
.mt3-dock__tab--active, .mt3-dock__tab--active:visited { color: var(--sh-dock-active, #667eea); }
.mt3-dock__tab:focus-visible { outline: 2px solid var(--sh-dock-active, #667eea); outline-offset: -3px; }
.mt3-dock__icon { flex: none; }
.mt3-dock__label {
  font-size: 10.5px; font-weight: var(--w-semi); line-height: 1.15;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════════════════
   FOOTER
   ══════════════════════════════════════════════════════════════════════════ */
.app-footer {
  background: #fff; padding: 1.25rem 2rem; box-shadow: 0 -2px 8px rgba(0, 0, 0, .06);
  text-align: center; margin-top: auto; border-top: 1px solid #e2e8f0;
  width: 100%; box-sizing: border-box; flex: none;
}
.app-footer__content { max-width: 1920px; margin: 0 auto; padding: 0 1rem; }
.app-footer__text { margin: 0; color: #718096; font-size: .875rem; font-weight: 400; line-height: 1.5; }

/* ══════════════════════════════════════════════════════════════════════════
   PAGE SHELL
   ══════════════════════════════════════════════════════════════════════════ */
/* The header is STICKY and in flow, so no padding-top compensates for it —
   that was the fixed header's tax and it left a 56px hole on every print. */
.app-main { flex: 1 0 auto; min-width: 0; }

/* Three widths, and the modifier is on the page rather than guessed per
   section. is-wide for registers — a 20-column grid should use the monitor it
   is given. is-narrow for single-column forms and prose — line length. */
.app-page {
  max-width: var(--w-page); margin: 0 auto;
  padding: var(--s5) var(--pad-page) calc(var(--h-actionbar) + var(--s7));
}
.app-page.is-wide   { max-width: none; }
.app-page.is-narrow { max-width: var(--w-narrow); }

/* ── AN ALIAS, NOT A FORK ──────────────────────────────────────────────────
   This module's twenty-eight pages were written before the chrome port and
   their body is `<main class="uv-main"><div id="app">` — which is live Pulse's
   `.app-main` + `.app-page`, split across two elements instead of one. Renaming
   them would be twenty-eight files edited while twenty-eight agents are writing
   them, so the two names are pointed at the rules above instead.

   IT IS AN ALIAS AND NOT A SECOND OPINION: nothing below sets a value the rule
   it aliases does not already set, so there is exactly one place to change a
   page width and one place to change a page's padding. When the pages do move
   to `.app-main` / `.app-page`, this block is deleted and not one other line
   changes. Every other rule in this file that names `.app-main` names
   `.uv-main` beside it for the same reason, marked "alias" at each site.

   `.uv-main.is-wide` is carried too: shell.css set it on the MAIN and one page
   already uses it there, so both spellings resolve to `.app-page.is-wide`.  */
.uv-main { flex: 1 0 auto; min-width: 0; }
.uv-main > #app {
  max-width: var(--w-page); margin: 0 auto;
  /* Bottom padding is --s7, NOT --s7 plus an action bar. procurement reserves
     the bar on every page because every procurement page has one; four pages
     here do (the two wizards, the planner, the imposition editor) and
     twenty-four do not, so twenty-four were carrying 64px of room for a
     control that never arrives. A page with a bar opts in below. */
  padding: var(--s5) var(--pad-page) var(--s7);
}
.uv-main.is-wide > #app,   .uv-main > #app.is-wide   { max-width: none; }
/* Opt-in for the four pages that end in a sticky action bar: the bar is
   position:sticky with a negative bottom margin, so the page has to hold room
   for it or the last row sits under it. */
.uv-main.has-actionbar > #app, .app-page.has-actionbar {
  padding-bottom: calc(var(--h-actionbar) + var(--s7));
}
.uv-main.is-narrow > #app, .uv-main > #app.is-narrow { max-width: var(--w-narrow); }

/* ── LIVE PULSE'S TWO UNSTYLED HOOKS ───────────────────────────────────────
   TopNavMenu.jsx emits both of these and NEITHER its own TopNavMenu.css nor
   this file styles either one: they are semantic hooks, not looks. They are
   restated here at the value their context already gives them — the
   positioning context is on `.top-nav__item`, and a wrapper div is a block —
   so that the check "every class shell.js emits has a rule" finds one, and so
   that nobody "fixes" the absence by inventing a style the host app does not
   have. Adding a declaration to either of these is a FORK, not a fix. */
.top-nav__item--has-dropdown { position: relative; }
.mobile-nav__section { display: block; }

/* ── THE 48px FLOOR, WHICH SURVIVES THE PORT ───────────────────────────────
   The one place this module's own rule is allowed to reach the chrome. These
   screens are read at arm's length, standing, beside a flatbed press, usually
   with gloves on: tokens.css:275 makes --tap (48px) a FLOOR for every
   interactive target, not a preference, and uv.css applies it to every control
   the module draws. A shell that is the single exception to the rule every page
   is audited against would be a worse divergence than the four declarations
   below, so the floor is applied here and stated rather than left implied.

   It changes HEIGHT and nothing else — no colour, no type, no spacing — so the
   chrome still ports back as itself, and in Pulse (where 48px is not a rule)
   deleting this block is the whole of the difference.

   NOT covered, deliberately: `.mobile-nav__filter-clear`, which is a 26px glyph
   INSIDE the 48px filter row it belongs to. Growing it would eat the field it
   clears, and it is a redundant affordance — the field is `type="search"` and
   selecting-and-deleting does the same job from the keyboard. */
.app-header-menu__hamburger,
.app-header-menu__user-btn,
.cd-search-trigger,
.mobile-nav__close,
.mobile-nav__dock,
.top-nav__link,
.top-nav__dropdown-link,
.app-header-menu__dropdown-item,
.app-header-menu__appearance-btn,
.mobile-nav__group-header,
/* The filter row was measured at 42px on every one of the 28 pages — the one
   control in the panel a gloved finger reaches for first, and the only member
   of this block that had been left out of it. Its 26px clear button stays out
   deliberately, for the reason above. */
.mobile-nav__filter,
.mobile-nav__item { min-height: var(--tap); }
.app-header-menu__hamburger,
.cd-search-trigger,
.mobile-nav__close,
.mobile-nav__dock { min-width: var(--tap); }
/* Measured at 110 x 46 — two pixels under, and it is the way home from every
   page. Height only; the wordmark's own padding is untouched. */
.app-header-menu__logo-link { min-height: var(--tap); }

/* ══════════════════════════════════════════════════════════════════════════
   THE DOCKED PANEL — the SAME element as the sheet above, docked.

   The top-nav is a faithful port and it is right for moving BETWEEN modules.
   It is wrong as the only way to move WITHIN one: twenty-nine procurement
   pages behind a single hover dropdown is a wall, and the shape of the module
   is invisible until you open it. The answer is BOTH, so nothing below is a
   new component — every rule here re-poses the sheet that shell.js already
   renders at every viewport. One markup path, one NAV literal, one filter, and
   no second thing to keep in step.

   769 is the far side of the 768 breakpoint at the foot of this file and is
   written HERE and only here. shell.js never learns the number: it writes
   data-nav-dock on <html> and this block decides whether that means anything.
   Below 768 not one declaration applies and the sheet is exactly what it was.

   It sits HERE, after PAGE SHELL rather than beside the sheet it re-poses,
   because it overrides .app-main and .app-footer at the same specificity they
   are declared with — above them it loses the cascade and the footer keeps its
   width:100%, which is 100% PLUS the margin and scrolls the page sideways.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 769px) {
  /* One width, read by the panel AND by the content it displaces, so the two
     can never disagree about how much room the chrome is taking. */
  :root                        { --w-dock: var(--w-side); }
  :root[data-nav-dock="rail"]  { --w-dock: var(--w-side-rail); }

  .mobile-nav {
    /* Under the header, not over it: the header is sticky and in flow, so
       offsetting by its height keeps the panel's top edge on the header's
       bottom edge at every scroll position without a second listener. */
    top: var(--h-header); bottom: 0; height: auto;
    width: var(--w-dock); transform: none;
    /* Below the dropdowns, deliberately — see --z-sidepanel in tokens.css. */
    z-index: var(--z-sidepanel);
    border-right: 1px solid #e2e8f0;
    transition: width var(--d3) var(--ease);
  }
  /* The scrim belongs to the overlay. A resize with the sheet left open must
     not strand a black sheet over a docked panel. */
  .mobile-nav__overlay, .mobile-nav__overlay.is-open { display: none; }
  .mobile-nav__close { display: none; }
  .mobile-nav__dock  { display: grid; }

  /* Content moves OVER, never under: a register that starts 264px in is a
     register you can read all of. The footer moves with it or it centres its
     line under the panel. Transitioned with the panel so the collapse is one
     movement rather than two. (.uv-main is the alias — see PAGE SHELL.) */
  .app-main, .uv-main, .app-footer {
    margin-left: var(--w-dock);
    transition: margin-left var(--d3) var(--ease);
  }
  /* width:100% + a margin is 100% + 264px of document, and the whole page
     scrolls sideways. */
  .app-footer { width: auto; }

  /* The two things you steer with stay put while twenty-nine rows scroll under
     them. The head is already outside the scroller; Pinned is inside it and
     has to be told. */
  .mobile-nav__card--pins { position: sticky; top: 0; z-index: 1; }

  /* ---- THE RAIL ---------------------------------------------------------
     The answer to a 20-column register losing 264px is to COLLAPSE, not to
     cap: the register gets the whole monitor back and every destination is
     still one click away. Labels go, icons stay, and shell.js moves the label
     into the row's title so an icon column is not a memory test. */
  :root[data-nav-dock="rail"] .mobile-nav__filter,
  :root[data-nav-dock="rail"] .mobile-nav__group-label,
  :root[data-nav-dock="rail"] .mobile-nav__count,
  :root[data-nav-dock="rail"] .mobile-nav__chevron,
  :root[data-nav-dock="rail"] .mobile-nav__section-label,
  :root[data-nav-dock="rail"] .mobile-nav__item-label,
  :root[data-nav-dock="rail"] .mobile-nav__note { display: none; }
  :root[data-nav-dock="rail"] .mobile-nav__head { padding: 10px 0; justify-content: center; }
  :root[data-nav-dock="rail"] .mobile-nav__content { padding: 8px 5px; gap: 6px; }
  :root[data-nav-dock="rail"] .mobile-nav__group-items { padding: 0 4px 4px; }
  :root[data-nav-dock="rail"] .mobile-nav__group-header,
  :root[data-nav-dock="rail"] .mobile-nav__group-header--static,
  :root[data-nav-dock="rail"] .mobile-nav__item {
    position: relative; padding: 10px 0; gap: 0; justify-content: center;
  }
  /* The count is the only thing in the panel that says "there is work here"
     without being asked, so it survives the collapse as a dot rather than
     being dropped. The number is in the title with the label. */
  :root[data-nav-dock="rail"] .nav-badge {
    position: absolute; top: 5px; right: 7px; z-index: 1;
    width: 7px; height: 7px; min-width: 0; margin: 0; padding: 0;
    border-radius: var(--r-pill); overflow: hidden; text-indent: -99px;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   BUTTONS — same .cd-* names, new values.
   Solid accent, no gradient, and NO translateY on hover: a register toolbar
   where every button lifts 1px is the fidgetiest thing in the app.
   ══════════════════════════════════════════════════════════════════════════ */
.cd-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: var(--h-ctl); padding: 0 14px; border: 1px solid transparent;
  border-radius: var(--r-ctl); font-family: inherit; font-size: var(--t-cell);
  font-weight: var(--w-semi); line-height: 1; white-space: nowrap; cursor: pointer;
  text-decoration: none; transition: background var(--d1), border-color var(--d1), color var(--d1);
}
a.cd-btn, a.cd-btn:hover { text-decoration: none; }
.cd-btn svg { width: 15px; height: 15px; flex: none; }
.cd-btn-sm { height: var(--h-ctl-sm); padding: 0 10px; font-size: var(--t-meta); }
.cd-btn-sm svg { width: 14px; height: 14px; }
.cd-btn-lg { height: var(--h-ctl-lg); padding: 0 18px; font-size: var(--t-body); }
/* THE PRIMARY WEARS THE BRAND GRADIENT, as live Pulse's nordic.css does.
   procurement retires it and fills flat with the accent; this module followed
   that for half a day and then took the user's instruction that the colours
   match production. So: Pulse's fill, and NOT Pulse's `transform:translateY(-1px)`
   on hover — a register toolbar where every button lifts a pixel is the
   fidgetiest thing in the app, and that is a motion decision rather than a
   colour one. Hover and press darken instead. */
.cd-btn-primary { background: var(--gradient); color: #fff; }
.cd-btn-primary:hover:not(:disabled) { background: var(--gradient); filter: brightness(.94); }
.cd-btn-primary:active:not(:disabled) { background: var(--gradient); filter: brightness(.88); }
.cd-btn-secondary { background: var(--surface); color: var(--ink-2); border-color: var(--line-2); }
.cd-btn-secondary:hover:not(:disabled) { background: var(--sunken); border-color: var(--line-3); color: var(--ink); }
.cd-btn-ghost { background: transparent; color: var(--ink-3); }
.cd-btn-ghost:hover:not(:disabled) { background: var(--mute-bg); color: var(--ink); }
.cd-btn-danger { background: var(--bad-dot); color: #fff; }
.cd-btn-danger:hover:not(:disabled) { background: var(--bad-fg); }
.cd-btn:disabled { opacity: .45; cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════════════════
   BADGES — a dot and a label, not a 999px uppercase letter-spaced pill.
   Uppercase is a no-op on 中文 and tracking on a CJK-bearing label looks like
   a rendering bug, so both are retired.
   ══════════════════════════════════════════════════════════════════════════ */
.cd-badge {
  display: inline-flex; align-items: center; gap: 6px; min-height: 20px; height: auto;
  padding: 2px 8px; border-radius: var(--r-badge); border: 1px solid transparent;
  font-size: var(--t-micro); font-weight: var(--w-semi); line-height: 1.45; white-space: nowrap;
}
.cd-badge::before { content: ''; flex: none; width: 6px; height: 6px; border-radius: 2px; background: currentColor; opacity: .85; }
/* QUIET is the default inside a table: no fill, no border, just the dot and the
   label in ink. Twenty filled chips in a register is a paintbox; the dot
   carries the same hue and the eye still scans the column in one pass. */
.cd-badge.is-quiet { background: none !important; border-color: transparent !important; color: var(--ink) !important; padding-left: 0; }
.cd-badge.is-quiet::before { opacity: 1; }
.cd-badge-success { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--ok-fg);   }
.cd-badge-info    { background: var(--info-bg); border-color: var(--info-line); color: var(--info-fg); }
.cd-badge-cyan    { background: var(--prog-bg); border-color: var(--prog-line); color: var(--prog-fg); }
.cd-badge-warning { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn-fg); }
.cd-badge-error   { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad-fg);  }
.cd-badge-gray    { background: var(--mute-bg); border-color: var(--mute-line); color: var(--mute-fg); }
.cd-badge-purple  { background: var(--odd-bg);  border-color: var(--odd-line);  color: var(--odd-fg);  }
.cd-badge.is-quiet.cd-badge-success::before { background: var(--ok-dot);   }
.cd-badge.is-quiet.cd-badge-info::before    { background: var(--info-dot); }
.cd-badge.is-quiet.cd-badge-cyan::before    { background: var(--prog-dot); }
.cd-badge.is-quiet.cd-badge-warning::before { background: var(--warn-dot); }
.cd-badge.is-quiet.cd-badge-error::before   { background: var(--bad-dot);  }
.cd-badge.is-quiet.cd-badge-gray::before    { background: var(--mute-dot); }
.cd-badge.is-quiet.cd-badge-purple::before  { background: var(--odd-dot);  }
/* ENDED, not FAILED. A cancelled PO is dead, not wrong: the label goes muted
   and the dot stays red. This is what stops the register looking like an alarm. */
.cd-badge.is-ended { color: var(--ink-4) !important; }
/* Terminal-and-good gets a lock, not a colour: billed is over, not "greener". */
.cd-badge.is-locked::after { content: '· locked'; color: var(--ink-4); font-weight: var(--w-med); }

/* ══════════════════════════════════════════════════════════════════════════
   TABLE
   ══════════════════════════════════════════════════════════════════════════ */
.cd-table-container {
  position: relative; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-surface);
  overflow: auto; overscroll-behavior: contain;
  /* Both axes deliberately. overflow-x:auto computes overflow-y to auto
     anyway, which makes this the scroll container — so position:sticky on the
     thead sticks to the container, and does nothing unless the container has a
     bounded height. fitGrid() in core.js sets --h-grid; this is the fallback. */
  max-height: var(--h-grid, calc(100vh - 360px)); min-height: 280px;
}
.cd-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--t-cell); }

/* Header: 12px, sentence case, 600, --ink-3. Pulse's 11px uppercase 0.6px-
   tracked header is retired (see the CJK note at the top). Separation now
   comes from the sunken ground, the rule and the weight, which is enough. */
.cd-table thead th {
  position: sticky; top: 0; z-index: var(--z-thead);
  height: var(--h-row-head); padding: 10px 14px; text-align: left;
  background: var(--sunken); color: var(--ink-3);
  font-size: var(--t-label); font-weight: var(--w-semi); line-height: 1.35;
  white-space: nowrap; user-select: none; border-bottom: 1px solid var(--line);
}
.cd-table tbody td {
  height: var(--h-row); padding: 12px 14px; vertical-align: middle;
  color: var(--ink); border-bottom: 1px solid var(--line); line-height: var(--lh-cell);
}
.cd-table tbody tr:last-child td { border-bottom: 0; }
.cd-table tbody tr:hover > td { background: var(--row-hover); }
.cd-table tbody tr.is-sel > td { background: var(--row-sel); }
.cd-table tbody tr.is-sel > td:first-child { box-shadow: inset 2px 0 0 0 var(--accent); }
/* No zebra. Zebra + 20 columns + status colour is soup; hairlines and hover win. */
.cd-table th.is-num, .cd-table td.is-num { text-align: right; }
.cd-table td.is-num { font-variant-numeric: tabular-nums; }
/* A vertical rule on every column is a spreadsheet; on the column that starts
   a group (the money block, the India block) it is structure. */
.cd-table th.is-groupstart, .cd-table td.is-groupstart { border-left: 1px solid var(--line); }
.cd-table tbody tr.is-bi td { height: var(--h-row-bi); }
/* BOM children under parent_line_id: indented, muted, and they do not enter
   the totals — the row says so rather than a footnote. */
.cd-table tbody tr.is-child td { color: var(--ink-3); background: var(--sunken); }
.cd-table tbody tr.is-child td:first-child { padding-left: 32px; background: var(--sunken); }

/* (A `.cd-th-sort` button block sat here describing a sortable-header markup
   cdTable does not emit: core.js sorts on a click handler on the bare <th>
   and marks the active column with `.uv-sort` (uv.css), and nothing sets
   aria-sort. Six dead rules — not nordic names either — deleted 30 Aug.
   CROSS-LAYER NOTE for core.js's owner: a real <button> in the <th> with
   aria-sort would be the accessible version these rules were written for.) */

/* Sticky first column. The identifier must never scroll away in an 18-column
   grid or you lose which row you are reading. It carries its own opaque
   ground, so the hover rule has to repaint it explicitly. */
.cd-table th:first-child, .cd-table td:first-child {
  position: sticky; left: 0; z-index: var(--z-stickycol); background: var(--surface);
}
.cd-table thead th:first-child { z-index: calc(var(--z-thead) + 1); background: var(--sunken); }
.cd-table tbody tr:hover > td:first-child { background: var(--row-hover); }
.cd-table tbody tr.is-sel > td:first-child { background: var(--row-sel); }
/* Edge shadow only once the grid has actually been scrolled, or every register
   carries a permanent smudge down its left third. */
.cd-table-container.is-scrolled td:first-child,
.cd-table-container.is-scrolled th:first-child { box-shadow: var(--sh-col); }

.cd-table-toolbar { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); flex-wrap: wrap; }
.cd-table-toolbar-info { font-size: var(--t-meta); color: var(--ink-3); }
.cd-table-toolbar-info strong { color: var(--ink); font-weight: var(--w-semi); }
.cd-table-toolbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

.cd-pagination-bar {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: 14px 2px 0; font-size: var(--t-meta); color: var(--ink-3);
}
.cd-pagination-bar .spacer { margin-left: auto; }

/* THE PAGER'S OWN NAMES, WHICH WERE MISSING. core.js's cdTable emits
   `.cd-pagination-info`, `.cd-pagination-controls`, `.cd-pagination-nav`,
   `.cd-pagination-btn` and `.cd-pagination-dots` — nordic.css:194-210 defines
   all five and this file had only the bar and `.cd-page-btn`, a name nothing
   emits. So every paginated table in the module rendered its prev/next as bare
   21px browser buttons: unstyled, and under the touch floor on a page the floor
   was written for. Re-implemented here at tokens.css values, same names, same
   shape, with nordic's 34px raised to --tap for the reason the block above
   gives. Height is the only figure that differs from nordic's. */
.cd-pagination-info { font-size: var(--t-meta); color: var(--ink-3);
  display: flex; align-items: center; gap: 6px; }
.cd-pagination-info strong { color: var(--ink); font-weight: var(--w-bold); }
.cd-pagination-controls { display: flex; align-items: center; gap: var(--s4); }
.cd-pagination-nav { display: flex; gap: 3px; align-items: center; }
.cd-pagination-btn {
  min-width: var(--tap); min-height: var(--tap); border: 1px solid var(--line-2);
  border-radius: var(--r-ctl); background: var(--surface); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-body); font-weight: var(--w-semi); color: var(--ink-2);
  font-family: inherit; transition: all .15s;
}
.cd-pagination-btn:hover:not(:disabled):not(.active) {
  background: var(--sunken); border-color: var(--line-strong); color: var(--ink); }
.cd-pagination-btn.active {
  background: var(--accent); color: #fff; border-color: var(--accent); }
.cd-pagination-btn:disabled { opacity: .3; cursor: not-allowed; }
.cd-pagination-dots {
  min-width: var(--tap); min-height: var(--tap); display: flex; align-items: center;
  justify-content: center; font-size: var(--t-meta); color: var(--muted);
  font-variant-numeric: tabular-nums; }
/* (.cd-page-btn — the name the comment above already convicts: nothing emits
   it and it is not a nordic name. Its four rules were deleted 30 Aug; the
   real pager is the .cd-pagination-* set above.) */

/* ══════════════════════════════════════════════════════════════════════════
   TABS
   ══════════════════════════════════════════════════════════════════════════ */
.cd-tabs {
  display: flex; align-items: center; gap: var(--s1);
  border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none;
}
.cd-tabs::-webkit-scrollbar { display: none; }
.cd-tab {
  position: relative; display: inline-flex; align-items: center; gap: 7px;
  height: 38px; padding: 0 12px; border: 0; background: none; cursor: pointer;
  font-family: inherit; font-size: var(--t-label); font-weight: var(--w-semi);
  color: var(--ink-3); white-space: nowrap; text-decoration: none;
}
.cd-tab:hover { color: var(--ink); text-decoration: none; }
.cd-tab.active { color: var(--accent); box-shadow: inset 0 -2px 0 0 var(--accent); }
.cd-tab .n {
  min-width: 18px; height: 17px; padding: 0 5px; border-radius: var(--r-pill);
  background: var(--mute-bg); color: var(--ink-3);
  font-size: 10.5px; font-weight: var(--w-semi); display: grid; place-items: center;
  font-variant-numeric: tabular-nums;
}
.cd-tab.active .n { background: var(--accent-tint); color: var(--accent); }

/* ══════════════════════════════════════════════════════════════════════════
   FORM CONTROLS
   ══════════════════════════════════════════════════════════════════════════ */
.cd-form-group { display: flex; flex-direction: column; margin: 0; min-width: 0; }
.cd-form-label {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 6px;
  min-height: 34px; margin-bottom: 6px;   /* two label lines reserved, so
     controls across a row land at the same height and the form is not ragged */
  font-size: var(--t-label); font-weight: var(--w-semi); color: var(--ink-2);
}
.cd-form-label .req { color: var(--bad-dot); font-weight: var(--w-bold); }
.cd-form-label .prov { font-size: var(--t-micro); font-weight: var(--w-reg); color: var(--ink-4); }
.cd-form-input, .cd-form-select, .cd-form-textarea {
  width: 100%; height: var(--h-ctl); padding: 0 12px;
  border: 1px solid var(--line-2); border-radius: var(--r-ctl);
  background: var(--surface); color: var(--ink);
  font-family: inherit; font-size: var(--t-body); line-height: var(--lh-body);
  outline: none; transition: border-color var(--d1), box-shadow var(--d1);
}
.cd-form-select { padding-right: 30px; appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-4) 50%),
                    linear-gradient(135deg, var(--ink-4) 50%, transparent 50%);
  background-position: calc(100% - 15px) 15px, calc(100% - 11px) 15px;
  background-size: 4px 4px, 4px 4px; background-repeat: no-repeat; }
.cd-form-textarea { height: auto; min-height: 84px; padding: 9px 12px; resize: vertical; line-height: var(--lh-prose); }
.cd-form-input:hover, .cd-form-select:hover { border-color: var(--line-3); }
.cd-form-input:focus, .cd-form-select:focus, .cd-form-textarea:focus {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring);
}
.cd-form-input::placeholder { color: var(--ink-4); }
.cd-form-input:disabled, .cd-form-select:disabled, .cd-form-textarea:disabled {
  background: var(--mute-bg); color: var(--ink-4); cursor: not-allowed;
}
/* --ink-4 is 2.56:1 on white. A form hint is the sentence that stops somebody
   filling the field in wrong; it is read, so it clears 4.5:1. */
.cd-form-hint { margin-top: 6px; font-size: var(--t-label); color: var(--ink-3); line-height: var(--lh-body); }
/* The error REPLACES the hint. Never both — a field with two grey lines under
   it and one red line under that is where people stop reading. */
.cd-form-group.is-err .cd-form-input,
.cd-form-group.is-err .cd-form-select { border-color: var(--bad-dot); box-shadow: 0 0 0 3px var(--bad-ring); }
.cd-form-group.is-err .cd-form-hint { display: none; }
.cd-form-err { margin-top: 6px; font-size: var(--t-label); font-weight: var(--w-med); color: var(--bad-fg); }

/* ══════════════════════════════════════════════════════════════════════════
   EMPTY / LOADING / ERROR
   ══════════════════════════════════════════════════════════════════════════ */
.cd-empty-state { padding: var(--s9) var(--s5); text-align: center; }
.cd-empty-state-icon { color: var(--ink-5); margin-bottom: var(--s3); }
.cd-empty-state-icon svg { width: 26px; height: 26px; }
.cd-empty-state-title { font-size: var(--t-node); font-weight: var(--w-semi); color: var(--ink); margin-bottom: 6px; }
.cd-empty-state-desc { font-size: var(--t-cell); color: var(--ink-3); max-width: 52ch; margin: 0 auto var(--s4); line-height: var(--lh-prose); }

/* Skeleton rows at the real row height, so the page does not jump 350px when
   the data lands. Never the word "Loading…".
   KEPT WHILE UNUSED, deliberately: the prototype's data is local so nothing
   emits .cd-skel or .is-refreshing yet, but rule 9 makes loading states part
   of every page and the real app fetches — these are the library's loading
   affordances, not dead weight. (Same holds for .cd-form-err / .is-err below:
   the wizards will emit them the day validation is wired.) */
.cd-skel {
  display: block; height: 12px; border-radius: 4px;
  background: linear-gradient(90deg, #eef0f4 0%, #f7f8fa 50%, #eef0f4 100%);
  background-size: 200% 100%; animation: cd-shim 1.15s linear infinite;
}
@keyframes cd-shim { from { background-position: 200% 0; } to { background-position: -200% 0; } }
/* Refreshing WITH data: keep the rows, dim them, one 2px indeterminate bar. */
.cd-table-container.is-refreshing tbody { opacity: .45; pointer-events: none; }
.cd-table-container.is-refreshing::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 2px;
  z-index: calc(var(--z-thead) + 2);
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  background-size: 40% 100%; animation: cd-bar 1s linear infinite;
}
@keyframes cd-bar { from { background-position: -40% 0; } to { background-position: 140% 0; } }

/* ══════════════════════════════════════════════════════════════════════════
   TOAST — a white surface with a 3px tone rail, not a solid saturated fill.
   White text on #ef4444 at 13px is the least readable thing in the live
   system, and the error toast is the one you most need to read.
   ══════════════════════════════════════════════════════════════════════════ */
.cd-toast-container { position: fixed; right: var(--s6); bottom: var(--s6); z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--s2); }
.cd-toast {
  display: flex; align-items: flex-start; gap: 10px;
  width: 360px; max-width: calc(100vw - var(--s8)); padding: 12px 14px;
  border-radius: var(--r-md); background: var(--surface);
  border: 1px solid var(--line); border-left: 3px solid var(--ink-5);
  box-shadow: var(--sh-pop); font-size: var(--t-cell); line-height: var(--lh-body);
  color: var(--ink); animation: cd-toast var(--d3) var(--ease);
}
.cd-toast-success { border-left-color: var(--ok-dot);   }
.cd-toast-error   { border-left-color: var(--bad-dot);  }
.cd-toast-warning { border-left-color: var(--warn-dot); }
.cd-toast-info    { border-left-color: var(--accent);   }
.cd-toast-msg { flex: 1; min-width: 0; }
.cd-toast-close { all: unset; cursor: pointer; color: var(--ink-4); flex: none; padding: 2px; border-radius: 4px; }
.cd-toast-close:hover { color: var(--ink); background: var(--mute-bg); }
@keyframes cd-toast { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ══════════════════════════════════════════════════════════════════════════
   MODAL — confirmations and short pickers only. Forms are pages.
   ══════════════════════════════════════════════════════════════════════════ */
.cd-modal-backdrop {
  display: none; position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--scrim); backdrop-filter: blur(2px);
  align-items: center; justify-content: center; padding: var(--s6);
}
.cd-modal-backdrop.visible { display: flex; }
.cd-modal {
  display: flex; flex-direction: column; width: 100%; max-height: 88vh;
  background: var(--surface); border-radius: var(--r-modal);
  box-shadow: var(--sh-modal); overflow: hidden; animation: cd-modal var(--d2) var(--ease);
}
.cd-modal-sm { max-width: 440px; } .cd-modal-md { max-width: 560px; }
.cd-modal-lg { max-width: 760px; } .cd-modal-xl { max-width: 960px; }
.cd-modal-header { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line); }
.cd-modal-header h3 { margin: 0; font-size: var(--t-node); font-weight: var(--w-semi); }
/* THE CLOSE BUTTON'S OWN NAME, WHICH WAS MISSING — the same story as the
   pagination block above. core.js's cdModal emits `.cd-modal-close` in every
   modal header and nordic.css:176 styles it; this file had no rule at all, so
   every modal's ✕ rendered as a bare browser button. Same name, token values,
   nordic's 4px-padded glyph raised to the --tap floor (negative margin keeps
   the header's optical height; the TARGET grows, the box does not). */
.cd-modal-close {
  display: grid; place-items: center; min-width: var(--tap); min-height: var(--tap);
  margin: calc(var(--s3) * -1) calc(var(--s3) * -1) calc(var(--s3) * -1) 0;
  border: none; background: none; color: var(--ink-4); cursor: pointer;
  border-radius: var(--r-ctl); flex: none;
}
.cd-modal-close:hover { color: var(--ink); background: var(--mute-bg); }
.cd-modal-body { flex: 1; overflow-y: auto; padding: var(--s5); font-size: var(--t-body); line-height: var(--lh-prose); }
.cd-modal-footer { display: flex; justify-content: flex-end; gap: var(--s2); padding: 14px var(--s5); border-top: 1px solid var(--line); background: var(--sunken); }
.cd-modal-footer .note { margin-right: auto; font-size: var(--t-label); color: var(--ink-3); }
@keyframes cd-modal { from { opacity: 0; transform: translateY(8px) scale(.985); } to { opacity: 1; transform: none; } }

/* ══════════════════════════════════════════════════════════════════════════
   HERO SEARCH — live Pulse's GlobalSearch, verbatim.
   (The ⌘K command palette that stood here is retired: the hero bar on every
   page is the one search now, and two searches with one keystroke between
   them is how Enter stops being trusted.)

   Every class below is live's own `.cd-search-*` / `.cd-suggest-*` /
   `.cd-saved-*` vocabulary at live's own literals, copied from
   pulse-mt3narada/…/dashboard/dashboard.css:77-247 (bar, suggest, saved
   dropdown), :599-615 (the 820/640 steps) and :666-698 (the launcher's
   main/sub/kind rows). Same deliberate exception to §10 as the chrome block
   at the top of this file: the literals ARE the point — #667eea here is
   --primary there, #94a3b8 is --gray-400 (their custom properties are scoped
   under .cd-dash, which this module does not ship, so the resolved values are
   inlined and named). THREE integration rules are ours and are marked at
   their sites: the z-index goes through --z-searchpop instead of live's bare
   400 (this module's layering contract, tokens.css), rows are real ANCHORS so
   middle-click works (live uses divs + JS nav), and interactive rows carry
   the module's 48px tap floor.
   ══════════════════════════════════════════════════════════════════════════ */
.cd-search-wrap { position: relative; margin-bottom: 18px; }
.cd-search {
  background: white; border: 1px solid #e2e8f0;
  border-radius: 14px; box-shadow: 0 2px 10px rgba(0,0,0,0.06);
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px; height: 56px;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.cd-search.focused { border-color: #667eea; box-shadow: 0 4px 24px rgba(102,126,234,0.18); }
.cd-search-icon { color: #94a3b8; flex-shrink: 0; display: flex; transition: color 0.2s; }
.cd-search.focused .cd-search-icon { color: #667eea; }
.cd-search-icon svg { width: 18px; height: 18px; }
.cd-search-input {
  flex: 1; border: none; outline: none; background: transparent;
  font-size: 15px; font-weight: 500; color: #1e293b;
  font-family: inherit; min-width: 0;
}
.cd-search-input::placeholder { color: #94a3b8; font-weight: 400; }
/* Ours: opt out of the module's inner focus ring (uv.css's global
   input:focus-visible). Focus stays visible — .cd-search.focused already
   draws the field's ring, live's own signal (indigo border + 24px halo) —
   and two rings around one box read as a rendering bug, not emphasis. */
.cd-search .cd-search-input:focus-visible { outline: none; }
.cd-search-clear {
  display: none; background: none; border: none; cursor: pointer;
  color: #94a3b8; padding: 4px; border-radius: 6px;
}
.cd-search-clear:hover { background: #f1f5f9; color: #475569; }
.cd-search-clear.visible { display: flex; }
/* OUR ONE ADDITION to the field: save-this-search. Live saves searches on its
   Orders page, which this module does not have, so the save affordance lives
   in the field — dressed exactly as the clear button beside it, tinting
   primary on hover because it KEEPS rather than discards. */
.cd-search-save {
  display: none; background: none; border: none; cursor: pointer;
  color: #94a3b8; padding: 4px; border-radius: 6px;
}
.cd-search-save:hover { background: #f1f5f9; color: #667eea; }
.cd-search-save.visible { display: flex; }
.cd-saved-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid #e2e8f0; border-radius: 8px;
  background: #f8fafc; color: #475569;
  padding: 7px 12px; font-size: 12.5px; font-weight: 600;
  cursor: pointer; font-family: inherit; white-space: nowrap;
  transition: all 0.15s; flex-shrink: 0;
}
.cd-saved-btn:hover { border-color: #667eea; color: #667eea; background: rgba(102,126,234,0.05); }
.cd-saved-btn svg { width: 14px; height: 14px; }
.cd-saved-count {
  background: #667eea; color: white; border-radius: 9px;
  font-size: 10px; font-weight: 700; min-width: 17px; height: 17px;
  line-height: 17px; text-align: center; padding: 0 5px;
}

/* Suggestions dropdown. z-index through --z-searchpop (ours — live says a
   bare 400 inside its scoped dashboard; this module names every layer). */
.cd-suggest {
  display: none; position: absolute; top: calc(100% + 6px); left: 0; right: 0;
  background: white; border-radius: 12px; border: 1px solid #e2e8f0;
  box-shadow: 0 16px 48px rgba(0,0,0,0.16);
  z-index: var(--z-searchpop); max-height: 460px; overflow-y: auto;
}
.cd-suggest.visible { display: block; }
.cd-suggest-group {
  padding: 8px 16px 5px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.7px;
  color: #94a3b8; background: #f8fafc;
  border-top: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9;
  position: sticky; top: 0;
}
.cd-suggest-group:first-child { border-top: none; }
/* "Recents" carries an inline Clear action on the right. */
.cd-suggest-group--recent {
  display: flex; align-items: center; justify-content: space-between;
}
.cd-suggest-recent-clear {
  border: none; background: none; cursor: pointer; padding: 0;
  font-family: inherit; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.7px; color: #667eea;
}
.cd-suggest-recent-clear:hover { text-decoration: underline; }
.cd-suggest-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px; cursor: pointer; font-size: 13px;
  color: #334155; transition: background 0.1s;
}
.cd-suggest-item:hover, .cd-suggest-item.active { background: rgba(102,126,234,0.07); }
.cd-suggest-item mark { background: rgba(102,126,234,0.16); color: #667eea; border-radius: 2px; padding: 0 1px; }
/* Ours: the rows are real anchors (middle-click, ⌘-click, long-press all
   work), so the page-level link colour and hover underline must not reach
   them — same opt-out story as the header popovers, other direction. */
a.cd-suggest-item, a.cd-suggest-item:hover { color: #334155; text-decoration: none; }
/* Ours: the module's tap floor (tokens.css --tap) — height only, nothing
   else, the same shape as the chrome floor block above. */
.cd-suggest-item, .cd-saved-item { min-height: var(--tap); }
.cd-suggest-icon { color: #94a3b8; display: flex; flex-shrink: 0; }
.cd-suggest-icon svg { width: 15px; height: 15px; }
.cd-suggest-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.cd-suggest-meta { font-size: 11px; color: #94a3b8; white-space: nowrap; flex-shrink: 0; }
.cd-suggest-empty { padding: 22px 16px; text-align: center; color: #94a3b8; font-size: 13px; }
.cd-suggest-empty kbd { font-size: 11px; padding: 1px 5px; border: 1px solid #e2e8f0; border-bottom-width: 2px; border-radius: 4px; background: #f8fafc; }
.cd-suggest-hint {
  padding: 7px 16px; font-size: 11.5px; color: #64748b;
  background: #f8fafc; border-top: 1px solid #f1f5f9;
}
.cd-suggest-footer {
  padding: 8px 16px; font-size: 11.5px; color: #94a3b8;
  background: #f8fafc; border-top: 1px solid #f1f5f9; cursor: pointer;
}
.cd-suggest-footer:hover { background: #f1f5f9; }
.cd-suggest-footer kbd, .cd-suggest-hint kbd {
  background: white; border: 1px solid #e2e8f0; border-radius: 4px;
  padding: 1px 5px; font-size: 10px; font-family: inherit;
}

/* Launcher rows (empty-focus state): a page, a saved search and an order you
   looked at yesterday are tellable apart at a glance — the kind sits on a
   chip at the row's end, live's own reasoning kept with its values. */
.cd-suggest-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: 1px;
}
.cd-suggest-sub {
  font-size: 11.5px; color: #94a3b8;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cd-suggest-kind {
  flex: none; margin-left: 10px; padding: 2px 7px; border-radius: 999px;
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; background: #f1f5f9; color: #64748b;
}
/* --recent IS the base look (live styles Recent with the bare class); named
   here so the modifier the markup emits always resolves. */
.cd-suggest-kind--recent { background: #f1f5f9; color: #64748b; }
.cd-suggest-kind--page { background: #eef2ff; color: #4f46e5; }
.cd-suggest-kind--saved { background: #fef3c7; color: #92400e; }

/* Saved searches dropdown. Same z rung as the suggest panel +1: when both
   could be open the saved list is the one just asked for. */
.cd-saved-dd {
  display: none; position: absolute; right: 0; top: calc(100% + 8px);
  width: 360px; max-width: calc(100vw - 40px);
  background: white; border-radius: 12px; border: 1px solid #e2e8f0;
  box-shadow: 0 16px 48px rgba(0,0,0,0.16);
  z-index: calc(var(--z-searchpop) + 1); overflow: hidden;
}
.cd-saved-dd.visible { display: block; }
.cd-saved-dd-head {
  padding: 11px 14px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.6px;
  color: #94a3b8; border-bottom: 1px solid #f1f5f9;
  display: flex; justify-content: space-between; align-items: center;
}
.cd-saved-dd-list { max-height: 320px; overflow-y: auto; }
.cd-saved-item { padding: 11px 14px; cursor: pointer; border-bottom: 1px solid #f8fafc; transition: background 0.1s; }
.cd-saved-item:hover { background: rgba(102,126,234,0.05); }
.cd-saved-item:last-child { border-bottom: none; }
.cd-saved-item-name { display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 700; color: #1e293b; }
.cd-saved-item-name svg { width: 13px; height: 13px; color: #667eea; flex-shrink: 0; }
.cd-saved-item-meta { display: flex; gap: 10px; font-size: 11px; color: #94a3b8; margin-top: 4px; }
.cd-saved-item-meta span { display: inline-flex; align-items: center; gap: 4px; }
.cd-saved-dd-foot { padding: 10px 14px; background: #f8fafc; border-top: 1px solid #f1f5f9; }
.cd-saved-foot-note { font-size: 11.5px; color: #94a3b8; }
.cd-saved-empty { padding: 24px 14px; text-align: center; color: #94a3b8; font-size: 12.5px; }
/* Ours: remove-one. Live's saved rows have no ✕ (its saving lives on the
   Orders page); a local list needs a local way out. On the row's end, quiet
   until hovered or focused — but always THERE, the touch rule. */
.cd-saved-x {
  margin-left: auto; flex: none; display: grid; place-items: center;
  width: 24px; height: 24px; border: none; border-radius: 6px;
  background: transparent; color: #cbd5e1; cursor: pointer; font-size: 12px;
}
.cd-saved-x:hover { background: #fee2e2; color: #ef4444; }

/* PLACEMENT (ours). renderShell() puts .cd-search-wrap first inside .uv-main,
   OUTSIDE #app — so it takes the page's own width and gutter here rather than
   inheriting none. Content, not chrome: it scrolls away with the page. The
   margin shorthand zeroes live's 18px bottom margin deliberately — #app's own
   top padding (--s5) is the gap below the bar, and both would be double. */
.uv-main > .cd-search-wrap, .app-main > .cd-search-wrap {
  width: calc(100% - (var(--pad-page) * 2));
  max-width: calc(var(--w-page) - (var(--pad-page) * 2));
  margin: var(--s5) auto 0;
}

/* Live's own responsive steps for the bar (dashboard.css:599-615). */
@media (max-width: 820px) {
  .cd-search { height: 48px; padding: 0 12px; gap: 8px; }
  .cd-saved-btn .cd-saved-btn-label { display: none; }
}
@media (max-width: 640px) {
  .cd-search-wrap { margin-bottom: 12px; }
  .cd-search { height: 44px; padding: 0 10px; gap: 8px; border-radius: 10px; }
  .cd-search-input { font-size: 13.5px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   DENSITY — eight scoped rules, never a fork.
   The DEFAULT is the designed value; compact is a stated, reversible,
   URL-addressable exception. HR's earlier version restated the whole type
   scale unconditionally and then put it back under an attribute — a fork in
   override form, and the single biggest reason that module stopped looking
   like its host app.
   ══════════════════════════════════════════════════════════════════════════ */
[data-density="compact"] .cd-table tbody td { height: 36px; padding: 8px 12px; font-size: 13px; }
[data-density="compact"] .cd-table thead th { height: 32px; padding: 8px 12px; font-size: 11.5px; }
[data-density="compact"] .cd-form-input,
[data-density="compact"] .cd-form-select { height: 32px; font-size: 13px; }
[data-density="compact"] .cd-table tbody tr.is-bi td { height: 52px; }
[data-density="compact"] .cd-badge { min-height: 18px; font-size: 10.5px; }
[data-density="compact"] .cd-btn { height: 32px; font-size: var(--t-meta); }
[data-density="compact"] .cd-tab { height: 34px; }

/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE / PRINT / MOTION
   ══════════════════════════════════════════════════════════════════════════ */
/* The breakpoints are live Pulse's own, in live Pulse's own order. Each one
   drops exactly one thing, and each drop buys the nav its width back. */

/* 1366 — the subtitle yields to the nav. Dropped whole, never ellipsised:
   show it fully or not at all. */
@media (max-width: 1366px) {
  .app-header-menu__subtitle { display: none; }
}
/* 1024 — the whole title section yields. */
@media (max-width: 1024px) {
  .app-header-menu__title-section { display: none; }
  .app-co .nm { display: none; }
}
/* 900 — the DEMO pill collapses to a dot and the More panel to one column. */
@media (max-width: 900px) {
  .app-header-menu__dev-text { display: none; }
  .app-header-menu__dev { width: 26px; padding: 0; justify-content: center; }
  .top-nav__dropdown--more { min-width: 260px; columns: 1; }
}
/* 768 — the hamburger and the dock appear, the horizontal nav goes. The
   breakpoint lives HERE and only here; shell.js reads the computed display of
   .top-nav rather than re-hardcoding 768 in a second place. */
@media (max-width: 768px) {
  :root { --pad-page: 16px; }
  .app-header-menu__hamburger { display: flex; }
  .top-nav { display: none; }
  .app-header-menu__user-name { display: none; }
  .app-rolechip { display: none; }
  .mt3-dock { display: flex; }
  /* Reserve the dock's height for as long as the dock EXISTS, not only while
     it is on screen: removing the padding mid-scroll reflowed the whole page
     and left a wide table's column layout visibly wrong until the next
     scroll. The bar hides to free the VIEW, not to hand the space back. */
  /* .uv-main is the alias — see PAGE SHELL. */
  /* The dock's height is reserved ONCE, on <body> (uv.css §20), and not here.
     Reserving it on main as well put the 64px between the last card and the
     FOOTER, which then sat 64px above a second 64px gap under it — about 160px
     of dead grey on a phone, measured at 375: pager ended at 3342, the footer
     did not start until 3502. The footer has to clear the dock too, and it
     lives outside main, so <body> is the one element that can reserve it for
     both. */
}
/* 640 — the search trigger shrinks and its panel goes full-width fixed. The
   row also tightens: 10px gaps, 12px ends, and the company chip drops its
   currency (the base currency is on every page that spends it). */
@media (max-width: 640px) {
  .cd-search-trigger { width: 36px; height: 36px; }
  .cd-search-pop {
    position: fixed; top: calc(var(--header-height, 60px) + 6px);
    left: 8px; right: 8px; width: auto;
  }
  .app-header-menu__main { gap: 10px; padding: 0 12px; }
  .app-co .ccy { display: none; }
}
/* 480 — a phone. The wordmark crops to its mark (preserveAspectRatio slice, so
   it is the SAME svg, not a second file) and the user button loses its padding.
   Below this the row was 477px wide inside a 360px viewport and the WHOLE PAGE
   scrolled sideways — a table you cannot read straight is worse than a logo
   you cannot read. */
@media (max-width: 480px) {
  .app-header-menu__logo { width: 26px; }
  .app-header-menu__user-btn { padding: 6px; gap: 6px; }
}
/* 400 — the smallest phones. The DEMO dot yields; the demo banner on every
   invented table still says what this data is. */
@media (max-width: 400px) {
  .app-header-menu__dev { display: none; }
  .app-header-menu__main { gap: 8px; padding: 0 8px; }
  .app-header-menu__search { margin-right: 4px; }
}
@media print {
  .app-header-menu, .mt3-dock, .mobile-nav, .mobile-nav__overlay,
  .app-footer, .cd-table-toolbar { display: none !important; }
  .app-main, .uv-main { margin: 0; padding: 0; }
  .app-page, .uv-main > #app { max-width: none; padding: 0; }
  .cd-table-container { max-height: none; border: 0; }
}
/* The ONE reduced-motion rule (uv.css carried a near-twin; consolidated here
   30 Aug). iteration-count 1 matters as much as the duration: an infinite
   pulse at .01ms is a strobe, not stillness. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
