/*
 * house.css - the ModWash house look: ONE design system for every page of the portal.
 *
 * Sources: the kiosk page (static/kiosk_customer.html - frozen; its token VALUES are copied here
 * verbatim and win over every other source) and the drafts' shared style block
 * (docs/superpowers/specs/2026-11-03-portal-redesign-mock/BRIEF.md, section 3). The bare class names
 * of the drafts are the h- names here: .topbar is .h-topbar, .card is .h-card, .chip is .h-chip.
 *
 * THE LAW: add a component here, never a per-page copy. A page moves onto the house look by using
 * these classes, or by pointing its own rules at these tokens.
 *
 * What a page can rely on:
 * - The tokens live on the document root. Every rule below reads a token WITH its literal value as
 *   the fallback, so a rule copied elsewhere, or a page whose root lost a token, still paints right.
 * - Nothing global but the root tokens, color-scheme and the focus ring: every other selector starts
 *   at an h- class, so including this sheet changes no element a page has not opted into.
 * - The scrim (.h-modal-bg), the drawer and the toast paint only with .show (the class App.openModal
 *   toggles), and every h- element honours the hidden attribute.
 * - Parts inside a component keep short names, scoped by their h- parent: the drafts' .d, .v, .l,
 *   .np, .album and .tr, and the hold ring's .track and .fill.
 * - No @import: each page keeps the Google Fonts link the kiosk page uses (Sora 600/700/800, Manrope
 *   500/600/700/800, display=swap). The system fallbacks and the explicit line-heights keep every
 *   component's size when the fonts are stripped (the bridge's offline portal).
 * - One look, no dark mode: color-scheme light.
 *
 * The tones. Chip text and the red and amber stat values are written in the tone's -ink shade, which
 * reads 4.5:1 on white, the tint and the ground: red #b3131f, amber #8a5a00, green #147032 (the
 * signal green on its chip tint is only 4.44:1). A chip's dot and a live dot take the colour of the
 * text around them (currentColor, as BRIEF section 3 draws the dot), and the green stat value keeps
 * the drafts' signal green #1a7f37 (22 px bold: 4.77:1 on the tint). White text on red sits on
 * #b3131f (on the signal red #ee2737 it is 4.2:1). The signal colours paint the toast dots, the hold
 * ring, the switch's on track, borders and lamps.
 *
 * Every departure from BRIEF section 3:
 * - Values changed: .h-pill is 44 px tall (drafted 40: the 44 px law, and the kiosk's own pill); the
 *   green chip text #147032, and .h-danger / .h-badge on #b3131f (the drafts' white on #ee2737 is
 *   4.2:1); the card shadow and the font stacks are the kiosk page's (P1; the drafts' lighter card
 *   shadow is retired); the rail labels and the music line are 11 px (drafted 10.5: the drafts' own
 *   11 px caption floor); the focus ring is .6 (3.2:1 on white, WCAG 1.4.11).
 * - Kept on purpose: .h-seg is inline-flex (drafted flex: the same inside a flex row, right when it
 *   stands alone); .h-icobox has a default tint, the brand at 12% (drafted none: a card sets its own);
 *   .h-h and .h-h2 have margin 0 (a heading tag keeps no browser margin).
 * - Reverted: the .np b and .who b weights are 700, what the drafts render (no weight there: bold
 *   over the boards' 400 body).
 * - Added, because a page's own rules are not the boards': every component that draws text names its
 *   font family, weight, size and an explicit line-height (sizes hold on the system font) - except
 *   .h-muted, which stays the drafts' colour and weight and takes the rest from its house container;
 *   box-sizing on each component; margin 0 on every control; a button reset (no border, no padding,
 *   transparent) on the rail button, which the boards draw as a link; text-decoration none on the
 *   controls that can be links; flex none on the tab (a scrolling tab row never squeezes it);
 *   user-select none and touch-action manipulation on the hold; the search pill's min-width 0 and
 *   ellipsis; :hover, :active and :disabled states.
 * - Not carried: the boards' body, a, button and universal rules (global); .board and .col (the
 *   shell's own layout); .check and .max (the shell's corner widgets).
 * - Changed in kind: .scrim is .h-modal-bg (fixed, shown by .show, centring its box); .modal is
 *   .h-modal (fluid: width 100% up to 640 px, .sm 420, .lg 1180, .xl 1300); .search kbd is .h-kbd and
 *   .dock-btn .badge is .h-badge, each usable alone.
 * - Beyond section 3: from the boards' own styles .h-ghost.round, the modal's head / tabs / body /
 *   actions and its 22 px title (StoreModal, Overlays), .h-input (Overlays .inp), .h-switch
 *   (Numbers), .h-table (AppPage), .h-hold with its ring and .h-live-dot (Lanes, Tunnel), .h-sp;
 *   drawn in the idiom: .h-select, .h-label, .h-drawer, .h-toast, .h-empty, .h-skeleton.
 * - The four plan colours are fallbacks only (live plan colours come from the config). ModWash's is
 *   the brand blue, the kiosk page's own colour for a plan without one: the old portal accent is
 *   retired everywhere.
 *
 * The gate: scripts/house_sheet.py renders every component in Chromium (44 px targets, the 11 px
 * floor, 4.5:1 text contrast, focus rings at 3:1, no overflow, the fonts, the same sizes on the
 * system font). The tests: tests/test_house_look.py.
 */

:root {
  color-scheme: light;
  --h-ink: #0b1220;
  --h-ground: #eef2f7;
  --h-card: #ffffff;
  --h-brand: #0047bb;
  --h-brand-2: #1e6cff;
  --h-brand-rgb: 0, 71, 187;
  --h-grad: linear-gradient(135deg, #0047bb, #1e6cff);
  --h-amber: #faac1c;
  --h-red: #ee2737;
  --h-green: #1a7f37;
  --h-amber-ink: #8a5a00;
  --h-red-ink: #b3131f;
  --h-green-ink: #147032;
  --h-muted: #5e6a7d;
  --h-text-2: #475569;
  --h-line: rgba(11,18,32,.09);
  --h-line-2: rgba(11,18,32,.06);
  --h-tint: #f6f8fb;
  --h-r-modal: 22px;
  --h-r-bar: 20px;
  --h-r-card: 18px;
  --h-r-btn: 14px;
  --h-r-icon: 12px;
  --h-r-pill: 999px;
  --h-shadow-card: 0 14px 32px rgba(11,18,32,.08), 0 2px 6px rgba(11,18,32,.05);
  --h-shadow-panel: 0 18px 40px rgba(11,18,32,.10), 0 2px 6px rgba(11,18,32,.06);
  --h-shadow-bar: 0 10px 28px rgba(11,18,32,.08), 0 2px 6px rgba(11,18,32,.05);
  --h-shadow-modal: 0 30px 70px rgba(11,18,32,.30);
  --h-font-head: Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --h-font-body: Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --h-plan-modmax: #1a7f37;
  --h-plan-modwash: #0047bb;
  --h-plan-morewash: #0969da;
  --h-plan-mustwash: #ee2737;
}

/* The focus ring: every control, keyboard focus only (.6 reads 3.2:1 on white, 3.0:1 on the ground). */
:focus-visible {
  outline: 3px solid rgba(0,71,187,.6);
  outline-offset: 2px;
}


/* ── The top bar (72 px) ─────────────────────────────────────────────────────────────────────── */

.h-topbar {
  box-sizing: border-box;
  height: 72px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 0 18px;
  border-radius: var(--h-r-bar, 20px);
  background: var(--h-card, #ffffff);
  box-shadow: var(--h-shadow-bar, 0 10px 28px rgba(11,18,32,.08), 0 2px 6px rgba(11,18,32,.05));
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.2;
  color: var(--h-ink, #0b1220);
}
.h-brand {
  display: flex;
  align-items: baseline;
  flex: none;
  margin-right: 6px;
}
.h-wm {
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -.01em;
  color: var(--h-brand, #0047bb);
}
.h-wm-sub {
  margin-left: 8px;
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--h-muted, #5e6a7d);
}

/* The search pill: opens the global search. */
.h-search {
  box-sizing: border-box;
  flex: 1;
  min-width: 0;
  max-width: 520px;
  height: 44px;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 0 14px;
  border: 2px solid var(--h-line, rgba(11,18,32,.09));
  border-radius: var(--h-r-pill, 999px);
  background: var(--h-tint, #f6f8fb);
  color: var(--h-muted, #5e6a7d);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 14px;
  line-height: 1.2;
  text-align: left;
  cursor: pointer;
}
.h-search:hover {
  background: var(--h-card, #ffffff);
}
.h-search > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.h-search svg {
  flex: none;
}
.h-search .h-kbd {
  margin-left: auto;
}

/* A key cap (Ctrl K). */
.h-kbd {
  box-sizing: border-box;
  display: inline-block;
  padding: 2px 6px;
  border: 1.5px solid rgba(11,18,32,.12);
  border-radius: 6px;
  background: var(--h-card, #ffffff);
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 11px;
  line-height: 1.3;
  letter-spacing: .06em;
  color: var(--h-muted, #5e6a7d);
  white-space: nowrap;
}

/* The widget dock: music, announce, tasks, notes, the admin gear. */
.h-dock {
  display: flex;
  gap: 6px;
  margin-left: auto;
}
.h-dock-btn {
  box-sizing: border-box;
  position: relative;
  height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0;
  padding: 0;
  border: 2px solid var(--h-line, rgba(11,18,32,.09));
  border-radius: var(--h-r-icon, 12px);
  background: var(--h-card, #ffffff);
  color: var(--h-text-2, #475569);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 13px;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}
.h-dock-btn:hover {
  background: var(--h-tint, #f6f8fb);
  color: var(--h-ink, #0b1220);
}
.h-dock-btn.music {
  padding: 0 12px 0 6px;
}
.h-dock-btn .album {
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--h-green, #1a7f37), var(--h-ink, #0b1220));
}
.h-dock-btn .np {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
  text-align: left;
}
.h-dock-btn .np b {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--h-ink, #0b1220);
}
.h-dock-btn .np small {
  font-size: 11px;
  font-weight: 700;
  color: var(--h-muted, #5e6a7d);
}
.h-dock-btn .h-badge {
  position: absolute;
  top: -7px;
  right: -7px;
}

/* A count bubble. */
.h-badge {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border: 2px solid #ffffff;
  border-radius: var(--h-r-pill, 999px);
  background: var(--h-red-ink, #b3131f);
  color: #ffffff;
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 11px;
  line-height: 1;
}

/* The user menu chip. */
.h-user {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex: none;
  height: 44px;
  margin: 0;
  padding: 0 12px 0 6px;
  border: 2px solid var(--h-line, rgba(11,18,32,.09));
  border-radius: var(--h-r-pill, 999px);
  background: var(--h-card, #ffffff);
  color: var(--h-text-2, #475569);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-size: 14px;
  line-height: 1.2;
  cursor: pointer;
}
.h-user:hover {
  background: var(--h-tint, #f6f8fb);
}
.h-avatar {
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--h-grad, linear-gradient(135deg, #0047bb, #1e6cff));
  color: #ffffff;
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 12px;
  line-height: 1;
}
.h-who {
  display: flex;
  flex-direction: column;
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.05;
  text-align: left;
}
.h-who b {
  font-size: 14px;
  font-weight: 700;
  color: var(--h-ink, #0b1220);
}
.h-who small {
  font-size: 11px;
  font-weight: 700;
  color: var(--h-muted, #5e6a7d);
}


/* ── The rail (84 px) ────────────────────────────────────────────────────────────────────────── */

.h-rail {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-height: 0;
  padding: 12px 8px;
  border-radius: var(--h-r-bar, 20px);
  background: var(--h-card, #ffffff);
  box-shadow: var(--h-shadow-bar, 0 10px 28px rgba(11,18,32,.08), 0 2px 6px rgba(11,18,32,.05));
}
.h-rail-btn {
  box-sizing: border-box;
  width: 68px;
  height: 64px;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--h-r-btn, 14px);
  background: transparent;
  color: var(--h-text-2, #475569);
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 11px;
  line-height: 1.1;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}
.h-rail-btn svg {
  width: 22px;
  height: 22px;
  flex: none;
}
.h-rail-btn:hover {
  background: var(--h-tint, #f6f8fb);
  color: var(--h-ink, #0b1220);
}
.h-rail-btn.on,
.h-rail-btn.on:hover {
  background: var(--h-ink, #0b1220);
  color: #ffffff;
}
.h-rail-sp {
  flex: 1;
}


/* ── Cards and type ──────────────────────────────────────────────────────────────────────────── */

.h-card {
  box-sizing: border-box;
  border-radius: var(--h-r-card, 18px);
  background: var(--h-card, #ffffff);
  box-shadow: var(--h-shadow-card, 0 14px 32px rgba(11,18,32,.08), 0 2px 6px rgba(11,18,32,.05));
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.4;
  color: var(--h-ink, #0b1220);
}
/* The caption idiom (Sora 12 px, uppercase, .08em, muted) - ONE rule for the caption, a field's
   label and a table's header, so the three cannot drift apart. */
.h-cap,
.h-label,
.h-table th {
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 12px;
  line-height: 1.3;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--h-muted, #5e6a7d);
}
.h-h {
  margin: 0;
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 18px;
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--h-ink, #0b1220);
}
.h-h2 {
  margin: 0;
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 15px;
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--h-ink, #0b1220);
}
/* Muted text: colour and weight only (BRIEF section 3); the font comes from the house container. */
.h-muted {
  font-weight: 600;
  color: var(--h-muted, #5e6a7d);
}
.h-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* A spacer: pushes what follows to the end of a row. */
.h-sp {
  flex: 1;
  min-width: 0;
}


/* ── Chips (a chip always carries its word) ──────────────────────────────────────────────────── */

.h-chip {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border: 1.5px solid rgba(11,18,32,.08);
  border-radius: var(--h-r-pill, 999px);
  background: var(--h-tint, #f6f8fb);
  color: var(--h-text-2, #475569);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
}
.h-chip .d {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
}
.h-chip.good {
  border-color: transparent;
  background: rgba(26,127,55,.10);
  color: var(--h-green-ink, #147032);
}
.h-chip.warn {
  border-color: transparent;
  background: rgba(250,172,28,.18);
  color: var(--h-amber-ink, #8a5a00);
}
.h-chip.bad {
  border-color: transparent;
  background: rgba(238,39,55,.10);
  color: var(--h-red-ink, #b3131f);
}
.h-chip.blue {
  border-color: transparent;
  background: rgba(var(--h-brand-rgb, 0, 71, 187), .08);
  color: var(--h-brand, #0047bb);
}
.h-chip.ink {
  border-color: transparent;
  background: var(--h-ink, #0b1220);
  color: #ffffff;
}

/* A licence plate. */
.h-plate {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  height: 34px;
  padding: 0 10px;
  border: 2px solid var(--h-ink, #0b1220);
  border-radius: 8px;
  background: var(--h-card, #ffffff);
  color: var(--h-ink, #0b1220);
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 15px;
  line-height: 1;
  letter-spacing: .12em;
  white-space: nowrap;
}

/* The live dot blinks in the colour of the text around it (currentColor): the -ink shade in a tone
   chip, white on a red LIVE badge. */
.h-live-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
  animation: h-blink 1.6s ease-in-out infinite;
}
@keyframes h-blink {
  0% { opacity: 1; }
  50% { opacity: .35; }
  100% { opacity: 1; }
}

/* An icon box: tinted in its colour (brand by default; a card sets its own). */
.h-icobox {
  box-sizing: border-box;
  width: 44px;
  height: 44px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--h-r-icon, 12px);
  background: rgba(var(--h-brand-rgb, 0, 71, 187), .12);
  color: var(--h-brand, #0047bb);
}
.h-icobox svg {
  width: 22px;
  height: 22px;
}


/* ── Buttons (44 px; .h-ghost.sm row buttons 36) ─────────────────────────────────────────────── */

.h-primary,
.h-danger,
.h-ghost {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  margin: 0;
  border-radius: var(--h-r-btn, 14px);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 14px;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
}
.h-primary svg,
.h-danger svg,
.h-ghost svg {
  flex: none;
}
.h-primary {
  padding: 0 18px;
  border: 0;
  background: var(--h-grad, linear-gradient(135deg, #0047bb, #1e6cff));
  color: #ffffff;
  box-shadow: 0 8px 18px rgba(var(--h-brand-rgb, 0, 71, 187), .22);
}
.h-danger {
  padding: 0 18px;
  border: 0;
  background: var(--h-red-ink, #b3131f);
  color: #ffffff;
}
.h-primary:active,
.h-danger:active {
  filter: brightness(.94);
}
.h-ghost {
  padding: 0 16px;
  border: 2px solid var(--h-line, rgba(11,18,32,.09));
  background: var(--h-card, #ffffff);
  color: var(--h-ink, #0b1220);
}
.h-ghost:hover {
  background: var(--h-tint, #f6f8fb);
}
.h-ghost:active {
  background: var(--h-ground, #eef2f7);
}
.h-ghost.sm {
  height: 36px;
  padding: 0 12px;
  border-radius: var(--h-r-icon, 12px);
  font-size: 13px;
}
/* The round icon button: the modal's 44 px close. */
.h-ghost.round {
  width: 44px;
  padding: 0;
  border-radius: 50%;
  color: var(--h-text-2, #475569);
}
.h-ghost.sm.round {
  width: 36px;
}

/* The pill and the tab share ONE base (44 px, pill radius, Manrope 800 14 px, text-2); each rule
   below keeps only what differs. */
.h-pill,
.h-tab {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  margin: 0;
  border-radius: var(--h-r-pill, 999px);
  color: var(--h-text-2, #475569);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 14px;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
}
/* A filter pill (44 px: the kiosk's pill). */
.h-pill {
  justify-content: center;
  padding: 0 16px;
  border: 2px solid var(--h-line, rgba(11,18,32,.09));
  background: var(--h-card, #ffffff);
}
.h-pill:hover {
  border-color: rgba(11,18,32,.18);
}
.h-pill:active {
  background: var(--h-ground, #eef2f7);
}
.h-pill.on {
  border-color: var(--h-brand, #0047bb);
  background: rgba(var(--h-brand-rgb, 0, 71, 187), .06);
  color: var(--h-brand, #0047bb);
}

/* Tabs (the shared base above); the selected tab is ink. */
.h-tab {
  flex: none;
  padding: 0 14px;
  border: 0;
  background: transparent;
}
.h-tab:hover {
  background: rgba(11,18,32,.05);
  color: var(--h-ink, #0b1220);
}
.h-tab.on,
.h-tab.on:hover {
  background: var(--h-ink, #0b1220);
  color: #ffffff;
}
/* A segmented control: tabs in a tinted track. */
.h-seg {
  box-sizing: border-box;
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border: 2px solid var(--h-line, rgba(11,18,32,.09));
  border-radius: var(--h-r-pill, 999px);
  background: var(--h-tint, #f6f8fb);
}

/* Disabled controls fade; their contrast is exempt. */
.h-primary:disabled,
.h-danger:disabled,
.h-ghost:disabled,
.h-pill:disabled,
.h-tab:disabled,
.h-hold:disabled,
.h-switch:disabled,
.h-dock-btn:disabled,
.h-input:disabled,
.h-select:disabled {
  opacity: .4;
  cursor: default;
  box-shadow: none;
  filter: none;
}


/* ── The hold-to-act button (the Lanes gate, the E-STOP) and its ring ────────────────────────── */
/* Markup: <button class="h-hold"><svg class="h-hold-ring" viewBox="0 0 48 48"><circle class="track"
   cx="24" cy="24" r="22"/><circle class="fill" cx="24" cy="24" r="22"/></svg>Hold to open the gate</button>.
   The page adds .holding while the press lasts; the ring fills over --h-hold-time (1 s by default). */

.h-hold {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 52px;
  margin: 0;
  padding: 0 18px;
  border: 2px solid rgba(11,18,32,.12);
  border-radius: var(--h-r-btn, 14px);
  background: var(--h-card, #ffffff);
  color: var(--h-ink, #0b1220);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 14px;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}
.h-hold:active,
.h-hold.holding {
  border-color: var(--h-brand, #0047bb);
}
.h-hold-ring {
  width: 28px;
  height: 28px;
  flex: none;
  transform: rotate(-90deg);
}
.h-hold-ring circle {
  fill: none;
  stroke-width: 4;
}
.h-hold-ring .track {
  stroke: rgba(11,18,32,.12);
}
.h-hold-ring .fill {
  stroke: var(--h-brand, #0047bb);
  stroke-linecap: round;
  stroke-dasharray: 138;
  stroke-dashoffset: 138;
}
.h-hold.holding .h-hold-ring .fill {
  animation: h-ring var(--h-hold-time, 1s) linear forwards;
}
@keyframes h-ring {
  from { stroke-dashoffset: 138; }
  to { stroke-dashoffset: 0; }
}
.h-hold.danger {
  border-color: transparent;
  background: var(--h-red-ink, #b3131f);
  color: #ffffff;
  box-shadow: 0 8px 18px rgba(238,39,55,.30);
}
.h-hold.danger .h-hold-ring .track {
  stroke: rgba(255,255,255,.25);
}
.h-hold.danger .h-hold-ring .fill {
  stroke: #ffffff;
}
.h-hold.danger:active,
.h-hold.danger.holding {
  border-color: transparent;
  background: #9f0f1d;
  box-shadow: 0 0 0 4px rgba(238,39,55,.25);
}


/* ── Stat cells ──────────────────────────────────────────────────────────────────────────────── */

.h-stat {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding: 12px 14px;
  border-radius: var(--h-r-btn, 14px);
  background: var(--h-tint, #f6f8fb);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.3;
}
.h-stat .v {
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.15;
  letter-spacing: -.02em;
  color: var(--h-ink, #0b1220);
}
.h-stat .l {
  font-family: var(--h-font-head, Sora, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 11px;
  line-height: 1.3;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--h-muted, #5e6a7d);
}
.h-stat.good .v {
  color: var(--h-green, #1a7f37);
}
.h-stat.bad .v {
  color: var(--h-red-ink, #b3131f);
}
.h-stat.warn .v {
  color: var(--h-amber-ink, #8a5a00);
}
.h-stat.blue .v {
  color: var(--h-brand, #0047bb);
}


/* ── Fields (44 px rows) ─────────────────────────────────────────────────────────────────────── */

/* A field's label: the caption idiom (the shared rule under Cards and type) on a line of its own. */
.h-label {
  display: block;
  margin: 0 0 6px;
}
.h-input,
.h-select {
  box-sizing: border-box;
  display: block;
  width: 100%;
  height: 44px;
  margin: 0;
  padding: 0 14px;
  border: 2px solid rgba(11,18,32,.12);
  border-radius: var(--h-r-btn, 14px);
  background-color: var(--h-card, #ffffff);
  color: var(--h-ink, #0b1220);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 14px;
  line-height: 1.2;
}
.h-input::placeholder {
  color: var(--h-muted, #5e6a7d);
  opacity: 1;
}
.h-input:focus,
.h-select:focus {
  border-color: var(--h-brand, #0047bb);
}
textarea.h-input {
  height: auto;
  min-height: 88px;
  padding: 10px 14px;
  line-height: 1.45;
  resize: vertical;
}
.h-select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235e6a7d' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 16px 16px;
  cursor: pointer;
}

/* A switch: <button class="h-switch" role="switch" aria-checked="false"><span class="tr"></span>Label</button>. */
.h-switch {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  margin: 0;
  padding: 0 8px;
  border: 0;
  border-radius: var(--h-r-btn, 14px);
  background: transparent;
  color: var(--h-ink, #0b1220);
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 14px;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
}
.h-switch:hover {
  background: var(--h-tint, #f6f8fb);
}
.h-switch .tr {
  position: relative;
  width: 44px;
  height: 24px;
  flex: none;
  border-radius: var(--h-r-pill, 999px);
  background: rgba(11,18,32,.16);
  transition: background-color .15s ease;
}
.h-switch .tr::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 3px rgba(11,18,32,.30);
  transition: transform .15s ease;
}
.h-switch[aria-checked="true"] .tr {
  background: var(--h-brand, #0047bb);
}
.h-switch[aria-checked="true"] .tr::after {
  transform: translateX(20px);
}


/* ── Tables: a caps header, 56 px rows, 1.5 px rules ─────────────────────────────────────────── */

.h-table {
  box-sizing: border-box;
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 600;
  font-size: 14px;
  line-height: 1.35;
  color: var(--h-ink, #0b1220);
}
/* The header cells take the caption idiom (the shared rule under Cards and type). */
.h-table th {
  height: 36px;
  padding: 0 10px;
  text-align: left;
  vertical-align: middle;
  white-space: nowrap;
}
.h-table td {
  height: 56px;
  padding: 0 10px;
  vertical-align: middle;
  border-top: 1.5px solid var(--h-line-2, rgba(11,18,32,.06));
}
.h-table th:first-child,
.h-table td:first-child {
  padding-left: 16px;
}
.h-table th:last-child,
.h-table td:last-child {
  padding-right: 16px;
}
.h-table tbody tr:hover td {
  background: var(--h-tint, #f6f8fb);
}


/* ── Modals: the scrim, the box, its head / tabs / body / actions ────────────────────────────── */
/* Markup: <div class="h-modal-bg" id="..."><div class="h-modal"> <div class="h-modal-head"> (code
   chip, .h-h title, chips, .h-sp, actions, the .h-ghost.round close) </div> <div class="h-modal-tabs">
   <div class="h-modal-body"> <div class="h-modal-actions"> (ghost first, then primary) </div></div>.
   The scrim shows with .show (App.openModal). */

.h-modal-bg {
  box-sizing: border-box;
  position: fixed;
  inset: 0;
  z-index: 500;
  display: none;
  align-items: center;
  align-items: safe center;
  justify-content: center;
  padding: 30px 20px;
  overflow-y: auto;
  background: rgba(11,18,32,.45);
}
.h-modal-bg.show {
  display: flex;
}
.h-modal {
  box-sizing: border-box;
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 640px;
  max-height: calc(100vh - 60px);
  overflow: hidden;
  border-radius: var(--h-r-modal, 22px);
  background: var(--h-card, #ffffff);
  box-shadow: var(--h-shadow-modal, 0 30px 70px rgba(11,18,32,.30));
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.4;
  color: var(--h-ink, #0b1220);
}
.h-modal.sm {
  max-width: 420px;
}
.h-modal.lg {
  max-width: 1180px;
}
.h-modal.xl {
  max-width: 1300px;
}
.h-modal-head {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  min-width: 0;
  padding: 18px 22px;
  border-bottom: 2px solid var(--h-line-2, rgba(11,18,32,.06));
}
.h-modal-head .h-h {
  font-size: 22px;
}
.h-modal-tabs {
  box-sizing: border-box;
  display: flex;
  gap: 4px;
  flex: none;
  padding: 10px 22px;
  overflow-x: auto;
}
.h-modal-body {
  box-sizing: border-box;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 18px 22px;
}
.h-modal-actions {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  flex: none;
  padding: 0 22px 18px;
}

/* A right sheet (420 px) with the modal's head / body / actions; shows with .show. Pair it with a
   .h-modal-bg scrim when the page behind must not be touched. */
.h-drawer {
  box-sizing: border-box;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 510;
  display: none;
  flex-direction: column;
  width: 420px;
  max-width: 100%;
  overflow: hidden;
  border-radius: var(--h-r-modal, 22px) 0 0 var(--h-r-modal, 22px);
  background: var(--h-card, #ffffff);
  box-shadow: var(--h-shadow-modal, 0 30px 70px rgba(11,18,32,.30));
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.4;
  color: var(--h-ink, #0b1220);
}
.h-drawer.show {
  display: flex;
}


/* ── States: the toast, the empty state, the skeleton ────────────────────────────────────────── */

/* A toast: a white card, bottom centre, with a dot in its tone (never a coloured left border). */
.h-toast {
  box-sizing: border-box;
  position: fixed;
  left: 50%;
  bottom: 24px;
  z-index: 9999;
  display: none;
  align-items: center;
  gap: 10px;
  width: max-content;
  min-height: 52px;
  max-width: 560px;
  max-width: min(560px, calc(100% - 32px));
  padding: 12px 16px 12px 14px;
  border-radius: var(--h-r-card, 18px);
  background: var(--h-card, #ffffff);
  box-shadow: var(--h-shadow-panel, 0 18px 40px rgba(11,18,32,.10), 0 2px 6px rgba(11,18,32,.06));
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  font-weight: 700;
  font-size: 14px;
  line-height: 1.35;
  color: var(--h-ink, #0b1220);
  transform: translateX(-50%);
}
.h-toast.show {
  display: flex;
}
.h-toast::before {
  content: "";
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 50%;
  background: var(--h-text-2, #475569);
}
.h-toast.good::before {
  background: var(--h-green, #1a7f37);
}
.h-toast.warn::before {
  background: var(--h-amber, #faac1c);
}
.h-toast.bad::before {
  background: var(--h-red, #ee2737);
}
.h-toast.blue::before {
  background: var(--h-brand, #0047bb);
}

/* An empty state: an icon, one sentence, one action. */
.h-empty {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 32px 20px;
  text-align: center;
  font-family: var(--h-font-body, Manrope, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  line-height: 1.45;
  color: var(--h-text-2, #475569);
}
.h-empty .h-icobox {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--h-tint, #f6f8fb);
  color: var(--h-muted, #5e6a7d);
}
.h-empty p {
  margin: 0;
  max-width: 36ch;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--h-text-2, #475569);
}

/* A loading placeholder (a skeleton, never a spinner alone). */
.h-skeleton {
  box-sizing: border-box;
  display: block;
  min-height: 14px;
  border-radius: 8px;
  background-color: var(--h-ground, #eef2f7);
  background-image: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.7) 50%, rgba(255,255,255,0) 100%);
  background-size: 200% 100%;
  background-repeat: no-repeat;
  color: transparent;
  animation: h-shimmer 1.4s ease-in-out infinite;
}
@keyframes h-shimmer {
  from { background-position: 150% 0; }
  to { background-position: -50% 0; }
}


/* ── Every house element honours the hidden attribute ────────────────────────────────────────── */

.h-topbar[hidden], .h-brand[hidden], .h-wm[hidden], .h-wm-sub[hidden], .h-search[hidden], .h-kbd[hidden],
.h-dock[hidden], .h-dock-btn[hidden], .h-badge[hidden], .h-user[hidden], .h-avatar[hidden], .h-who[hidden],
.h-rail[hidden], .h-rail-btn[hidden], .h-rail-sp[hidden], .h-card[hidden], .h-cap[hidden], .h-h[hidden],
.h-h2[hidden], .h-muted[hidden], .h-row[hidden], .h-sp[hidden], .h-chip[hidden], .h-plate[hidden],
.h-live-dot[hidden], .h-icobox[hidden], .h-primary[hidden], .h-danger[hidden], .h-ghost[hidden],
.h-pill[hidden], .h-tab[hidden], .h-seg[hidden], .h-hold[hidden], .h-hold-ring[hidden], .h-stat[hidden],
.h-label[hidden], .h-input[hidden], .h-select[hidden], .h-switch[hidden], .h-table[hidden],
.h-modal-bg[hidden], .h-modal[hidden], .h-modal-head[hidden], .h-modal-tabs[hidden], .h-modal-body[hidden],
.h-modal-actions[hidden], .h-drawer[hidden], .h-toast[hidden], .h-empty[hidden], .h-skeleton[hidden] {
  display: none !important;
}


/* ── Reduced motion: nothing blinks, fills or shimmers ───────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .h-live-dot {
    animation: none;
  }
  .h-hold.holding .h-hold-ring .fill {
    animation: none;
  }
  .h-skeleton {
    animation: none;
  }
  .h-switch .tr,
  .h-switch .tr::after {
    transition: none;
  }
}

/* == W-8 (2026-11-05, the owner after the 18:50 restart: the user menu listed "Broadcast announcement" and "Assign
   task" twice): the menu renders its own announce and task rows (#umAnnounce / #umAssignTask) AND the place the
   portal's boot mover puts the announce and task widgets a person docked in the menu (#umWidgets). Both carry the same
   key (feature_announce / feature_tasks), so a docked widget is always beside its twin. While the widget sits in the
   menu, its twin row steps aside: each action shows ONCE. Moved back to the bar (right-click, "Move to top bar"),
   the row shows again - :has() follows every move, the boot mover's, a drag's or a right-click's, with no script.
   Here, not in portal_layout.css: a docked layout outlives a customize revoke, and portal_layout.css / .js load
   only with that key; this sheet loads for everyone. The selectors exist only on the portal page. */
#userMenuDropdown:has(#umWidgets [data-widget-id="w_broadcast"]) #umAnnounce,
#userMenuDropdown:has(#umWidgets [data-widget-id="w_task"]) #umAssignTask{display:none}
/* the row that stays is the widget's own button, which opens its composer and leaves the menu open (the twin row
   closed the menu first); on a phone the open menu (z 9575) covers the composer (z 600). While either composer
   shows, the user menu steps out of sight; the !important beats the menu's inline display. == */
body:has(#announceComposerModal[style*="flex"]) #userMenuDropdown,
body:has(#taskComposerModal[style*="flex"]) #userMenuDropdown{display:none!important}

/* == W-8 part 2 (lands with the second restart, beside W-8's portal.py; the board Chrome.dc.html (a)): on a desk the
   bar's dock carries Broadcast announcement and Assign task, and the menu's own rows are My links and Sign out. So
   above 600 px a menu row steps aside while its widget shows in the dock: each action once, in the dock. At 600 px
   and under 10b hides those two buttons in the dock and the menu's rows stay their home. == */
@media (min-width:601px){
body:has(#plTopDock [data-widget-id="w_broadcast"]) #umAnnounce,
body:has(#plTopDock [data-widget-id="w_task"]) #umAssignTask{display:none}
}
