@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;450;500;550;600;650;700&family=IBM+Plex+Mono:wght@400;500&display=swap');
/* Chinese text (2026-10-31): the system's own CJK fonts, never a web font (Google Fonts is blocked in China).
   'WB CJK' covers only CJK characters (unicode-range), so Latin keeps DM Sans in both languages. It gives each weight
   band one real face: DM Sans's in-between weights (450, 550, 650) would otherwise round up to PingFang Semibold or
   Microsoft YaHei Bold, so Chinese read heavier than English.
   Each band lists PingFang SC (Mac), Microsoft YaHei (Windows), then Noto Sans CJK / Source Han Sans (Linux, Android);
   where none of them is installed, the family list below takes over as before. */
@font-face { font-family: 'WB CJK'; font-weight: 100 449; font-display: swap;
  src: local('PingFangSC-Regular'), local('PingFang SC Regular'), local('Microsoft YaHei'), local('MicrosoftYaHei'),
    local('NotoSansCJKsc-Regular'), local('Noto Sans CJK SC'), local('SourceHanSansSC-Regular');
  unicode-range: U+2E80-2FDF, U+3000-33FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE30-FE4F, U+FF00-FFEF; }
@font-face { font-family: 'WB CJK'; font-weight: 450 599; font-display: swap;
  src: local('PingFangSC-Medium'), local('PingFang SC Medium'), local('Microsoft YaHei'), local('MicrosoftYaHei'),
    local('NotoSansCJKsc-Medium'), local('Noto Sans CJK SC Medium'), local('SourceHanSansSC-Medium');
  unicode-range: U+2E80-2FDF, U+3000-33FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE30-FE4F, U+FF00-FFEF; }
@font-face { font-family: 'WB CJK'; font-weight: 600 1000; font-display: swap;
  src: local('PingFangSC-Semibold'), local('PingFang SC Semibold'), local('Microsoft YaHei Bold'),
    local('MicrosoftYaHei-Bold'), local('NotoSansCJKsc-Bold'), local('Noto Sans CJK SC Bold'),
    local('SourceHanSansSC-Bold');
  unicode-range: U+2E80-2FDF, U+3000-33FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE30-FE4F, U+FF00-FFEF; }
/* Shared colour roles: four themes, picked in Settings and stored as data-theme: Mist (light, the default, :root), Dim
   slate (dim), Solarized light and Solarized dark (Ethan Schoonover's palette, kept as the hues; the greys of text
   and the accent are shifted along their hue only as far as 4.5:1 needs). The dark ones also carry
   data-theme-mode="dark", so a rule for dark themes in general is written against that attribute, never one theme's
   name. No pure white or black, body text about 8-12:1 on its surface, secondary text and text on the
   selection at least 4.5:1 (tests/test_look_contrast.py). No soft shadows (transparent): what floats gets
   --hard-shadow ("Components" at the end of this file). */
:root {
  color-scheme: light;
  --sidebar-width: 196px;
  --font-sans: 'DM Sans', 'WB CJK', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB',
    'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --picture-filter: saturate(0.6);
  --canvas: #e9ecec;
  --surface: #eff1f1;
  --surface-alt: #e1e5e5;
  --soft: #dfe3e4;
  --text: #30383b;
  --secondary: #475257;
  --muted: #5c676b;
  --border: #d3d9da;
  --border-strong: #b5bfc1;
  --accent: #4f6575;
  --accent-hover: #41586a;
  --accent-edge: #313f49;         /* a Keycap key action's heavier bottom edge: the accent, darker */
  --accent-hover-edge: #283742;
  --on-accent: #eff1f1;
  --link: #41586a;
  --focus: #4f6575;
  --focus-soft: #4f657529;
  /* Highlights (the standard, Haofeng 2026-10-28; it replaces #150's double line): the accent fill
     is for the one key action a page expects (.primary: Send, Save, Commit), never a setting. What is selected, on or
     current (an option, a setting, a filter, a row, a link) is a soft fill: --selected-bg, a background slightly
     darker than the surface (muted text keeps 4.5:1 on it), no line and no accent. Mist and Dim slate set it
     (tests/test_look_contrast.py). */
  --selected-bg: #dfe3e4;
  --nav-selected-bg: #d1d5d6;   /* the sidebar's current page: a shade darker than the sidebar (--surface-alt) */
  /* chart colours: --cat-* for bars that are different things (one hue each, the same bright mid tones in both
     themes, as the old look had them: Haofeng liked them better, #210); the accent ramp (--chart-*) only for ranked
     or ordered values */
  --cat-1: #3f8f88;
  --cat-2: #c27c2c;
  --cat-3: #6f6cc4;
  --cat-4: #c4566a;
  --cat-5: #4f86c6;
  --cat-6: #8a9a3a;
  --cat-7: #a0679e;
  --cat-other: #858d91;
  --warning: #755b00;
  --warning-bg: #ebe1bd;
  --warning-border: #d2c27f;
  --danger: #9e2b33;
  --live: #376348;   /* a state: on and working (Push to Talk available or live), as --danger is a refusal */
  --danger-bg: #f0dcdb;
  --danger-border: #dcb3b1;
  --no-agent: #e6a9a4;   /* off limits to the AI: the light red border (off-limits.js, UI_RULES "Off limits to the AI") */
  --chart-1: #4f6575;
  --chart-2: #6d8291;
  --chart-3: #91a3b0;
  --chart-4: #b6c4cd;
  --shadow: #1f2a3000;
  --shadow-soft: #1f2a3000;
  --hard-shadow: 2px 2px 0 var(--border-strong);
  --scrim: #30383b66;
  /* drawing (draw.js): paper and the four inks, the same in both themes: drawings sit on the page, which stays paper
     (wbench/annotated_pdf.py draws with the same colours) */
  --paper: #fcfcfa;
  --ink-red: #c62828;
  --ink-blue: #1e5bc6;
  --ink-green: #2e7d32;
  --ink-black: #1b1b1b;
}
:root[data-theme="dim"] {
  color-scheme: dark;
  --picture-filter: brightness(0.9) saturate(0.5) hue-rotate(170deg);
  --canvas: #22272e;
  --surface: #282e36;
  --surface-alt: #1f242a;
  --soft: #2c323a;
  --text: #c9ced6;
  --secondary: #aab1bb;
  --muted: #929aa5;
  --border: #363d47;
  --border-strong: #4d5562;
  --accent: #7fa3b8;
  --accent-hover: #9dbccd;
  --accent-edge: #4f6572;
  --accent-hover-edge: #61757f;
  --on-accent: #1a1f25;
  --link: #9dbccd;
  --focus: #7fa3b8;
  --focus-soft: #7fa3b829;
  --selected-bg: #20252b;
  --nav-selected-bg: #2f343b;
  --cat-1: #3f8f88;
  --cat-2: #c27c2c;
  --cat-3: #6f6cc4;
  --cat-4: #c4566a;
  --cat-5: #4f86c6;
  --cat-6: #8a9a3a;
  --cat-7: #a0679e;
  --cat-other: #7d858f;
  --warning: #d3b36e;
  --warning-bg: #302d24;
  --warning-border: #5e5338;
  --danger: #e8958b;
  --live: #8cbc98;
  --danger-bg: #3a2a2c;
  --danger-border: #6e4a4a;
  --no-agent: #8c5552;
  --chart-1: #5a8199;
  --chart-2: #7097ad;
  --chart-3: #87abbf;
  --chart-4: #a0bfcf;
  --shadow: #0d101400;
  --shadow-soft: #0d101400;
  --scrim: #0d1014b3;
  --paper: #fcfcfa;
  --ink-red: #c62828;
  --ink-blue: #1e5bc6;
  --ink-green: #2e7d32;
  --ink-black: #1b1b1b;
}
/* Solarized light: base3 surface, base2 sidebar and fills, base02 text; yellow, red and green are darkened to read as
   text, blue is the accent. Colour roles only (tests/test_look_contrast.py). */
:root[data-theme="solarized-light"] {
  --picture-filter: sepia(0.2) saturate(0.7);
  --canvas: #f5efdc;
  --surface: #fdf6e3;
  --surface-alt: #eee8d5;
  --soft: #eae4d0;
  --text: #124351;
  --secondary: #4a626a;
  --muted: #4f676f;
  --border: #e0dac6;
  --border-strong: #93a1a1;
  --accent: #18689a;
  --accent-hover: #135780;
  --accent-edge: #0f4468;
  --accent-hover-edge: #0b3956;
  --on-accent: #fdf6e3;
  --link: #135780;
  --focus: #268bd2;
  --focus-soft: #268bd229;
  --selected-bg: #eee8d5;
  --nav-selected-bg: #e1dbc7;
  --cat-1: #2aa198;
  --cat-2: #cb4b16;
  --cat-3: #6c71c4;
  --cat-4: #d33682;
  --cat-5: #268bd2;
  --cat-6: #859900;
  --cat-7: #b58900;
  --cat-other: #93a1a1;
  --warning: #7a5c00;
  --warning-bg: #f1e5b8;
  --warning-border: #d9c36a;
  --danger: #b93130;
  --live: #4f6b0f;
  --danger-bg: #f6ddd2;
  --danger-border: #e3b3a6;
  --no-agent: #e8a59b;
  --chart-1: #18689a;
  --chart-2: #3a82b3;
  --chart-3: #6a9fc7;
  --chart-4: #9bbcd8;
  --shadow: #07364200;
  --shadow-soft: #07364200;
  --scrim: #002b3666;
}
/* Solarized dark: base03 canvas, base02 surface, base2 text; blue, yellow, red and green are lightened to read as
   text on base02. */
:root[data-theme="solarized-dark"] {
  color-scheme: dark;
  --picture-filter: brightness(0.9) saturate(0.6) sepia(0.2);
  --canvas: #002b36;
  --surface: #073642;
  --surface-alt: #00252e;
  --soft: #0d3f4c;
  --text: #d9d5c2;
  --secondary: #a2adab;
  --muted: #99a6a5;
  --border: #124855;
  --border-strong: #3d6572;
  --accent: #66abd7;
  --accent-hover: #85bde3;
  --accent-edge: #3f7aa0;
  --accent-hover-edge: #5189ad;
  --on-accent: #002b36;
  --link: #85bde3;
  --focus: #66abd7;
  --focus-soft: #66abd729;
  --selected-bg: #04303b;
  --nav-selected-bg: #0b3a47;
  --cat-1: #2aa198;
  --cat-2: #cb4b16;
  --cat-3: #6c71c4;
  --cat-4: #d33682;
  --cat-5: #268bd2;
  --cat-6: #859900;
  --cat-7: #b58900;
  --cat-other: #839496;
  --warning: #c39f2d;
  --warning-bg: #2c3722;
  --warning-border: #5f6a2a;
  --danger: #eb887e;
  --live: #9dac2d;
  --danger-bg: #3a2a2e;
  --danger-border: #7a4a4c;
  --no-agent: #94585a;
  --chart-1: #4d94c4;
  --chart-2: #66abd7;
  --chart-3: #85bde3;
  --chart-4: #a5cfe9;
  --shadow: #00141a00;
  --shadow-soft: #00141a00;
  --scrim: #001a21b3;
}
:root {
  font-family: var(--font-sans);
  color: var(--text);
  background: var(--canvas);
  font-size: 14px;
  --green: var(--accent);
  /* one height for every control that can sit in a row: button (its natural height: 8px padding, 1.4 line, 1px + 2px keycap edge; quiet buttons set their own padding), select, dropdown, text
     and date field, search box; the second for compact rows (button.small). Never size a single control by hand. */
  --control-h: calc(14px * 1.4 + 16px + 3px);
  --control-h-sm: 30px;
}
* {
  box-sizing: border-box;
}
body {
  margin: 0;
}
button,
input,
select {
  font: inherit;
}
input[type='file'] {
  color: var(--muted);
}
input[type='file']::file-selector-button {
  margin-right: 8px;
  padding: 4px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--surface-alt);
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
input[type='file']::file-selector-button:hover {
  background: var(--soft);
}
button,
select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  color: var(--text);
  padding: 8px 10px;
}
/* a button's height is the token whatever its font size: the vertical padding is what the line and the 1px + 2px
   keycap edge leave of --control-h (8px at 14px). A rule sets a button's font, never its vertical padding. */
button {
  cursor: pointer;
  font-weight: var(--button-weight, 550);
  padding-block: calc((var(--control-h) - 3px - 1lh) / 2);
}
select {
  height: var(--control-h);
  padding-top: 0;
  padding-bottom: 0;
}
input:is(:not([type]), [type='text'], [type='search'], [type='number'], [type='date'], [type='month'], [type='week'], [type='time'],
  [type='datetime-local'], [type='url'], [type='email'], [type='tel'], [type='password']) {
  height: var(--control-h);
  padding-top: 0;
  padding-bottom: 0;
}
button:hover {
  background: var(--soft);
  border-color: var(--border);
}
button:disabled {
  opacity: 0.4;
  cursor: default;
}
a {
  color: var(--link);
  text-decoration: none;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}
[hidden] {
  display: none !important;
}
.sidebar {
  width: var(--sidebar-width);
  position: fixed;
  inset: 0 auto 0 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 26px 16px;
  display: flex;
  flex-direction: column;
}
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 6px;
  color: var(--text);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.6px;
}
.brand small {
  display: block;
  font-size: 9px;
  letter-spacing: 1.7px;
  color: var(--secondary);
  margin-top: 5px;
}
.mark {
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 27px;
  width: 37px;
  height: 40px;
  border-radius: 10px;
}
.nav-label {
  font-size: 10px;
  color: var(--muted);
  margin: 40px 12px 10px;
}
nav {
  display: grid;
  gap: 6px;
}
nav a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 7px;
  color: var(--secondary);
  font-weight: 550;
}
nav a > span {
  font-size: 20px;
  width: 23px;
}
nav a.active {
  background: var(--soft);
  color: var(--accent);
}
nav b {
  font-size: 11px;
  font-weight: 500;
  background: var(--soft);
  border-radius: 5px;
  padding: 3px 6px;
  margin-left: auto;
}
nav a.active b {
  background: var(--soft);
}
.sidebar-bottom {
  margin-top: auto;
  border-top: 1px solid var(--border);
  padding: 18px 6px 0;
  font-size: 11px;
  color: var(--secondary);
}
.sidebar-bottom small {
  display: block;
  margin: 8px 0 0 10px;
  color: var(--muted);
  font-size: 10px;
}
.status-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: 6px;
}
main {
  margin-left: var(--sidebar-width);
}
header {
  height: 56px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 0 26px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.breadcrumb {
  font-size: 12px;
  color: var(--secondary);
}
.breadcrumb span {
  margin: 0 10px;
  color: var(--muted);
}
.breadcrumb strong {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.3px;
  color: var(--text);
}
/* the top bar's controls sit 4px apart and are 30px tall: the bar's room is precious (#249) */
.header-actions {
  display: flex;
  gap: 4px;
  align-items: center;
}
.header-actions button {
  min-height: var(--control-h-sm);
  padding: 6px 10px;
  font-size: 11px;
}
.read-only {
  font-size: 11px;
  color: var(--secondary);
}
.content {
  padding: 22px 26px;
  max-width: 1800px;
  margin: auto;
}
.eyebrow {
  font-size: 10px;
  color: var(--secondary);
  font-weight: 600;
}
/* Read by screen readers, not shown: the page's h1 repeats the title bar's crumb (#70). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
h1 {
  font-size: 30px;
  letter-spacing: -1px;
  font-weight: 600;
  margin: 8px 0;
}
p {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.6;
  margin: 0;
}
h2 {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.2px;
  margin: 0 0 6px;
}
.panel-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 16px 14px;
  gap: 10px;
}
.panel-heading p {
  font-size: 11px;
}
.mini-label {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 6px;
}
.bars {
  padding: 4px 16px 16px;
  min-height: 230px;
}
.bar-row {
  margin: 0 0 10px;
}
.bar-label {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  margin-bottom: 4px;
  color: var(--secondary);
}
.bar-label b {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
}
.bar-track {
  background: var(--soft);
  height: 7px;
  border-radius: 3px;
  overflow: hidden;
}
.bar-fill {
  height: 100%;
  background: var(--chart-1);
  border-radius: 3px;
}
.bar-row:nth-child(2) .bar-fill {
  background: var(--chart-2);
}
.bar-row:nth-child(3) .bar-fill {
  background: var(--chart-3);
}
.bar-row:nth-child(n + 4) .bar-fill {
  background: var(--chart-4);
}
.recent .panel-heading a {
  font-size: 11px;
}
.table-scroll {
  overflow: auto;
}
table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  text-align: left;
  white-space: nowrap;
  font-size: 12px;
}
th {
  background: var(--surface-alt);
  color: var(--secondary);
  font-size: 10px;
  font-weight: 550;
  border-bottom: 1px solid var(--border);
  padding: 10px 14px;
  position: sticky;
  top: 0;
  z-index: 1;
}
td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  color: var(--secondary);
  max-width: 330px;
  overflow: hidden;
  text-overflow: ellipsis;
}
tbody tr:last-child td {
  border-bottom: 0;
}
tbody tr:hover td {
  background: var(--soft);
}
tbody tr {
  cursor: pointer;
}
.mono {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text);
}
.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  max-width: 100%;
  overflow-wrap: anywhere;
  line-height: 1.4;
  background: transparent;
  border: 1px solid currentColor;
  padding: 4px 7px;
  color: var(--secondary);
  font-size: 10px;
}
.pill.warning {
  color: var(--warning);
}
.muted {
  color: var(--muted);
}
/* a price change on the Offers grid (#181): cheaper now is good, dearer is bad */
.cheaper {
  color: var(--live);
}
.dearer {
  color: var(--danger);
}
.home-footer {
  display: flex;
  justify-content: space-between;
  color: var(--muted);
  font-size: 10px;
  margin-top: 18px;
}
.sheet-toolbar {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 12px;
  border-radius: 9px;
}
.search-box {
  min-height: var(--control-h);
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: min(200px, 100%);
  padding: 0 8px;
}
.search-box > svg.icon {
  flex: none;
  color: var(--muted);
}
.search-box input {
  border: 0;
  background: transparent;
  padding: 0;
  height: auto;
  width: 100%;
  outline: none;
  color: var(--text);
}
.sheet-toolbar button,
.sheet-toolbar select {
  font-size: 11px;
}
.sheet-toolbar #filters {
  display: flex;
  gap: 6px;
}
.sheet-info {
  display: flex;
  justify-content: space-between;
  padding: 16px 2px 10px;
  font-size: 10px;
  color: var(--muted);
}
.sheet-info span:first-child {
  color: var(--secondary);
}
.grid {
  max-height: 65vh;
  min-height: 120px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.grid th {
  padding: 0;
}
.grid th button {
  border: 0;
  background: transparent;
  width: 100%;
  text-align: left;
  padding: 10px 14px;
  border-radius: 0;
  font-size: 11px;
}
/* the sorted column's arrow (app.js gridHeader): the kit's icon after the name (#626) */
.grid th button > svg.icon { margin-left: 4px; vertical-align: -2px; }
.grid td {
  padding: 6px 12px;
  font-size: 13px;
}
.grid td .mono { font-size: 12px; }
/* fixed column widths (app.js COLUMN_WIDTHS): the table keeps its shape; long values end in an ellipsis */
.grid table.fixed-columns { table-layout: fixed; min-width: 100%; }
.grid table.fixed-columns td, .grid table.fixed-columns th button { overflow: hidden; text-overflow: ellipsis; }
/* data cells a little tighter, so the Components grid fits the page (#201) */
.grid table.fixed-columns td:not([class]), .grid table.fixed-columns th:not([class]) > button { padding-inline: 8px; }
/* a pill too long for its cell (a category) is cut short with "…" inside its outline, like the cell's text; it never
   spills past its own edge (#200: it was centred in a box narrower than its text, cut off at both ends) */
.grid td .pill { display: inline-block; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; }
.row-number {
  color: var(--muted);
  font:
    10px var(--font-mono);
  background: var(--surface-alt) !important;
  border-right: 1px solid var(--border);
  text-align: center;
  position: sticky;
  left: 0;
  z-index: 1;
  width: 32px;
  min-width: 32px;
}
.pagination {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11px;
  color: var(--secondary);
  padding: 14px 0;
}
.pagination > div {
  display: flex;
  gap: 12px;
  align-items: center;
}
.pagination button {
  font-size: 11px;
}
.pagination select {
  padding: 5px;
  margin-left: 6px;
}
.column-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-top: 8px;
  font-size: 11px;
}
.column-picker label {
  display: flex;
  align-items: center;
  gap: 5px;
}
dialog {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 20px 100px var(--shadow);
  padding: 22px;
  width: min(620px, 92vw);
  max-height: calc(100dvh - var(--popup-top, 72px) - 24px); /* opens below the top bar (Popups, #148) */
  color: var(--text);
}
.dialog-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}
/* a dialog's heading and its × stay in view while the dialog scrolls (#148; app.js popupDialog) */
dialog .dialog-heading {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface);
}
.dialog-heading h2 {
  font-size: 20px;
  overflow-wrap: anywhere;
}
.dialog-heading button {
  border: 0;
  font-size: 22px;
  padding: 0 6px;
}
.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}
.primary {
  background: var(--accent);
  color: var(--on-accent);
}
.primary:hover {
  background: var(--accent-hover);
}
dl {
  margin: 0;
  display: grid;
  grid-template-columns: 145px 1fr;
  font-size: 12px;
  gap: 0;
}
dt,
dd {
  margin: 0;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
dt {
  color: var(--secondary);
  padding-right: 15px;
}
dd {
  line-height: 1.6;
}
dd pre {
  white-space: pre-wrap;
  margin: 0;
  font-size: 11px;
}
.empty {
  text-align: center;
  padding: 46px 16px;
  color: var(--muted);
  font-size: 12px;
}
/* notices: square, the surface, a 4px edge in their status colour */
.notice {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--warning-border);
  background: var(--surface);
  color: var(--warning);
  font-size: 12px;
  margin-bottom: 16px;
}
.notice button {
  margin-left: 10px;
  font-size: 11px;
}
@media (min-width: 1500px) {
  .content {
    padding-top: 26px;
  }
  .bars {
    min-height: 280px;
  }
  .bar-row {
    margin-bottom: 18px;
  }
}
@media (max-width: 1000px) {
  .sidebar {
    width: var(--sidebar-width);
    padding: 22px 10px;
  }
  main {
    margin-left: var(--sidebar-width);
  }
  .content {
    padding: 22px 20px;
  }
  header {
    padding: 0 20px;
  }
  .sheet-toolbar #filters {
    flex-wrap: wrap;
  }
}
@media (max-width: 720px) {
  .brand {
    font-size: 17px;
  }
  .mark {
    height: 32px;
    width: 32px;
  }
  .nav-label,
  .sidebar-bottom {
    display: none;
  }
  nav {
    display: flex;
    gap: 4px;
    overflow: auto;
    margin-top: 14px;
  }
  nav a {
    font-size: 12px;
    padding: 8px;
  }
  nav a > span,
  nav b {
    display: none;
  }
  main {
    margin-left: 0;
  }
  header {
    height: 50px;
    padding: 0 14px;
  }
  .content {
    padding: 20px 14px;
  }
  h1 {
    font-size: 25px;
  }
  .home-footer,
  .sheet-info {
    gap: 12px;
  }
  .sheet-info .sheet-hint {
    display: none;
  }
  .pagination {
    gap: 8px;
    flex-wrap: wrap;
  }
  .panel-heading {
    padding: 14px;
  }
  .bars {
    padding: 8px 14px 14px;
  }
  dl {
    grid-template-columns: 110px 1fr;
  }
  .header-actions {
    gap: 6px;
  }
}

/* Compact navigation and a three-card overview. */
.sidebar {
  width: var(--sidebar-width);
  padding: 0 10px 12px;
}
.sidebar nav {
  margin-top: 10px;
  gap: 3px;
}
nav a {
  padding: 8px 10px;
}
nav a > .nav-text {
  font-size: 13px;
  width: auto;
}
/* a tab's count on its sidebar link (app.js loadNavBadges: what waits on the viewer) */
nav a > .nav-badge {
  margin-left: auto;
  min-width: 18px;
  padding: 1px 6px;
  border-radius: 9px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
}
main {
  margin-left: var(--sidebar-width);
}
h1 {
  margin: 0;
}
/* Chinese: no tracking tuned for Latin: negative letter-spacing jams the characters together, positive spreads them
   apart. */
html:lang(zh) :is(h1, h2, h3, h4, .brand, .breadcrumb strong, .research-headline strong, .launch-group, .chat-sec) {
  letter-spacing: 0; }
/* Chinese: the CJK faces above are heavier than DM Sans at the same number, so Latin beside Chinese (part numbers,
   product names, English words) reads light. Raise only the Latin: each rule stays inside the CJK face's band
   (600+ and 450-599 above), so the Chinese glyphs keep their face and only DM Sans gets heavier. */
html:lang(zh) :is(h1, h2, h3, h4, .brand, .tile-title, .block-head h3, .create-tile-label, .tab-strip > a[aria-current='page'],
    .tab-strip > a > .tab-width, .nav-project.active, .whoami b, .toast-text, .setup-field > span, .picture-answers summary,
    .select-list li.select-group, .spend-by button[aria-pressed='true'], .files-preview h3) { font-weight: 700; }
html:lang(zh) :is(nav a, .tile-grid[data-size='s'] .tile-title) { font-weight: 560; }
html:lang(zh) :is(nav a.active, nav a.nav-project.active) { font-weight: 700; }
html:lang(zh) .nav-group:has(a.nav-project.active) > a.active { font-weight: 560; }
html:lang(zh) { --button-weight: 590; }
html:lang(zh) :is(.project-link, .tile-value, .project-table th) { font-weight: 590; }
html:lang(zh) :is(button.tile-title, button.nav-heading, button.pipeline-open, .tab-strip > a > .tab-width) { font-weight: 700; }
@media (max-width: 720px) {
  main {
    margin-left: 0;
  }
  .brand {
    padding: 0 6px;
  }
  .header-actions {
    gap: 6px;
  }
}

/* Frontend-only import portal. */
.import-preview {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-top: 14px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--soft);
}
.import-preview .pill {
  background: var(--soft);
  white-space: nowrap;
}
.import-preview p {
  font-size: 11px;
}
.import-steps {
  display: flex;
  gap: 24px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}
.import-steps span {
  padding: 0 0 10px;
}
.import-steps .current {
  color: var(--green);
  border-bottom: 2px solid var(--green);
  font-weight: 600;
}
.import-type-section {
  margin-bottom: 20px;
}
.import-type-section h2 {
  margin-bottom: 10px;
}
.import-types {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
.import-type {
  text-align: left;
  padding: 16px 16px;
  position: relative;
  background: var(--surface);
  border-radius: 9px;
  border: 1px solid var(--border);
  padding-left: 62px;
}
.import-type strong {
  display: block;
  font-size: 13px;
  margin-bottom: 5px;
}
.import-type p {
  font-size: 11px;
  font-weight: 400;
}
.import-type.selected {
  border-color: var(--border-strong);
  background: var(--soft);
  box-shadow: 0 0 0 1px var(--focus-soft);
}
.import-type-icon {
  position: absolute;
  top: 20px;
  left: 20px;
  font-size: 24px;
  color: var(--secondary);
}
.import-panel > h2 {
  margin-bottom: 16px;
}
.import-drop {
  cursor: pointer;
  border: 1.5px dashed var(--border);
  border-radius: 9px;
  background: var(--surface-alt);
  display: flex;
  align-items: center;
  flex-direction: column;
  padding: 24px 14px;
  gap: 8px;
  text-align: center;
}
.import-drop.dragging {
  border-color: var(--green);
  background: var(--soft);
}
.upload-icon {
  display: inline-flex;
  color: var(--secondary);
  border-radius: 10px;
  background: var(--soft);
  padding: 8px 10px;
}
.import-drop p {
  font-size: 11px;
}
.import-drop small {
  font-size: 10px;
  line-height: 1.7;
  color: var(--secondary);
}
.import-file-empty {
  text-align: center;
  padding: 22px 0 7px;
  color: var(--secondary);
}
.import-file-empty strong {
  font-size: 11px;
  font-weight: 500;
}
.import-file-empty p {
  font-size: 10px;
  margin-top: 5px;
}
.import-file-count {
  font-size: 11px;
  color: var(--secondary);
  margin: 16px 0 8px;
}
.import-files {
  border: 1px solid var(--border);
  border-radius: 7px;
}
.import-files th,
.import-files td {
  padding: 8px 10px;
  font-size: 10px;
}
.import-files td:first-child {
  max-width: 210px;
}
.import-files tr {
  cursor: default;
}
.import-files button {
  padding: 2px 7px;
}
.import-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 12px;
}
.import-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  font-size: 11px;
  color: var(--secondary);
}
.import-field:has(textarea) {
  grid-column: 1/-1;
}
.import-field input,
.import-field select,
.import-field textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
.import-field input::placeholder,
.import-field textarea::placeholder {
  color: var(--muted);
}
.import-field textarea {
  resize: vertical;
  min-height: 78px;
}
.required {
  color: var(--warning);
}
.import-actions {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 18px;
}
.import-actions button {
  font-size: 12px;
}
.import-actions .primary:disabled {
  background: var(--soft);
  border-color: var(--border);
  color: var(--secondary);
  opacity: 1;
}
.import-footnote {
  font-size: 10px;
  color: var(--muted);
  margin-top: 14px;
}
.import-review-details {
  margin: 10px 0 22px;
  grid-template-columns: 180px 1fr;
}
.import-review-details dt,
.import-review-details dd {
  padding: 8px 0;
}
@media (max-width: 1050px) {
  .import-files td:first-child {
    max-width: 300px;
  }
}
@media (max-width: 720px) {
  .import-types {
    grid-template-columns: 1fr;
  }
  .import-preview {
    align-items: flex-start;
    flex-direction: column;
    gap: 6px;
  }
  .import-fields {
    grid-template-columns: 1fr;
  }
  .import-review-details {
    grid-template-columns: 110px 1fr;
  }
}

.import-record-counts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin: 20px 0 22px;
}
.import-record-counts > div {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  background: var(--surface-alt);
}
.import-record-counts span {
  display: block;
  color: var(--secondary);
  font-size: 12px;
}
.import-record-counts strong {
  display: block;
  font-size: 29px;
  font-weight: 500;
  margin: 10px 0;
  color: var(--secondary);
}
.import-record-counts small {
  font-size: 10px;
  color: var(--secondary);
}
@media (max-width: 600px) {
  .import-record-counts {
    grid-template-columns: 1fr;
  }
}

.import-preview-table {
  margin-top: 16px;
}
.import-preview-table .table-scroll {
  max-height: 390px;
}
.import-record-counts strong {
  color: var(--text);
}
.import-panel > .import-footnote {
  margin-bottom: 16px;
}
.import-issues {
  margin: 16px 0 0;
  max-height: 250px;
  overflow: auto;
}
.import-issues p {
  color: inherit;
  margin: 5px 0 10px;
}
.import-panel + .import-actions {
  align-items: center;
}

/* User observations are kept separate from source-derived record fields. */
.record-issues {
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: 14px;
  margin: 0 0 18px;
  background: var(--surface-alt);
}
.record-alternates {
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: 14px 18px;
  margin: 0 0 22px;
}
.record-alternates h3 {
  margin: 0 0 6px;
  font-size: 14px;
}
.record-alternates > p {
  font-size: 12px;
  line-height: 1.6;
  margin: 6px 0;
}
.alternate-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: grid;
  gap: 8px;
}
.alternate-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  font-size: 13px;
}
.alternate-list li > div {
  flex-basis: 100%;
  font-size: 12px;
}
.issue-heading {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.issue-heading h3 {
  margin: 0;
  font-size: 14px;
}
.record-issues > p {
  font-size: 12px;
  line-height: 1.6;
  margin: 8px 0;
}
#issue-form label {
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-size: 12px;
  margin: 10px 0;
}
#issue-form select,
#issue-form textarea {
  font: inherit;
  width: 100%;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
}
#issue-form textarea {
  resize: vertical;
}
.issue-item {
  border-top: 1px solid var(--border);
  padding: 10px 0 4px;
}
.issue-meta {
  font-size: 11px;
  color: var(--secondary);
}
.issue-body {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 6px 0;
  line-height: 1.6;
  font-size: 13px;
}
.issue-item small,
#issue-notice {
  font-size: 11px;
  color: var(--secondary);
}
#issue-notice:not(:empty) {
  padding: 10px 0;
}
#issue-list > p {
  font-size: 12px;
  margin-top: 12px;
}

.title-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
/* Provenance filters remain usable as the purchase view gains source fields. */
.sheet-toolbar {
  flex-wrap: wrap;
}
.sheet-toolbar #filters {
  flex-wrap: wrap;
  min-width: 0;
}
.admin-nav {
  margin-top: 16px;
}
#admin-portal p {
  line-height: 1.5;
  color: var(--muted);
}
#admin-portal h2 {
  margin: 0 0 6px;
  line-height: 1.4;
}
/* the token sets a framed button's height; a quiet one (launch tile, switch, legend) keeps its own padding but never
   drops below the compact token */
#admin-portal button {
  font-size: 12px;
  padding-inline: 10px;
  line-height: 1.3;
  min-height: var(--control-h-sm);
}
#admin-portal .admin-job {
  padding: 14px 16px;
}
#admin-portal .admin-job h3 {
  font-size: 13px;
  line-height: 1.45;
  margin: 0 0 6px;
}
#admin-portal .admin-job > p + p {
  margin-top: 4px;
}
#admin-portal .admin-job > details {
  margin-top: 8px;
}
#admin-portal .admin-job > details + p {
  margin-top: 6px;
}
#admin-portal .admin-job summary {
  cursor: pointer;
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
}
#admin-portal .admin-job .notice {
  margin: 8px 0;
}
#admin-portal pre {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 340px;
  overflow: auto;
  font-size: 12px;
  margin: 8px 0 0;
  padding: 10px;
  background: var(--surface-alt);
  border-radius: 6px;
  line-height: 1.5;
}
#admin-portal .admin-job > input {
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  margin: 8px 8px 0 0;
}
#admin-portal .admin-job > button {
  margin: 8px 6px 0 0;
}

.language-toggle {
  display: inline-flex;
  align-items: center;
  padding: 3px;
  gap: 2px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface-alt);
}
.language-toggle button {
  border: 0;
  background: transparent;
  border-radius: 6px;
  padding: 6px 10px;
  color: var(--muted);
}
.language-toggle button[aria-pressed='true'] {
  background: var(--green);
  color: var(--on-accent);
}
.language-toggle button:hover:not([aria-pressed='true']) {
  background: var(--soft);
  color: var(--green);
}
@media (max-width: 720px) {
  .admin-nav {
    margin-top: 0;
  }
}
.source-readiness {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin: 10px 0;
}
.research-fields {
  display: grid;
  grid-template-columns: 140px;
  gap: 14px;
  margin: 12px 0;
}
.research-fields label {
  display: grid;
  align-content: start;
  gap: 6px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--secondary);
}
.research-fields input,
.research-fields textarea {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 6px 8px;
  background: var(--surface);
  font: inherit;
  color: inherit;
}
.research-fields textarea {
  height: 56px;
  min-height: 56px;
  resize: vertical;
}
.research-checks {
  display: grid;
  gap: 10px;
  margin: 10px 0;
}
.research-sources {
  display: flex;
  gap: 16px;
}
.research-options {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px 16px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.research-checks label {
  display: flex;
  align-items: flex-start;
  font-size: 12px;
  gap: 7px;
  line-height: 1.45;
}
#admin-portal .research-checks input {
  margin: 2px 0 0;
  flex-shrink: 0;
  accent-color: var(--green);
}
#admin-portal .research-panel > p:last-of-type {
  font-size: 11px;
  line-height: 1.5;
  margin-top: 10px;
}
.research-panel > button {
  margin-top: 12px;
}
.research-results {
  max-height: 300px;
  overflow: auto;
  margin: 8px 0;
}
.research-results td {
  padding: 8px 12px;
}
.research-results td button + button {
  margin-left: 6px;
}
@media (max-width: 900px) {
  .research-options {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 720px) {
  .research-fields {
    grid-template-columns: 1fr;
  }
  #admin-portal .admin-job {
    padding: 12px 14px;
  }
}

.usage-heading {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 14px;
  margin-bottom: 6px;
}
#admin-portal .usage-heading h2 {
  margin: 0;
}

/* One tab strip everywhere (#144, #147: projects.js WB.tabStrip): a section's sub-tabs, a project's tabs and their
   second row, Admin's sections, a work order's. Minimalist file-folder tabs on one line: the current tab is outlined
   and "on top", its bottom open onto the page below it. The line is the strip's background, so the current tab's own
   background covers it (no negative margin, which a scrolling strip would clip). Stacked folders and sub-tabs: "The
   standard" at the end of this file. */
.tab-strip { display: flex; align-items: flex-end; gap: 2px; margin: 0 0 14px; padding: 4px 0 0; overflow-x: auto;
  white-space: nowrap; scrollbar-width: none; background: linear-gradient(to top, var(--border-strong) 1px, transparent 1px); }
.tab-strip > a { position: relative; flex-shrink: 0; display: inline-grid; gap: 0; padding: 5px 14px;
  border: 1px solid transparent; border-bottom: 0; border-radius: 6px 6px 0 0; color: var(--muted); font-size: 13px;
  text-decoration: none; }
.tab-strip > a:hover { color: var(--text); }
.tab-strip > a[hidden] { display: none; }
.tab-strip > a[aria-current='page'] { background: var(--canvas); border-color: var(--border-strong); color: var(--text);
  font-weight: 600; }
/* each link reserves its bold width with a hidden bold copy of its label (WB.tabStrip), so tabs never move (#98);
   its ::after stays free for the New/Updated tag (#107) */
.tab-strip > a > .tab-width { font-size: inherit; width: auto; font-weight: 600; height: 0; visibility: hidden; overflow: hidden; }
/* a section's sub-tabs sit close under the title bar, leaving the room to the page (#147) */
#subtabs { margin-top: -12px; }
/* Access page and signed-in badge */
.whoami {
  font-size: 12px;
  color: var(--muted);
  margin-right: 4px;
  white-space: nowrap;
}
.whoami b {
  color: var(--text);
  font-weight: 600;
}
/* who is signed in (access.js): name, home company, role; cut short before the page's title is (#143), but only the
   text: Sign out stays whole after it (#178) */
#settings-menu-list > .whoami { display: inline-flex; align-items: baseline; flex-shrink: 1; min-width: 0; max-width: min(60vw, 420px); }
#settings-menu-list > .whoami > .whoami-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
#settings-menu-list > .whoami > a { flex-shrink: 0; }
.whoami.warning {
  color: var(--warning);
}
#access-portal .access-panel h2 {
  margin: 0 0 6px;
}
#access-portal p {
  line-height: 1.5;
}
#access-portal table {
  width: 100%;
  border-collapse: collapse;
}
#access-portal th,
#access-portal td {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
#access-portal select,
#access-portal input {
  font: inherit;
  padding: 6px 6px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
}
.access-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.access-form {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.access-form label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  color: var(--muted);
}
.access-form h3,
#access-portal h3 {
  width: 100%;
  margin: 12px 0 6px;
  font-size: 13px;
}
.access-form h3 {
  margin: 0;
}
.access-form .form-note {
  width: 100%;
  margin: 0;
  font-size: 12px;
}
.access-toolbar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin: 8px 0 6px;
}
.access-toolbar input[type='search'] {
  min-width: min(240px, 100%);
  flex: 1;
}
.access-toolbar .check {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
}
#access-portal tbody tr {
  cursor: default;
}
#access-portal tr.inactive td {
  opacity: 0.6;
}
.user-who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
}
.user-who .muted {
  color: var(--muted);
  font-size: 12px;
}
.pill.muted-pill {
  color: var(--muted);
}
.notice.success {
  border-left-color: var(--accent);
  color: var(--green);
}
#access-portal button.danger {
  color: var(--danger);
}
#access-portal button:disabled {
  opacity: 0.45;
  cursor: default;
}
#access-portal.busy {
  opacity: 0.7;
  pointer-events: none;
}

/* Research controls */
.research-controls .research-box {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 12px 0;
}
.research-controls legend {
  font-weight: 600;
  padding: 0 6px;
}
.research-action {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 6px;
  border-radius: 6px;
  cursor: pointer;
}
.research-action.selected {
  background: var(--soft);
}
.research-action b {
  font-weight: 600;
  min-width: 210px;
}
.research-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin: 6px 0;
}
.research-filter > span:first-child {
  min-width: 170px;
  font-size: 12px;
}
.research-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 3px 12px 3px 0;
  font-size: 13px;
}
.research-controls input[type='number'],
.research-controls input:not([type]),
.research-controls select {
  font: inherit;
  padding: 6px 6px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
}
.research-footer {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}
.research-estimate p {
  margin: 6px 0 0;
}
/* Research part selection: filter chips with live counts beside the matching parts. */
.research-matches {
  position: sticky;
  top: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-top: 12px;
  background: var(--surface-alt);
  min-width: 0;
}
.research-matches > h3,
.research-section h3 {
  font-size: 12px;
  font-weight: 600;
  margin: 0 0 6px;
  color: var(--text);
}
.research-section {
  margin: 4px 0 12px;
}
.research-section > p {
  font-size: 11px;
  margin: 0 0 6px;
}
.research-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.research-chips.sub {
  margin-top: 6px;
  max-height: 150px;
  overflow: auto;
  padding: 2px;
}
.research-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  background: var(--surface);
}
.research-chip b {
  font:
    500 10px var(--font-mono);
  color: var(--secondary);
  background: var(--soft);
  border-radius: 999px;
  padding: 1px 6px;
}
.research-chip i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}
.research-chip.on {
  background: var(--selected-bg);
  border-color: var(--border-strong);
  color: var(--text);
}
.research-chip.on b {
  background: var(--surface);
  color: var(--text);
}
.research-chip.empty {
  opacity: 0.45;
}
.research-find {
  margin-top: 6px;
}
.research-find input {
  width: min(260px, 100%);
}
.research-links {
  display: flex;
  gap: 12px;
  margin-top: 4px;
}
.link-button {
  border: 0;
  background: none;
  padding: 2px 0;
  color: var(--green);
  font-size: 12px;
}
.link-button:hover {
  background: none;
  text-decoration: underline;
}
.research-more {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.research-headline {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 10px;
}
.research-headline strong {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.5px;
  color: var(--text);
}
.research-headline .pill {
  align-self: flex-start;
}
.research-bars {
  display: grid;
  gap: 8px;
  margin: 6px 0 12px;
}
.research-bar > span {
  font-size: 11px;
}
.research-track {
  display: flex;
  height: 10px;
  border-radius: 5px;
  overflow: hidden;
  background: var(--soft);
  margin: 4px 0;
}
.research-track i {
  display: block;
  height: 100%;
}
.research-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 11px;
  color: var(--secondary);
}
.research-legend span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.research-legend i,
.research-list .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  margin-right: 5px;
}
.research-list {
  max-height: 460px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
}
.research-list td,
.research-list th {
  padding: 6px 8px;
}
.research-list td small {
  display: block;
  font-size: 10px;
  color: var(--secondary);
  white-space: normal;
  max-width: 260px;
  margin-top: 2px;
}
.research-list td:nth-child(4),
.research-list td:nth-child(5) {
  white-space: normal;
  max-width: 150px;
}
.research-list tr {
  cursor: default;
}
.research-list td {
  max-width: 220px;
}
.research-list tr.later td {
  color: var(--muted);
}
.research-list tr.later .mono {
  color: var(--muted);
}
.research-divider td {
  background: var(--surface-alt);
  color: var(--secondary);
  font-size: 11px;
  text-align: center;
  padding: 6px;
}
.research-matches .research-footer {
  margin-top: 10px;
}
@media (max-width: 1200px) {
  .research-matches {
    position: static;
  }
}
.research-controls .research-action b {
  min-width: 170px;
}
.db-bars {
  margin-top: 12px;
  max-width: 760px;
}
.db-imports {
  border: 1px solid var(--border);
  border-radius: 7px;
  margin-top: 8px;
}
.db-imports tr {
  cursor: default;
}
.db-imports td {
  padding: 8px 12px;
}
/* Admin → Dev */
#admin-portal .dev-panel {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
#admin-portal .dev-panel .notice {
  margin: 0;
}
.dev-bench-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 10px 0;
}
.dev-bench-form input:not([type]) {
  min-width: 220px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 7px;
}
.dev-bench-result {
  max-height: 520px;
  overflow: auto;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 10px;
  font:
    11px/1.5 var(--font-mono);
  white-space: pre-wrap;
  color: var(--text);
}
a.button {
  display: inline-block;
  border-radius: 7px;
  padding: 8px 14px;
  font-weight: 550;
}
.import-badge {
  font-size: 11px;
  padding: 5px 8px;
  border-radius: 12px;
  background: var(--soft);
  color: var(--green);
  font-weight: 600;
  white-space: nowrap;
}
.import-activity {
  font-size: 11px;
  color: var(--secondary);
  align-self: center;
  margin-left: auto;
}

/* until the first data answer that is not a 401 (app.js); a 401 sends the page to /signin while still hidden */
body.auth-pending > .sidebar,
body.auth-pending > main,
body.auth-pending .helper-root {
  visibility: hidden;
}
#signin-screen {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 20px 14px;
  background:
    radial-gradient(60rem 30rem at 15% 10%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 70%),
    radial-gradient(50rem 30rem at 90% 95%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 70%);
}
/* the card sits in front of Spot; she peeks over its top edge every few seconds */
.signin-stage {
  position: relative;
  width: min(420px, 100%);
}
.signin-peek {
  position: absolute;
  top: 0;
  right: 42px;
  z-index: 0;
  line-height: 0;
  transform: translateY(0);
  animation: signin-peek 11s ease-in-out 1.5s infinite;
}
.signin-peek .spot-art { filter: drop-shadow(0 2px 2px var(--shadow)); }
@keyframes signin-peek {
  0%, 62%, 100% { transform: translateY(0); }
  68%, 86% { transform: translateY(-64%); }
  74% { transform: translateY(-56%); }
  80% { transform: translateY(-64%) rotate(-4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .signin-peek { animation: none; transform: translateY(-40%); }
}
.signin-card {
  position: relative;
  z-index: 1;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 30px 26px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 1px 2px var(--shadow-soft), 0 18px 60px var(--shadow-soft);
}
.signin-card .brand {
  padding: 0;
}
.signin-card h1 {
  font-size: 26px;
  margin: 10px 0 0;
}
.signin-card .signin-lede {
  margin: 0;
  color: var(--secondary);
}
.signin-card .signin-refused {
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--warning-border);
  border-radius: 6px;
  background: var(--warning-bg);
  overflow-wrap: anywhere;
}
.signin-card a.button {
  text-align: center;
  margin-top: 6px;
}
.signin-top {
  display: flex;
  flex-wrap: wrap;            /* three languages: the toggle goes under the name rather than past a phone's edge */
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}
.signin-top .language-toggle button {
  min-width: 0;
  padding: 6px 8px;
  font-size: 11px;
}

/* Report an issue (bug_report.js) */
.header-actions button.report-issue {
  background: transparent;
  border-color: var(--border) var(--border) var(--keycap-edge);
  color: var(--secondary);
  font-weight: 600;
  height: 30px;
  padding: 0 10px;
  font-size: 12px;
}
.header-actions button.report-issue:hover {
  background: var(--soft);
  border-color: var(--border-strong) var(--border-strong) var(--keycap-edge);
}
.report-issue span[aria-hidden] {
  margin-right: 3px;
}
/* the type: a segmented group of radio buttons */
.bug-types {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 2px;
  margin-bottom: 8px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface-alt);
}
.bug-types label {
  position: relative;
  flex: 1 1 auto;
  text-align: center;
  padding: 5px 8px;
  border-radius: 5px;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
}
.bug-types input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.bug-types label:hover { color: var(--text); }
.bug-types label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
/* the description: a plain text box (bug_report.js writer) */
.bug-writer {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 132px;
  max-height: 50vh;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  resize: vertical;
}
.bug-writer:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.bug-writer::placeholder { color: var(--muted); }
/* a field's frame and 5px corners, as every text box has in the standard (#150.2); the id outranks the square reset
   ("Components") */
#bug-dialog .bug-writer { border-color: var(--border-strong); border-radius: 5px !important; }
/* Attach a file, then Point at it on the page, on one row of plain buttons (#150) */
.bug-attach-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.bug-attach-row .bug-prompt-row button {
  font-size: 11px;
  padding: 6px 10px;
}
.bug-notice {
  font-size: 12px;
  color: var(--secondary);
  margin-top: 10px;
}
.bug-notice.error {
  color: var(--danger);
}
/* the prompts above the text box: point at wrong data, or ask Spot a question */
.bug-prompt {
  background: var(--soft);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 8px;
  font-size: 13px;
}
.bug-prompt[hidden] {
  display: none;
}
.bug-prompt > p {
  margin: 0 0 8px;
}
.bug-prompt-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.bug-prompt-row button {
  font-size: 12px;
  padding: 6px 12px;
}
.bug-prompt .bug-prompt-note {
  font-size: 11px;
  color: var(--secondary);
  margin: 8px 0 0;
}
.bug-prompt-row .bug-prompt-note {
  margin: 0;
}
.bug-marked {
  font-size: 12px;
  font-weight: 600;
  overflow-wrap: anywhere;
}
/* pointing mode: a crosshair, an outline on what's under the pointer, and a bar saying what to do */
body.bug-pointing,
body.bug-pointing * {
  cursor: crosshair !important;
}
.bug-point-box {
  position: fixed;
  z-index: 2147483000;
  pointer-events: none;
  outline: 3px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 4px;
}
.bug-point-bar {
  position: fixed;
  z-index: 2147483001;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: 0 8px 24px var(--shadow);
  padding: 8px 10px 8px 14px;
  font-size: 13px;
  font-weight: 600;
}
.bug-point-bar,
.bug-point-bar * {
  cursor: default !important;
}
.bug-point-bar button {
  cursor: pointer !important;
  font-size: 12px;
  padding: 5px 10px;
}
.bug-sent p.bug-sent-lead {
  font-size: 15px;
  font-weight: 600;
  margin-bottom: 6px;
}
.bug-sent p {
  font-size: 13px;
  margin-bottom: 12px;
}
.dialog-actions .bug-shortcut {
  margin-right: auto;
  align-self: center;
  font-size: 11px;
  color: var(--secondary);
}

/* Spot's celebration after a report is sent (celebrate.js): a click-through layer above the dialog */
.celebrate {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: hidden;
  pointer-events: none;
  z-index: 2147483000;
}
.celebrate canvas,
.celebrate-clip {
  position: absolute;
  left: 0;
  width: 100%;
}
.celebrate canvas {
  top: 0;
  image-rendering: pixelated;
}
.celebrate-clip {
  overflow: hidden;
}
.celebrate-actor {
  position: absolute;
  transform-origin: 50% 100%;
  will-change: transform;
}
.celebrate-actor .spot-art {
  filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.25));
}
/* the medal on every 50th report (pixel art, celebrate.js) */
.celebrate-medal {
  position: absolute;
  transform-origin: 50% 100%;
  will-change: transform;
}
.celebrate-medal svg {
  display: block;
  filter: drop-shadow(0 4px 0 rgba(0, 0, 0, 0.2));
}
.bug-sent p.bug-milestone {
  display: flex;
  align-items: center;
  gap: 10px;
  width: fit-content;
  margin: 4px 3px 16px;
  padding: 8px 12px;
  background: #fbe6a2;
  color: #5a420d;
  font-weight: 700;
  box-shadow: 0 -3px 0 #8a6a1c, 0 3px 0 #8a6a1c, -3px 0 0 #8a6a1c, 3px 0 0 #8a6a1c, 0 6px 0 rgba(0, 0, 0, 0.15);
}
.bug-milestone .spot-art {
  flex: none;
}
.celebrate-bubble {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
}
.celebrate-bubble svg {
  display: block;
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, 0.18));
  transform-origin: 50% 100%;
  animation: celebrate-pop 0.24s steps(3, end) both;
}
/* text the pixel font lacks (Chinese): plain text in the same notched pixel frame */
.celebrate-bubble span {
  display: block;
  background: #fbf9f4;
  color: #2c2c35;
  font-size: 15px;
  font-weight: 800;
  padding: 5px 12px;
  white-space: nowrap;
  box-shadow: 0 -3px 0 #4a4640, 0 3px 0 #4a4640, -3px 0 0 #4a4640, 3px 0 0 #4a4640, 0 6px 0 rgba(0, 0, 0, 0.18);
  animation: celebrate-pop 0.24s steps(3, end) both;
}
@keyframes celebrate-pop {
  from { transform: scale(0.4); }
  to { transform: scale(1); }
}
@media (max-width: 720px) {
  .header-actions button.report-issue span:last-child {
    display: none;
  }
}

/* Research: batch size next to Run research; the report's example buttons */
.research-controls .research-footer {
  align-items: center;
  gap: 8px;
}
#admin-portal .research-controls .research-footer > button,
#admin-portal .research-controls .research-footer input {
  margin-block: 0;
}
.research-controls .research-footer label {
  display: flex;
  align-items: center;
  gap: 6px;
}
.research-controls .research-footer input {
  width: 90px;
}
#admin-portal .research-controls .research-footer input[type='checkbox'] {
  width: auto;
  height: auto;
  margin: 0;
}
.research-controls button.link {
  padding: 3px 6px;
  margin: 2px 4px 2px 0;
  font-size: 11px;
}

/* Admin → Tool calls */
#admin-portal .toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0;
}
#admin-portal .toolbar input[type='search'] {
  flex: 1 1 220px;
}
#admin-portal tr.clickable {
  cursor: pointer;
}
#admin-portal tr.clickable:hover td {
  background: var(--soft);
}
#admin-portal tr.clickable td:nth-child(4) {
  max-width: 420px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tool-calls-table th { padding: 10px 12px; }
.tool-calls-table td { padding: 6px 12px; line-height: 1.5; }
.tool-calls-table th:nth-child(5),
.tool-calls-table th:nth-child(6),
.tool-calls-table td:nth-child(5),
.tool-calls-table td:nth-child(6) {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.tool-calls-table tr:not(.clickable) { cursor: default; }
/* Admin → MCP Limits (mcp_limits.js): usage bars, the per-limit list and the 30-day trend */
.mcp-bar { display: inline-block; width: 120px; height: 8px; border-radius: 4px; background: var(--soft); vertical-align: middle; overflow: hidden; }
.mcp-bar i { display: block; height: 100%; background: var(--chart-1); }
.mcp-bar.near i { background: var(--warning); }
.mcp-bar.over i { background: var(--danger); }
.mcp-usage { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.mcp-usage li { display: grid; grid-template-columns: minmax(160px, 220px) 120px auto; gap: 10px; align-items: center; }
.mcp-trend { display: inline-flex; align-items: flex-end; gap: 1px; height: 24px; }
.mcp-trend i { display: block; width: 4px; background: var(--chart-2); }
.mcp-trend i.over { background: var(--danger); }
.mcp-limits input[type='number'] { width: 9em; }
.mcp-panel { overflow-x: auto; }
@media (max-width: 720px) { .mcp-usage li { grid-template-columns: 1fr; gap: 2px; } }
.tool-calls-table .tool-calls-empty td { padding: 20px 12px; color: var(--muted); }
.tool-calls-table .tool-calls-empty:hover td { background: transparent; }
.tool-calls-table .tool-call-detail td {
  padding: 12px;
  white-space: normal;
  overflow: visible;
  background: var(--surface-alt);
}
.tool-calls-table .tool-call-detail pre { max-height: 360px; overflow: auto; }
.tool-calls-panel > button { margin: 10px 12px; }

/* (i) tips: explanations shown on hover / keyboard focus */
.info-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  margin-left: 6px;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--muted);
  font:
    italic 600 10px Georgia,
    serif;
  vertical-align: middle;
  cursor: help;
  position: relative;
}
[data-tip] {
  position: relative;
}
[data-tip]:hover::after,
[data-tip]:focus::after {
  content: attr(data-tip);
  position: absolute;
  /* app.js fitTip moves it left by what would run past the window's right edge (#613) */
  left: var(--tip-shift, 0px);
  top: calc(100% + 6px);
  z-index: 60;
  box-sizing: border-box;
  width: max-content;
  max-width: min(360px, calc(100vw - 16px));
  padding: 6px 8px;
  border-radius: 6px;
  background: var(--text);
  color: var(--surface);
  font:
    400 12px/1.45 var(--font-sans);
  font-style: normal;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  white-space: pre-line;
  box-shadow: 0 6px 18px var(--shadow);
  pointer-events: none;
}
.small {
  font-size: 12px;
}

/* Admin: steady layout while sections switch */
#admin-shell {
  min-height: 70vh;
}

/* Admin → Jobs → Ongoing Job: full width, progress and a live printout */
#admin-portal .job-live {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
#admin-portal .job-live.status-running,
#admin-portal .job-live.status-queued {
  border-color: var(--border);
}
#admin-portal .job-live.status-cancelling {
  border-color: var(--warning-border);
}
.job-live-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
}
#admin-portal .job-live-head h2 {
  margin: 0;
}
.job-live-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  font-size: 13px;
}
.job-live-head .job-actions {
  margin-left: auto;
}
#admin-portal .job-live p {
  margin: 0;
}
.job-progress {
  height: 10px;
  border-radius: 5px;
  background: var(--soft);
  overflow: hidden;
}
.job-progress-fill {
  height: 100%;
  background: var(--green);
  transition: width 0.2s ease;
}
.job-live.status-cancelling .job-progress-fill {
  background: var(--warning);
}
.job-live-figures {
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.job-live-now {
  font:
    12px/1.4 var(--font-mono);
  color: var(--text);
}
.job-live-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.job-count {
  font-size: 11px;
  font-weight: 600;
  border-radius: 4px;
  padding: 2px 6px;
  background: var(--soft);
  color: var(--text);
}
.job-count.st-completed,
.job-count.st-priced {
  background: var(--soft);
  color: var(--green);
}
.job-count.st-partial,
.job-count.st-near_match,
.job-count.st-conflicting,
.job-count.st-not_found,
.job-count.st-no_offer,
.job-count.st-not_asked {
  background: var(--warning-bg);
  color: var(--warning);
}
.job-count.st-failed {
  background: var(--danger-bg);
  color: var(--danger);
}
.job-log-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
}
.job-log-bar > span {
  margin-right: auto;
}
#admin-portal .job-log-bar button,
.job-log-bar a {
  font-size: 11px;
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  font-weight: 550;
}
.job-log {
  height: min(60vh, 560px);
  overflow: auto;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-alt);
  font:
    12px/1.55 var(--font-mono);
  color: var(--text);
  user-select: text;
}
.job-row .job-log {
  height: auto;
  max-height: 320px;
}
.log-line {
  display: flex;
  gap: 8px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.log-line time {
  flex: none;
  color: var(--muted);
}
.log-line.lvl-ok span {
  color: var(--accent);
}
.log-line.lvl-warn span {
  color: var(--warning);
}
.log-line.lvl-error span {
  color: var(--danger);
  font-weight: 600;
}

/* Admin → Jobs */
.job-pane .job-stats {
  flex: 1;
}
.job-pane .job-stats strong {
  display: block;
  font-size: 18px;
  font-weight: 550;
  margin-bottom: 6px;
}
.job-pane .research-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.job-pane .research-footer {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
}
#admin-portal .job-history h2 {
  margin: 0 0 6px;
}
#admin-portal .user-tasks h2 {
  margin: 0 0 6px;
}
#admin-portal .user-tasks h3 {
  margin: 10px 0 4px;
  font-size: 12px;
}
#admin-portal .user-tasks p {
  margin: 4px 0;
  font-size: 12px;
}
#admin-portal .user-tasks td {
  padding: 6px 10px;
  white-space: normal;
}
.job-row {
  border-bottom: 1px solid var(--border);
  font-size: 12px;
}
.job-row:last-child {
  border-bottom: 0;
}
.job-row > summary {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) 85px 155px minmax(0, 1fr) auto;
  gap: 8px;
  align-items: center;
  padding: 7px 0;
  cursor: pointer;
  list-style: none;
}
.job-row > summary::-webkit-details-marker {
  display: none;
}
.job-row .job-outcome {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
}
.job-row .job-actions button {
  font-size: 11px;
  padding: 3px 6px;
}
.job-row.status-failed .job-status {
  color: var(--danger);
  background: var(--danger-bg);
}
.job-row.status-running .job-status,
.job-row.status-queued .job-status {
  color: var(--warning);
  background: var(--warning-bg);
}
.job-row .job-body {
  padding: 4px 0 10px;
}
.job-row .job-body pre {
  max-height: 320px;
  overflow: auto;
}

/* Admin → Database */
.db-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 12px;
  margin: 0;
  font-size: 13px;
}
.db-list dt,
.db-list dd {
  padding: 0;
  border: 0;
}
.db-list dt {
  color: var(--muted);
  display: flex;
  align-items: center;
}
/* values and their notes wrap normally (not dd's pre-wrap): a note that wraps in the narrower pane, as when the floating
   controls keep their column free (#143), would leave its line-end space hanging past the right-aligned edge */
.db-list dd {
  margin: 0;
  min-width: 0;
  text-align: right;
  white-space: normal;
}
.db-list dd strong {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.db-list dd small {
  display: block;
  font-size: 11px;
  color: var(--muted);
}
/* Admin → Database: download links beside storage sizes and imports */
.db-download {
  font-size: 11px;
  font-weight: 500;
  margin-left: 6px;
}
.db-download:hover {
  text-decoration: underline;
}
.db-pane .db-action {
  border-top: 1px solid var(--border);
  padding-top: 8px;
}
#admin-portal .db-pane .db-action h3 {
  margin: 0 0 6px;
  font-size: 13px;
  display: flex;
  align-items: center;
}
.db-action-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.db-action-row input:not([type='checkbox']) {
  width: 120px;
}

/* Admin → Connections */
.conn-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  margin: 0 0 12px;
  font-size: 13px;
}
.conn-summary .conn-stat b {
  font-variant-numeric: tabular-nums;
}
.conn-summary .conn-stat.ok b {
  color: var(--green);
}
.conn-summary .conn-stat.attention b {
  color: var(--warning);
}
.conn-summary .conn-stat.failing b {
  color: var(--danger);
}
.conn-summary .conn-sep {
  color: var(--muted);
}
.conn-summary-actions {
  display: flex;
  gap: 6px;
  margin-left: auto;
}
#admin-portal .conn-group > h2 {
  margin: 0 0 6px;
  display: flex;
  align-items: center;
  font-size: 14px;
}
.conn-tile .conn-role {
  color: var(--muted);
  font-size: 11px;
}
.conn-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.conn-tile .conn-cost {
  color: var(--muted);
  overflow-wrap: anywhere;
}
.conn-chip {
  display: inline-flex;
  align-items: center;
  min-height: 20px;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface-alt);
  color: var(--secondary);
  font-size: 10px;
  line-height: 16px;
  white-space: nowrap;
}
.conn-reason {
  margin-top: 4px;
  font-size: 11px;
  color: var(--secondary);
}
.conn-failing .conn-reason {
  color: var(--danger);
}
/* a long reason: its first sentence on one line, the whole text folded out below it (#217) */
.conn-reason > summary { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.conn-reason > p { margin: 4px 0 0; overflow-wrap: anywhere; }
/* the tile's state in words, coloured by state (#218) */
.conn-state.is-ok { background: var(--soft); color: var(--green); }
.conn-state.is-attention { background: var(--warning-bg); color: var(--warning); }
.conn-state.is-failing { background: var(--danger-bg); color: var(--danger); }
.conn-bad {
  color: var(--danger);
}
.conn-test-chip {
  font: inherit;
  font-size: 11px;
  border: 0;
  border-radius: 4px;
  padding: 2px 6px;
  white-space: nowrap;
}
.conn-test-chip.pass {
  background: var(--soft);
  color: var(--green);
}
.conn-test-chip.fail {
  background: var(--danger-bg);
  color: var(--danger);
}
.conn-result {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
}
.conn-result.fail {
  border-color: var(--danger-bg);
  background: var(--danger-bg);
}
.conn-result pre {
  max-height: none;
  flex: 1;
  min-width: 0;
  margin: 0;
  font:
    12px/1.5 var(--font-mono);
  color: var(--text);
  padding: 0;
  background: none;
  border: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: text;
}
.conn-result-bar {
  display: flex;
  gap: 6px;
  flex: none;
}
.conn-result-bar button {
  font-size: 11px;
}
.conn-hosts {
  margin: 6px 0 8px;
  font-size: 12px;
}
.conn-hosts summary {
  cursor: pointer;
  font-weight: 600;
}
.conn-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 6px;
}
.conn-table th,
.conn-table td {
  text-align: left;
  padding: 5px 6px;
  border-bottom: 1px solid var(--border);
}
.conn-table td.conn-bad {
  color: var(--danger);
}

/* Admin → Users */
.access-me {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: 0 0 12px;
  font-size: 13px;
}
#access-portal .access-panel h2 {
  display: flex;
  align-items: center;
}
#access-portal td {
  padding: 5px 6px;
}
/* Admin / Users: buttons, fields and menus share one font size; their height is the token's */
#access-portal select,
#access-portal input {
  padding: 4px 8px;
  font-size: 13px;
  vertical-align: middle;
}
#access-portal tr.unsaved td {
  background: var(--soft);
}
#access-portal button:not(.chip-x) {
  padding-inline: 10px;
  font-size: 13px;
  min-height: var(--control-h-sm);
}
.activity-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; margin-bottom: 14px; }
.activity-columns { display: flex; align-items: flex-end; gap: 2px; height: 80px; border-bottom: 1px solid var(--border); }
.activity-column { flex: 1; height: 100%; display: flex; align-items: flex-end; }
.activity-column > div { width: 100%; background: var(--chart-1); border-radius: 2px 2px 0 0; min-height: 1px; }
.activity-axis { display: flex; justify-content: space-between; margin-top: 2px; }

.access-form h3 {
  display: flex;
  align-items: center;
}
.share-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.share-chip {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 12px;
  padding: 2px 4px 2px 6px;
  border-radius: 12px;
  background: var(--soft);
  color: var(--green);
}
.share-chip .chip-x {
  border: 0;
  background: transparent;
  color: inherit;
  padding: 0 4px;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
/* same size as the other fields in the row, and wide enough to read a company name (a flex item may shrink) */
#access-portal select.share-add,
#access-portal input.domain-add {
  min-width: 11em;
}
#access-portal select.share-add {
  color: var(--muted);
}
#admin-portal .job-row .job-actions button {
  padding: 2px 8px;
  font-size: 11px;
  line-height: 1.5;
  min-height: 0;
}
/* Status tags are Title Case (the job states come from the server in lower case) */
.job-status {
  text-transform: capitalize;
  white-space: nowrap;
  justify-self: start;
}

/* The upload box is the same full-width panel on Review and Import (Quotation: an Upload tile, #162). */
.upload-panel > h2 {
  margin-bottom: 16px;
}
/* File Annotate: one small panel per supported file type, above the drop box; the panel is as wide as the tiles
   (#158), and they wrap on a narrow window */
.review-upload { width: fit-content; }   /* the width of the file-type row, not the page (#158) */
.review-upload > h2 { width: 0; min-width: 100%; }   /* the sentence wraps to that width instead of setting it */
.file-types {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 14px;
}
.file-type {
  width: 130px;
  display: grid;
  justify-items: center;
  gap: 4px;
  padding: 12px 8px;
  border: 1px solid var(--border);
  border-radius: 9px;
  text-align: center;
}
.file-type-icon {
  color: var(--secondary);
  background: var(--soft);
  border-radius: 10px;
  padding: 7px;
  line-height: 0;
}
.file-type-icon svg {
  width: 24px;
  height: 24px;
}
.file-type strong {
  font-size: 12px;
}
.file-type small {
  font-size: 10px;
  color: var(--secondary);
}
/* Quotation (bom.js) */
.bom-fields textarea {
  width: 100%;
  font: inherit;
}
.bom-panel > h2 {
  margin-bottom: 16px;
}
.bom-panel .panel-heading {
  padding: 0 0 14px;
  flex-wrap: wrap;
}
.bom-panel .table-scroll {
  margin: 0 -10px -8px;
}
.bom-hint,
.bom-sub {
  color: var(--muted);
  font-size: 11px;
}
.bom-hint {
  margin-top: 12px;
}
.bom-upload {
  margin-bottom: 22px;
}
.bom-upload > h2 {
  margin: 0;
}
.bom-upload .bom-hint {
  margin-top: 8px;
}
.bom-sub {
  margin-top: 3px;
  white-space: nowrap;
}
.bom-fields {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px 18px;
}
.bom-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-size: 11px;
  color: var(--muted);
  min-width: 0;
}
.bom-field input,
.bom-field select {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
.bom-form-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.bom-fields .bom-form-actions {
  grid-column: 1/-1;
}
.bom-title {
  margin: 4px 0 16px;
}
.bom-plan {
  margin-top: 12px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-alt);
}
.bom-plan .bom-form-actions {
  margin-top: 12px;
}
.bom-gaps > button {
  margin-top: 12px;
}
.bom-toggle {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 12px;
}
.bom-lines td {
  vertical-align: top;
  padding: 11px 14px;
}
.bom-lines th {
  padding: 11px 14px;
}
.bom-warnings .pill {
  margin: 0 4px 4px 0;
}
.bom-warnings {
  white-space: normal;
  min-width: 160px;
}
.bom-muted-row td {
  opacity: 0.6;
}
.bom-saving {
  color: var(--green);
}

/* Layout invariants: background updates and language/theme switches never move the viewport. */
html { scrollbar-gutter: stable; }
body { line-height: 1.45; overflow-anchor: none; }
main { min-width: 0; }
button, input, select, textarea { line-height: 1.4; }
select { min-width: 0; max-width: 100%; text-overflow: ellipsis; }
textarea { font: inherit; color: var(--text); background: var(--surface); }
textarea:focus-visible, summary:focus-visible, tr:focus-visible {
  outline: 2px solid var(--focus); outline-offset: -2px;
}
.header-actions > * { flex-shrink: 0; }
.header-actions button.icon-button {
  width: 30px; height: 30px; display: grid; place-items: center; padding: 0;
}
.language-toggle { height: var(--control-h-sm); }
.language-toggle button { min-width: 55px; padding: 4px 6px; }
.read-only { min-width: 80px; text-align: right; }
.breadcrumb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
h1 { line-height: 1.25; }
h2 { line-height: 1.4; }
.panel-heading p { min-height: 17px; }
.bar-label { gap: 10px; min-height: 16px; align-items: baseline; }
.bar-label > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-label b { flex-shrink: 0; }
.table-scroll, .job-log, pre, .research-results {
  overscroll-behavior: contain; overflow-anchor: none; scrollbar-gutter: stable;
}
.grid { max-height: 65dvh; }
.grid thead th { position: sticky; top: 0; z-index: 2; }
.grid thead .row-number { z-index: 3; }
.sheet-toolbar #filters {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));
  flex-basis: 100%; gap: 6px; width: 100%;
}
.sheet-toolbar #filters:empty { display: none; }
.sheet-toolbar #filters select { width: 100%; }
/* searchable filters (app.js suggestField) look like the dropdowns next to them */
.sheet-toolbar #filters .filter-suggest { display: block; min-width: 0; }
.sheet-toolbar #filters .filter-field {
  width: 100%; min-width: 0; background: var(--surface); border: 1px solid var(--border); border-radius: 7px;
  color: var(--text); padding: 8px 10px;
}
.sheet-toolbar #filters .filter-field::placeholder { color: var(--secondary); }
.sheet-toolbar #filters .filter-field:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.sheet-toolbar #filters .filter-check input[type='number'] {
  background: var(--surface); border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px; color: var(--text);
}
.sheet-toolbar #filters input[type='search'] { width: 100%; min-width: 0; }
/* the Offers checks sit in the same boxes as the fields beside them (#40); a label wraps inside its box rather than
   running out of it (French on a phone, #978) */
.sheet-toolbar #filters .filter-check {
  display: flex; align-items: center; gap: 8px; white-space: nowrap; font-size: 13px; min-width: 0;
  height: var(--control-h); padding: 0 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 7px;
  color: var(--text); cursor: pointer;
}
.sheet-toolbar #filters .filter-check-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }   /* #702 */
.sheet-toolbar #filters .filter-check:hover { border-color: var(--border-strong); }
.sheet-toolbar #filters .filter-check:has(input:checked) { border-color: var(--border-strong); background: var(--selected-bg); }
.sheet-toolbar #filters .filter-check input[type='checkbox'] { margin: 0; width: 15px; height: 15px; accent-color: var(--accent); }
/* the lead-time field sits inside its box, never taller than the controls beside it (#628) */
.sheet-toolbar #filters .filter-check input[type='number'] { width: 72px; margin-left: auto; padding: 0 8px; height: calc(var(--control-h) - 10px); }
/* suggestions under a filter field (app.js suggestField) and the open dropdowns follow the theme (#32) */
.sheet-toolbar #filters .filter-suggest { position: relative; }
.suggest-list {
  position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; right: 0; max-height: 280px; overflow-y: auto;
  margin: 0; padding: 4px; list-style: none; background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 7px; box-shadow: 0 8px 24px var(--shadow-soft);
}
.suggest-list[hidden] { display: none; }
.suggest-list li { padding: 6px 8px; border-radius: 5px; cursor: pointer; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggest-list li:hover, .suggest-list li[aria-selected='true'] { background: var(--soft); }
/* Dropdowns (the standard, 2026-10-28: "dropdowns should always have the site formatting"): every
   one-choice <select> is the site's control with a drawn chevron, and its open list is the site's too: the browser's
   customizable select where it has one (appearance: base-select), else ui.js's list (.select-list), never the OS menu.
   The chevron is two gradients in --muted, so it follows the theme; !important as pages set the background shorthand. */
select:not([multiple]) {
  appearance: none; -webkit-appearance: none; padding-right: 26px !important;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%) !important;
  background-position: calc(100% - 14px) 52%, calc(100% - 9px) 52% !important;
  background-size: 5px 5px, 5px 5px !important; background-repeat: no-repeat !important; }
@supports (appearance: base-select) {
  select:not([multiple]), select:not([multiple])::picker(select) { appearance: base-select; }
  /* a base-select is a flex box whose text sat at its top (#981, "text is offset"): centred at any height (the
     control's or the compact one), one line kept off the chevron, and a long name cut with … (ui.js dropdown's box) */
  select:not([multiple]) { align-items: center; white-space: nowrap; overflow: clip; overflow-clip-margin: content-box; }
  select:not([multiple]) > button { all: unset; display: block; flex: 1 1 auto; width: 100%; min-width: 0; }
  select:not([multiple]) selectedcontent { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  select::picker-icon { display: none; }
  select option::checkmark { display: none; }
  ::picker(select) {
    margin: 4px 0; padding: 4px; max-height: min(320px, 60dvh); background: var(--surface); color: var(--text);
    border: 1px solid var(--border-strong); border-radius: 7px; box-shadow: 0 8px 24px var(--shadow-soft); }
  select option { padding: 6px 8px; border-radius: 5px; font-size: 13px; }
}
select option:hover, select option:focus-visible { background: var(--soft); }
select option:checked { background: var(--focus-soft); }
/* ui.js's list, where the browser has no customizable select: the same frame, under (or over) its control */
.select-list { position: fixed; z-index: 90; margin: 0; padding: 4px; overflow-y: auto; list-style: none; background: var(--surface);
  color: var(--text); border: 1px solid var(--border-strong); border-radius: 7px; box-shadow: 0 8px 24px var(--shadow-soft);
  font-size: 13px; outline: none; }
.select-list li { padding: 6px 8px; border-radius: 5px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.select-list li.select-group { padding-top: 8px; cursor: default; color: var(--muted); font-weight: 600; font-size: 12px; }
.select-list li[aria-selected='true'] { background: var(--focus-soft); }
.select-list li.marked { background: var(--soft); }
.select-list li[aria-disabled='true'] { color: var(--muted); cursor: default; }
/* on a phone, the list is a sheet from the bottom with finger-sized options */
@media (max-width: 720px) {
  .select-list.select-sheet { left: 0 !important; right: 0; top: auto !important; bottom: 0 !important; min-width: 0 !important;
    max-height: 60dvh !important; padding: 8px 8px calc(8px + env(safe-area-inset-bottom)); border-width: 1px 0 0;
    border-radius: 7px 7px 0 0; }
  .select-list.select-sheet li { min-height: 44px; display: flex; align-items: center; font-size: 15px; }
  @supports (appearance: base-select) {
    ::picker(select) { position: fixed; inset: auto 0 0 0; width: auto; margin: 0; max-height: 60dvh;
      border-radius: 7px 7px 0 0; }
    select option { min-height: 44px; font-size: 15px; }
  }
}
/* offer details (#38): the facts without a picture column, then the price breaks */
.part-summary.offer-summary { grid-template-columns: minmax(0, 1fr); }
.offer-summary .summary-media { display: none; }
.offer-summary .summary-facts { gap: 10px 28px; font-size: 15px; }
.price-breaks { border-collapse: collapse; font-size: 13px; }
.price-breaks th { text-align: left; font-size: 11px; font-weight: 600; color: var(--muted); }
.price-breaks th, .price-breaks td { padding: 4px 18px 4px 0; border-bottom: 1px solid var(--border); }
.sheet-info { min-height: 45px; }
.pagination { min-height: 40px; }
#admin-portal, #access-portal, #import-portal { overflow-anchor: none; }
#admin-portal .job-log { height: min(42dvh, 360px); }
#admin-portal .job-row .job-log { height: auto; max-height: 280px; }
.job-history { min-width: 0; }
.job-row summary { min-width: 0; }
html[lang='zh-CN'] h1, html[lang='zh-CN'] h2, html[lang='zh-CN'] th {
  letter-spacing: 0;
}
@media (max-width: 1000px) and (min-width: 721px) {
  .sidebar { width: var(--sidebar-width); padding-inline: 10px; }
  main { margin-left: var(--sidebar-width); }
  header { padding-inline: 20px; }
  nav a { gap: 6px; padding-inline: 6px; }
  .read-only { display: none; }
}
@media (max-width: 720px) {
  header { padding-inline: 14px; height: 56px; }
  .breadcrumb strong { font-size: 15px; }
  .header-actions { gap: 6px; }
  .read-only { display: none; }
  .header-actions button.report-issue { padding: 6px 6px; min-width: var(--control-h-sm); height: var(--control-h-sm); }
  .report-issue span[aria-hidden] { margin: 0; }
  .content { padding: 20px 14px; }
  .title-actions { flex-shrink: 0; }
  .panel-heading { padding: 14px; }
  .bars { padding: 0 14px 14px; }
  .sheet-toolbar { padding: 10px; gap: 6px; }
  .search-box { flex-basis: 100%; padding: 0 4px; }
  .sheet-toolbar #filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid td { padding: 6px 10px; }
  .grid th button { padding: 8px 10px; }
  .tab-strip > a { padding: 5px 9px; font-size: 12px; }
  dialog { padding: 16px; width: calc(100vw - 24px); max-height: calc(100dvh - var(--popup-top, 72px) - 24px); }
  dl { grid-template-columns: minmax(90px, 30%) minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; scroll-behavior: auto !important; }
}

.sheet-toolbar > button { order: 1; }
.sheet-toolbar #filters { order: 2; }
body:has(dialog[open]) { overflow: hidden; }
dialog { scrollbar-gutter: stable; }

/* Wide screens: every row of a list uses the same fixed columns, so names, pills, dates and outcomes line up from
   row to row (a column sized by its own row's content, or a sixth item in a five-column grid, made them jump). */
@media (min-width: 1101px) {
  .job-history .job-row > summary { grid-template-columns: minmax(140px, 1fr) 100px 80px 190px minmax(0, 3fr) auto; }
  .job-row > summary > .pill { justify-self: start; }
  .job-row .job-actions { justify-self: end; display: flex; gap: 4px; }
}
/* Compact history rows reflow instead of clipping dates and actions on small screens. */
@media (max-width: 1100px) {
  .job-row > summary { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 10px; padding-block: 10px; }
  .job-row > summary > .muted, .job-row .job-outcome { grid-column: 1 / -1; }
  .job-row .job-actions { grid-column: 2; grid-row: 1; }
  .job-row .job-status { justify-self: start; }
  .job-row > summary > b { min-width: 0; overflow-wrap: anywhere; }
}
@media (max-width: 480px) {
  .access-toolbar input[type='search'] { width: 100%; flex-basis: 100%; }
}


/* A single disclosure treatment, independent of platform glyphs or translated labels. */
summary { cursor: pointer; list-style: none; position: relative; padding-left: 20px; }
summary::-webkit-details-marker { display: none; }
summary::before {
  content: ''; position: absolute; left: 3px; top: calc(50% - 4px); width: 6px; height: 6px;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: rotate(-45deg); transform-origin: center; transition: transform .12s ease;
}
details[open] > summary::before { transform: rotate(45deg); }
.job-row > summary { padding-left: 20px; }
.job-row > summary::before { top: 13px; }
.grid .image-column, #recent-parts .image-column { width: 58px; min-width: 58px; padding: 4px 8px; text-align: center; }
.part-thumbnail {
  display: inline-grid; place-items: center; vertical-align: middle; width: 40px; height: 40px;
  overflow: hidden; border: 1px solid var(--border); border-radius: 5px; background: var(--surface-alt);
  position: relative;
}
.part-thumbnail::before {
  content: ''; width: 14px; height: 12px; border: 1px solid var(--muted); border-radius: 2px;
  background: linear-gradient(135deg, transparent 50%, var(--border-strong) 51%) center / 10px 9px no-repeat;
  opacity: .65;
}
/* the photo shows on the tile's own shade, with no backing of its own (a catalog picture's filter would tint one);
   the placeholder icon goes once it has loaded, and an image that fails never flashes */
.part-thumbnail img { position: absolute; inset: 0; display: block; width: 40px; height: 40px; object-fit: contain; opacity: 0; }
.part-thumbnail img.loaded { opacity: 1; }
.part-thumbnail:has(img.loaded)::before { visibility: hidden; }

.grid .row-number { padding-inline: 6px; text-align: center; }

.sidebar nav .nav-icon {
  display: grid; place-items: center; flex: 0 0 20px; width: 20px; height: 20px; line-height: 0;
}
.nav-icon svg { display: block; width: 18px; height: 18px; }

/* Activity stays inside its existing box, so live updates never change the layout. */
@keyframes activity-spin { to { transform: rotate(360deg); } }
@keyframes activity-sheen { to { background-position: 40px 0; } }
.loading-spinner::before,
.job-live:is(.status-running,.status-queued,.status-cancelling) .job-status::before {
  content: ''; display: inline-block; vertical-align: -2px; width: 11px; height: 11px;
  margin-right: 6px; border: 1.5px solid var(--border-strong); border-top-color: var(--accent);
  border-radius: 50%; animation: activity-spin 1s linear infinite;
}
.job-live:is(.status-running,.status-queued) .job-progress-fill {
  background-image: repeating-linear-gradient(110deg, transparent 0 12px, var(--focus-soft) 12px 22px, transparent 22px 40px);
  background-size: 40px 100%; animation: activity-sheen 1.6s linear infinite;
}
@media (max-width: 1100px) { .job-row > summary::before { top: 16px; } }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; } }

.pagination > div { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px; }
.page-jump { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.page-jump input {
  width: 68px; padding: 5px 6px; border: 1px solid var(--border); border-radius: 5px;
  color: var(--text); background: var(--surface); text-align: center; font-variant-numeric: tabular-nums;
  appearance: textfield;
}
.page-jump input::-webkit-inner-spin-button, .page-jump input::-webkit-outer-spin-button { appearance: none; margin: 0; }
.summary-loading { grid-column: 1 / -1; height: 151px; display: flex; align-items: center; justify-content: center; color: var(--muted); }

@media (max-width: 720px) { .summary-loading { height: 372px; } }

.view-toggle {
  display: inline-flex; align-items: center; padding: 2px; gap: 2px; margin-left: auto; order: 1;
  border: 1px solid var(--border); border-radius: 7px; background: var(--surface-alt); flex-shrink: 0;
}
.view-toggle button { min-width: 62px; border: 0; padding: 5px 8px; background: transparent; color: var(--muted); font-size: 12px; }
.view-toggle button[aria-pressed='true'] { background: var(--selected-bg); color: var(--text); }

#result-count { position: relative; }
#sheet:has(#grid[aria-busy='true']) #result-count::after {
  content: ''; position: absolute; left: calc(100% + 8px); top: 1px; width: 10px; height: 10px;
  border: 1.5px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%;
  animation: activity-spin 1s linear infinite;
}

/* Keep navigation mounted, and reserve notice space in the title row: no overlap or layout shift. */
.sidebar { z-index: 40; }
main > header { position: sticky; top: 0; z-index: 30; }
/* a menu of the bar's open (Settings, the avatar's): over Dev Comments, which floats just under the bar (#1041) */
main > header:has(.header-menu.open) { z-index: 904; }
/* Pages have no subtitle (#94, #95): content starts at the top, and the warning slot (#message) takes space only
   while it shows a notice. The page title is the title bar's crumb (#70). */
#message { min-width: 0; }
#message:not(:empty) { margin-bottom: 20px; }
#message .notice { display: flex; align-items: flex-start; gap: 10px; margin: 0; max-height: 64px; overflow: auto; padding: 10px 12px; line-height: 18px; }
#message .notice > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
#message .notice-dismiss { flex-shrink: 0; display: grid; place-items: center; width: 18px; height: 18px; margin: 0; padding: 0; border: 0; background: transparent; font-size: 18px; line-height: 1; }
.sheet-info { align-items: center; flex-wrap: wrap; gap: 8px 12px; padding: 9px 2px; }
#result-count { flex: 1; min-width: 150px; overflow-wrap: anywhere; }
.sheet-view-actions { display: flex; align-items: center; gap: 12px; margin-left: auto; }
.sheet-view-actions .view-toggle { margin-left: 0; order: initial; }
@media (max-width: 720px) {
  #message:not(:empty) { margin-bottom: 16px; }
  #message .notice { max-height: 42px; padding: 5px 9px; font-size: 11px; line-height: 15px; }
}

/* Compact spreadsheet rows and a single export boundary for all matching records. */
.sheet-toolbar .export-actions { display: inline-flex; align-items: center; gap: 6px; order: 1; }
.export-actions .info-tip { margin: 0; flex-shrink: 0; }
#export-info::after { left: auto; right: 0; max-width: min(320px, calc(100vw - 48px)); }
.recent-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 10px; }
.recent-actions .view-toggle { margin: 0; order: initial; }
#recent-parts td { padding: 6px 12px; }
#recent-parts td:nth-last-child(3) { max-width: 360px; }
#recent-parts .datasheet-column { width: 40px; min-width: 40px; text-align: center; }
.datasheet-link { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 4px; vertical-align: middle; }
.datasheet-link:hover { background: var(--soft); }
.datasheet-link svg { display: block; }
/* the parts grid's datasheet and 3D model icons before the MPN (#157: "a little small"): 18px in the text's ink
   (white on the dark theme; the accent is for key actions only) when the part has one, faded when it hasn't */
.doc-icon { display: inline-block; width: 18px; height: 18px; vertical-align: -4px; color: var(--text); }
.grid .doc-column { padding: 4px 0; text-align: center; }
.grid th.doc-column svg { display: inline-block; width: 18px; height: 18px; vertical-align: -4px; color: var(--secondary); }
.doc-icon svg { display: block; width: 100%; height: 100%; }
.doc-icon.missing { color: var(--muted); opacity: 0.35; }
/* the minimal PDF / CAD file viewer (#93): the file fills it; Report an issue and × are all it adds */
/* Files (files.js): the viewer over the page, a preview, a one-file picker, drop targets */
dialog.file-viewer[open] { width: 92vw; height: calc(100dvh - var(--popup-top, 72px) - 24px); max-height: calc(100dvh - var(--popup-top, 72px) - 24px); padding: 12px 16px 16px; display: flex; flex-direction: column; }
.file-viewer .dialog-heading { margin-bottom: 10px; gap: 12px; }
.file-viewer .dialog-heading h2 { font-size: 15px; }
.file-viewer .heading-actions button:first-child { font-size: 12px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 999px; }
.file-preview { display: flex; flex-direction: column; gap: 6px; min-height: 0; }
.file-viewer .file-preview { flex: 1; }
.file-preview iframe { flex: 1; width: 100%; min-height: 50vh; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); }
.file-preview iframe.file-site { background: var(--paper); }
.file-site-open { align-self: flex-end; font-size: 12px; }
.file-preview img { max-width: 100%; max-height: 50vh; display: block; border-radius: 6px; }
.file-viewer .file-preview img { max-height: 100%; object-fit: contain; margin: auto; }
.file-preview pre { max-height: 50vh; overflow: auto; font-size: 12px; margin: 0; white-space: pre-wrap;
  overflow-wrap: anywhere; color: var(--text); }
.file-viewer .file-preview pre { max-height: none; flex: 1; }
.file-viewer .file-preview > p:first-child { margin: auto auto 0; }
.file-viewer .file-preview > a { margin: 0 auto auto; }
.file-pick { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; border: 1.5px dashed transparent;
  border-radius: 8px; }
.file-pick button { font-size: 11px; padding: 6px 10px; }
.file-pick-name { font-size: 11px; color: var(--secondary); }
.file-pick-name.error-text { color: var(--danger); }
.file-pick.dragging { border-color: var(--green); background: var(--soft); }
@media (max-width: 720px) {
  .recent .panel-heading { flex-wrap: wrap; }
  .recent-actions { width: 100%; justify-content: space-between; }
}
#admin-portal .toolbar input[type='search'] {
  min-width: 0; padding: 8px 10px; border: 1px solid var(--border); border-radius: 7px;
  color: var(--text); background: var(--surface); font: inherit; font-size: 12px;
}
#admin-portal .toolbar input[type='search']:focus-visible {
  outline: 2px solid var(--focus); outline-offset: -2px;
}

.automation-settings { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 8px; }
.automation-settings > summary { display: flex; align-items: center; gap: 8px; }
.automation-settings > summary .pill { margin-left: auto; }
.automation-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 12px; padding-top: 10px; }
.automation-form > label { display: flex; flex-direction: column; align-items: stretch; gap: 4px; font-size: 11px; color: var(--muted); min-width: 0; }
.automation-form input:not([type='checkbox']), .automation-form select { width: 100%; min-width: 0; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; color: var(--text); background: var(--surface); font: inherit; font-size: 12px; }
.automation-form > .automation-enabled { flex-direction: row-reverse; justify-content: flex-end; align-items: center; grid-column: 1 / -1; }
.automation-footer { display: flex; align-items: center; gap: 4px; grid-column: 1 / -1; }
.automation-status { grid-column: 1 / -1; min-height: 17px; margin: 0 !important; font-size: 11px; overflow-wrap: anywhere; }
.connection-switch { min-width: 50px; padding: 3px 8px; font-size: 11px; }
.connection-switch[aria-checked='true'] { color: var(--accent); background: var(--soft); }
@media (max-width: 390px) { .automation-form { grid-template-columns: minmax(0, 1fr); } }

.info-tip { text-transform: none; letter-spacing: 0; }
#recent-parts tbody tr { height: 36px; }

/* ==== Review: upload a file, point at what is wrong, comment (review.js). Theme colours only. ==== */
#review-portal {
  --mark: var(--accent);
  --mark-fill: color-mix(in srgb, var(--accent) 13%, transparent);
  --mark-line: color-mix(in srgb, var(--accent) 55%, transparent);
  --sent-line: var(--border-strong);
  --sent-fill: color-mix(in srgb, var(--muted) 10%, transparent);
}

/* the open file */
.review-back {
  display: inline-block;
  font-size: 12px;
  margin-bottom: 10px;
}
.review-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 16px;
  margin-bottom: 16px;
}
.review-titles {
  min-width: 0;
}
.review-titles h2 {
  margin: 0 0 6px;
  font-size: 18px;
  overflow-wrap: anywhere;
}
.review-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  color: var(--muted);
}
.review-head-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
a.button.review-download {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 8px 12px;
  background: var(--surface);
  color: var(--text);
  font-weight: 550;
  font-size: 13px;
  white-space: nowrap;
}
a.button.review-download:hover {
  background: var(--soft);
}
.review-deck {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  font-size: 13px;
  font-weight: 550;
  white-space: nowrap;
  overflow: hidden;
}
.review-deck > span {
  padding: 8px 10px 8px 12px;
  color: var(--text);
}
/* the download icon beside the label (review.js withDownload, #634) */
.review-deck > span, .review-changes-downloads > button { display: inline-flex; align-items: center; gap: 6px; }
.review-deck > a {
  padding: 8px 10px;
  border-left: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
}
.review-deck > a:hover {
  background: var(--soft);
}
.review-deck.disabled {
  opacity: 0.5;
}
.review-deck.disabled > a {
  cursor: not-allowed;
}
.review-deck.disabled > a:hover {
  background: none;
}
.review-stage {
  min-width: 0;
}
.review-stage-bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}
.review-pager {
  display: flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}

/* the comment number, used on the file, in the list and in the bubble */
.review-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 10px;
  background: var(--surface);
  border: 1.5px solid var(--mark);
  color: var(--mark);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  cursor: pointer;
  box-shadow: 0 1px 3px var(--shadow-soft);
}
.review-num.draft,
.review-num.new {
  background: var(--mark);
  color: var(--on-accent);
}

/* tables */
.review-scroll {
  max-height: calc(100vh - 290px);
  overflow: auto;
}
.review-pages,
.review-text {
  min-height: 240px;
}
.review-table {
  font-size: 12px;
}
.review-table th,
.review-table td {
  padding: 7px 12px;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.review-table td {
  position: relative;
  color: var(--text);
  max-width: 280px;
  user-select: none; /* a drag selects cells, not text */
}
.review-table thead th {
  text-align: left;
}
.review-table.grid thead th {
  text-align: center;
  min-width: 64px;
}
.review-table .review-rownum {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface-alt);
  color: var(--muted);
  text-align: right;
  width: 1%; /* as narrow as its numbers, however few columns the sheet has */
  min-width: 52px;
  white-space: nowrap;
  font-weight: 500;
}
.review-table thead .review-rownum {
  z-index: 3;
}
.review-table .target {
  cursor: cell;
}
.review-table th.target {
  cursor: pointer;
}
.review-table td.target:hover,
.review-table th.target:hover {
  box-shadow: inset 0 0 0 1.5px var(--focus);
}
.review-table .commented,
.review-table tr.commented > td {
  background: var(--mark-fill);
}
.review-table td.commented {
  box-shadow: inset 2px 0 0 var(--mark);
}
.review-table .commented-sent,
.review-table tr.commented-sent > td {
  background: var(--sent-fill);
}
.review-table td.commented-sent {
  box-shadow: inset 2px 0 0 var(--sent-line);
}
.review-table .editing,
.review-table td.dragging,
.review-table .hot {
  box-shadow: inset 0 0 0 2px var(--mark) !important;
  background: var(--mark-fill) !important;
}
.review-table th .review-num,
.review-table td .review-num {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
  min-width: 18px;
  height: 18px;
  font-size: 10px;
}
.review-table th.review-rownum .review-num {
  right: auto;
  left: 4px;
}
.review-sheets {
  display: flex;
  gap: 2px;
  padding: 6px 8px 0;
  border-top: 1px solid var(--border);
  background: var(--surface-alt);
  overflow-x: auto;
}
.review-sheets button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 7px 7px 0 0;
  border-bottom: 0;
  padding: 6px 14px;
  font-size: 12px;
  background: transparent;
  border-color: transparent;
  color: var(--secondary);
  white-space: nowrap;
}
.review-sheets button[aria-pressed="true"] {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
}
.review-sheet-count {
  font-size: 10px;
  font-weight: 700;
  border-radius: 8px;
  padding: 1px 6px;
  background: var(--mark);
  color: var(--on-accent);
}

/* text */
.review-text {
  white-space: pre-wrap;
  padding: 24px 28px;
  line-height: 1.75;
  font-size: 14px;
  cursor: text;
  overflow-wrap: anywhere;
}
.review-text mark {
  color: inherit;
  background: var(--sent-fill);
  border-bottom: 2px solid var(--sent-line);
  border-radius: 2px;
  cursor: pointer;
}
.review-text mark.draft {
  background: var(--mark-fill);
  border-bottom-color: var(--mark);
}
.review-text mark.editing,
.review-text mark.hot {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  border-bottom-color: var(--mark);
}
/* Edit: the edited copy beside the file with its tracked changes (#158); stacked on a narrow screen */
.review-changes {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.review-changes > * {
  min-width: 0;
}
.review-edit {
  min-height: 240px;
  padding: 24px 28px;
  border: 0;
  border-right: 1px solid var(--border);
  border-radius: 0;
  resize: none;
  font: inherit;
  font-size: 14px;
  line-height: 1.75;
  background: transparent;
  color: inherit;
}
.review-text del.review-del {
  color: var(--danger);
  text-decoration: line-through;
}
.review-text ins.review-ins {
  color: var(--accent);
  text-decoration: underline;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.review-changes-downloads {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
}
@media (max-width: 720px) {
  .review-changes {
    grid-template-columns: 1fr;
  }
  .review-edit {
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
}
.review-text .review-num.inline {
  position: relative;
  top: -0.55em;
  margin: 0 2px;
  min-width: 18px;
  height: 18px;
  font-size: 10px;
}

/* pages and images */
.review-pages {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 20px;
  background: var(--canvas);
}
.review-page {
  position: relative;
  width: 100%;
  background: #fff;
  box-shadow:
    0 1px 4px var(--shadow-soft),
    0 0 0 1px var(--border);
  cursor: crosshair;
  user-select: none;
  touch-action: none;
}
.review-page img {
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
.review-page-no {
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 10px;
}
.review-box {
  position: absolute;
  border: 2px solid var(--sent-line);
  background: var(--sent-fill);
  border-radius: 4px;
  cursor: pointer;
}
.review-box.draft {
  border-color: var(--mark);
  background: var(--mark-fill);
}
.review-box.editing,
.review-box.hot {
  border-color: var(--mark);
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  box-shadow: 0 0 0 4px var(--focus-soft);
}
.review-box > .review-num {
  position: absolute;
  top: -11px;
  left: -11px;
}
.review-pin {
  position: absolute;
  width: 0;
  height: 0;
}
/* a point: a small dot exactly where the click was, its number on a flag beside it so the content stays readable */
.review-pin::before {
  /* a ring: what it points at stays visible through the middle */
  content: "";
  position: absolute;
  left: -7px;
  top: -7px;
  width: 14px;
  height: 14px;
  box-sizing: border-box;
  border-radius: 50%;
  border: 2.5px solid var(--mark);
  background: transparent;
  box-shadow:
    0 0 0 1.5px var(--surface),
    inset 0 0 0 1px var(--surface),
    0 1px 3px var(--shadow);
}
.review-pin.sent::before {
  border-color: var(--mark-line);
}
.review-pin > .review-num {
  position: absolute;
  left: 7px;
  top: -7px;
  transform: translateY(-100%);
  border-radius: 10px 10px 10px 2px;
}
.review-pin.editing::before,
.review-pin.hot::before {
  box-shadow:
    0 0 0 1.5px var(--surface),
    0 0 0 6px var(--focus-soft),
    0 1px 3px var(--shadow);
}
.review-pin.editing > .review-num,
.review-pin.hot > .review-num {
  box-shadow:
    0 0 0 4px var(--focus-soft),
    0 1px 3px var(--shadow);
}

/* 3D models (three-cad-viewer draws its own toolbar, tree and canvas inside .review-cad-mount) */
.review-cad {
  position: relative;
  min-height: 420px;
  padding: 8px;
  overflow: hidden;
}
.file-preview .file-cad { flex: 1; padding: 0; }   /* a STEP file in the file viewer (files.js cadView) */
.review-cad-status {
  padding: 24px;
  font-size: 12px;
  color: var(--muted);
}
.review-cad-overlay {
  position: absolute;
  inset: 0;
  z-index: 300; /* above the viewer's own panels (z-index 100-200) */
  pointer-events: none;
  overflow: hidden;
}
.review-cad-overlay .review-pin {
  pointer-events: auto;
}

/* comments panel */
.review-comments {
  position: sticky;
  top: 16px;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 120px);
}
.review-comments-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 16px 18px 12px;
  border-bottom: 1px solid var(--border);
}
.review-comments-head h3 {
  margin: 0;
  font-size: 14px;
}
.review-count {
  font-size: 11px;
  font-weight: 650;
  border-radius: 9px;
  padding: 1px 7px;
  background: var(--soft);
  color: var(--secondary);
}
.review-list {
  list-style: none;
  margin: 0;
  padding: 8px;
  overflow: auto;
  flex: 1;
  min-height: 80px;
}
.review-empty {
  display: grid;
  gap: 6px;
  padding: 22px 12px;
  text-align: center;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
}
.review-empty strong {
  color: var(--text);
  font-size: 13px;
}
.review-comment {
  display: grid;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid transparent;
  border-radius: 9px;
  cursor: pointer;
}
.review-comment + .review-comment {
  margin-top: 4px;
}
.review-comment:hover,
.review-comment:focus-visible {
  background: var(--surface-alt);
  border-color: var(--border);
  outline: none;
}
.review-comment.editing {
  background: var(--soft);
  border-color: var(--mark-line);
}
.review-comment-top {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.review-where {
  font-size: 11px;
  font-weight: 600;
  color: var(--secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.review-text-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.review-comment-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 10px;
  color: var(--muted);
}
.review-comments-foot {
  display: grid;
  gap: 8px;
  padding: 12px 14px 14px;
  border-top: 1px solid var(--border);
}
.review-comments-foot .primary {
  width: 100%;
  padding: 10px;
}
.review-foot-note {
  margin: 0;
  font-size: 11px;
  color: var(--muted);
  text-align: center;
}
.review-done {
  margin: 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--soft);
  color: var(--text);
  font-size: 12px;
}

/* Onshape: link a document on the upload screen; the AI's proposed edit under its comment */
.review-onshape-link {
  display: grid;
  gap: 8px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.review-onshape-link h3 {
  margin: 0;
  font-size: 14px;
}
.review-onshape-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.review-onshape-row input {
  flex: 1 1 320px;
  min-width: 0;
}
.review-onshape-row select {
  max-width: 100%;
}
.review-onshape-doc {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.review-edit {
  display: grid;
  gap: 6px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-alt);
  cursor: default;
}
.review-edit.failed {
  border-color: var(--danger-border);
}
.review-edit-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 12px;
}
.review-edit-change {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  overflow-wrap: anywhere;
}
.review-edit-why {
  margin: 0;
  font-size: 12px;
  color: var(--secondary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.review-edit-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.review-edit-actions button {
  padding: 5px 10px;
  font-size: 12px;
}
.review-edit-checks {
  margin: 0;
  padding-left: 18px;
  font-size: 11px;
  color: var(--secondary);
}
.review-edit-error {
  margin: 0;
  font-size: 12px;
  color: var(--danger);
  overflow-wrap: anywhere;
}
.review-edit-link {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
}
.pill.ok {
  color: var(--accent);
}
.pill.danger {
  color: var(--danger);
}

/* the comment bubble */
.review-bubble {
  position: fixed;
  z-index: 1000; /* above the 3D viewer's panels */
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px;
  box-shadow:
    0 10px 30px var(--shadow),
    0 1px 3px var(--shadow-soft);
}
.review-bubble-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  min-width: 0;
}
.review-bubble textarea {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  resize: vertical;
  font-size: 13px;
  min-height: 72px;
}
.review-bubble textarea:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 0;
  border-color: transparent;
}
.review-bubble-text {
  margin: 2px 2px 10px;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.review-bubble-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 8px;
}
.review-bubble-actions button {
  padding: 6px 12px;
  font-size: 12px;
}
.review-bubble-note {
  margin-right: auto;
  font-size: 11px;
  color: var(--muted);
}
.review-bubble-actions .review-delete {
  margin-right: auto;
  color: var(--danger);
}
tr.clickable {
  cursor: pointer;
}
@media (max-width: 1100px) {
  .review-comments {
    position: static;
    max-height: none;
  }
  .review-scroll {
    max-height: 70vh;
  }
}
@media (max-width: 720px) {
  .review-head {
    flex-direction: column;
    align-items: flex-start;
  }
}
/* An open file is a workspace (#271, "you shouldn't have to scroll down in the browser"): on a wide window the file
   and its comments (two-thirds + third) fill it from the portal's top (--review-top, review.js fitWorkspace) down to
   the content's room for Spot, and scroll inside; the window doesn't scroll. Narrower windows stack them as before. */
@media (min-width: 1101px) {
  #review-portal.review-workspace:not([hidden]) {
    display: flex;
    flex-direction: column;
    height: calc(100dvh - var(--review-top, 120px) - 116px);
    min-height: 360px;
  }
  #review-portal.review-workspace > .blocks {
    flex: 1;
    min-height: 0;
    margin: 0;
    grid-template-rows: minmax(0, 1fr);
    align-items: stretch;
  }
  #review-portal.review-workspace > .blocks > [data-span] {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
  }
  .review-workspace .review-comments {
    position: static;
    max-height: none;
  }
  .review-workspace .review-stage > .review-scroll,
  .review-workspace .review-stage > .review-changes,
  .review-workspace .review-stage > .review-cad {
    flex: 1;
    min-height: 0;
    max-height: none;
  }
  .review-workspace .review-stage > .review-changes {
    grid-template-rows: minmax(0, 1fr);
  }
  .review-workspace .review-changes > .review-scroll {
    max-height: none;
    overflow: auto;
  }
}
.review-cad [data-theme="light"],
.review-cad [data-theme="dark"] {
  --tcv-theme-blue: var(--accent);
  --tcv-bg-color: var(--surface);
  --tcv-font-color: var(--text);
  --tcv-bg-highlight-color: var(--soft);
  --tcv-bg-pressed-color: var(--surface-alt);
  --tcv-bg-pressed-border-color: var(--border-strong);
  --tcv-bg-tooltip-color: var(--surface-alt);
  --tcv-dropdown-bg: var(--surface-alt);
}

/* Prepared demo steps (demo.js, local mode only) */
.demo-tour {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 50;
  width: min(360px, calc(100vw - 32px));
  max-height: 60vh;
  overflow: auto;
  padding: 12px 14px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: 0 8px 24px var(--shadow);
}
.demo-tour.collapsed {
  width: auto;
  padding: 4px;
}
.demo-tour h3 {
  margin: 4px 0 6px;
}
.demo-tour p {
  margin: 4px 0;
}
.demo-tour-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.demo-tour-look {
  color: var(--accent);
}
.demo-tour-bar {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

/* Navigation groups: Engineering, Operations, Commercial, then Admin at the bottom. */
/* Many projects and tabs make the links taller than the window (#119): they scroll inside the sidebar, and Admin
   stays pinned at its foot instead of running off the bottom. */
.sidebar nav { flex: 1; display: flex; flex-direction: column; gap: 12px; min-height: 0; }
/* the links scroll above the Admin footer, which is outside the scroller: it never covers one */
.nav-scroll { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 12px; overflow-y: auto;
  overscroll-behavior: contain; scrollbar-width: thin; }
.nav-group { display: grid; gap: 0; }
.nav-bench:empty { display: none; } /* My Bench (tabs/bench.py) until its link arrives, or while it is Prototype */
.nav-heading {
  display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 0 10px 4px;
  border: 0; background: none; font-size: 10.5px; font-weight: 700; letter-spacing: 0.9px; text-align: left;
  text-transform: uppercase; color: var(--muted);
}
.nav-heading:hover { background: none; color: var(--text); }
/* quiet: the fold chevron shows on hover or focus */
.nav-heading::after { content: ''; width: 6px; height: 6px; margin-right: 3px; border: solid currentColor;
  border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); opacity: 0; }
.nav-heading:is(:hover, :focus-visible)::after, .nav-heading[aria-expanded='false']::after { opacity: 0.6; }
.nav-heading[aria-expanded='false']::after { transform: rotate(-45deg); }
.nav-group.folded > :not(.nav-heading) { display: none; }
html[lang='zh-CN'] .nav-heading { letter-spacing: 0; text-transform: none; font-size: 11.5px; }
.sidebar nav .admin-nav { flex: none; padding-top: 8px; border-top: 1px solid var(--border); }
/* Admin → Automation (automation.js) */
.automation-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.automation-head h2 { margin-right: auto; }
.automation-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin: 0.5rem 0; }
.automation-build-state { margin: 0.25rem 0; }
.automation-form { display: flex; flex-wrap: wrap; align-items: end; gap: 0.5rem 1rem; margin: 0.5rem 0; }
.automation-form label { display: flex; flex-direction: column; gap: 0.2rem; }
.automation-form label.check { flex-direction: row; align-items: center; }
.automation-form input[type='number'] { width: 7rem; }
.automation-checks table { width: 100%; border-collapse: collapse; }
.automation-checks td { vertical-align: top; padding: 0.3rem 0.4rem; border-top: 1px solid var(--border, #ddd); }
.automation-checks td small { display: block; }
.automation-checks tr.check-block .pill { color: var(--danger); background: var(--danger-bg); }
.automation-checks tr.check-warn .pill { color: var(--warning); background: var(--warning-bg); }
.automation-notices { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
.automation-notices li {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 10px;
  border-left: 4px solid var(--border-strong); border-radius: 6px; background: var(--surface-alt);
}
.automation-notices li.notice-danger { border-left-color: var(--danger); background: var(--danger-bg); }
.automation-notices li.notice-danger .pill { color: var(--danger); background: transparent; border: 1px solid var(--danger); }
.automation-notices li.notice-warning { border-left-color: var(--warning); background: var(--warning-bg); }
.automation-notices li.notice-warning .pill { color: var(--warning); background: transparent; border: 1px solid var(--warning); }
.automation-notices li.notice-info { border-left-color: var(--accent); }
.automation-notices li.notice-info .pill { color: var(--accent); background: transparent; border: 1px solid var(--accent); }
.automation-notices .notice-clear { margin-left: auto; }
.automation-notices-head { margin-bottom: 10px; }
#admin-portal .notices-clear-all { padding: 2px 6px; border-color: transparent; background: transparent; color: var(--muted); font-size: 11px; }
#admin-portal .notices-clear-all:hover { color: var(--text); text-decoration: underline; }
.automation-notices .notice-details { flex-basis: 100%; }
.automation-notices .notice-details pre {
  max-height: 16rem; overflow: auto; white-space: pre-wrap; font-size: 12px; margin: 6px 0 0;
}
.job-progress.spend-split { display: flex; }
.job-progress.spend-split.warning { outline: 1px solid var(--warning); }
.spend-swatch { width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; }
/* the month's bar in its weeks: neighbouring weeks in two shades of one colour (#165) */
.job-progress-fill.spend-week.alt { opacity: 0.6; }
/* the legend under the bars, on one line: By task / By provider, then each task or provider with its colour */
.spend-legend { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px; margin: 6px 0 8px; font-size: 12px; }
.spend-key { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.spend-by { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.spend-by button { border: 0; border-radius: 0; padding: 2px 8px; font-size: 11px; background: transparent; color: var(--muted); }
.spend-by button[aria-pressed='true'] { background: var(--surface-alt); color: var(--text); font-weight: 600; }
.automation-queue { margin-top: 0.5rem; }
/* Spending: the nested day, week and month bars, with the limits behind a small toggle at their right */
.spend-top { display: flex; align-items: flex-start; gap: 10px; }
.spend-bars { flex: 1; min-width: 0; padding-left: 8px; border-left: 2px solid var(--border); }
.spend-bars > .bar-row:nth-child(2) { padding-left: 10px; }
/* the limits open over the panel's edge rather than being cut off by it */
.block.automation-spend { overflow: visible; }
.spend-limits { position: relative; flex: none; }
.spend-limits > summary { font-size: 12px; color: var(--muted); padding: 2px 6px 2px 18px; border: 1px solid var(--border); border-radius: 6px; }
.spend-limits > .automation-form {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 5; width: max-content; max-width: min(320px, 80vw);
  padding: 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); box-shadow: 0 4px 12px var(--shadow);
}
.pipeline-open { border: 0; padding: 0; background: none; color: var(--text); font: inherit; font-weight: 700; text-align: left; overflow-wrap: anywhere; }
.pipeline-open:hover { color: var(--link); text-decoration: underline; }
.tile .pipeline-state { display: flex; align-items: center; gap: 6px; }
.tile .job-outcome { color: var(--muted); overflow-wrap: anywhere; }
.tile .pipeline-waiting { color: var(--muted); overflow-wrap: anywhere; }
.tile .pipeline-waiting.notice { margin: 0; padding: 4px 8px; color: var(--warning); }
.tile .pipeline-memory { color: var(--muted); font-size: 12px; }
.tile .pipeline-memory.notice { margin: 0; padding: 4px 8px; color: var(--warning); }
.pipeline-icon { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 6px; color: var(--secondary); background: var(--surface); }
.pipeline-icon svg { width: 18px; height: 18px; }
/* round status icons in place of the pipeline's own: running (a spinning circle), done, needs attention */
.pipeline-icon.is-done { border-radius: 50%; }
.pipeline-icon.is-attention { background: var(--warning-bg); border-radius: 50%; }
.pipeline-icon.is-running { border-radius: 50%; }
.pipeline-icon.is-running::before {
  content: ''; width: 16px; height: 16px; border: 2px solid var(--border-strong); border-top-color: var(--accent);
  border-radius: 50%; animation: activity-spin 1s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .pipeline-icon.is-running::before { animation-duration: 3s; } }
dialog.pipeline-dialog { width: min(760px, calc(100vw - 32px)); }
.pipeline-dialog .dialog-heading { margin-bottom: 4px; }
/* Admin → Dev → Delivery Health (dev.js): the four tiles reuse the pipeline tiles; their sheets hold plain tables */
.delivery-grid + p { margin-bottom: 18px; }
/* its two Trend tiles: Activity's columns at the Trend template's 56px (#278) */
.delivery-bars { height: 56px; }
/* in a 580px sheet the long cells (titles) wrap rather than run past its edge */
.delivery-table { margin: 8px 0 14px; white-space: normal; }
.delivery-table td { overflow-wrap: anywhere; }
.automation-storage h3 { margin: 12px 0 4px; }
.job-progress.warning .job-progress-fill { background: var(--warning); }
/* ==== Spot, the dashboard helper (helper.js) ==== */
.helper-root {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  pointer-events: none;
}
.helper-root > * {
  pointer-events: auto;
}
/* room for Spot at the bottom right (bottom 20px + 76px button + 20px gap; phones below): the page and every popup
   keep it at their foot, so their last rows scroll clear of her, and a popup's bottom right panels open above her
   (#259; UI_RULES: floating things don't cover controls at rest) */
:root { --spot-room: 116px; }
.content {
  padding-bottom: var(--spot-room);
}
/* the demo panel (demo.js) owns the bottom right in local demos: Spot moves to the left of the content */
body:has(.demo-tour) .helper-root {
  right: auto;
  left: calc(var(--sidebar-width) + 20px);
  align-items: flex-start;
}
/* one Spot on screen: the corner one steps away while she peeks or celebrates (celebrate.js) */
body:has(.celebrate) .helper-root {
  visibility: hidden;
}
/* Spot floats on the page: no circle behind her, only a soft shadow (and a faint halo on the dark theme) */
.helper-button {
  position: relative;
  width: 80px;
  height: 76px;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: transparent;
  display: grid;
  place-items: end center;
  transition: transform 0.15s ease;
}
.helper-button:hover {
  transform: translateY(-3px);
  background: transparent;
}
.helper-button .spot-art {
  max-width: 100%;
  height: auto;
  filter: drop-shadow(0 3px 3px var(--shadow));
}
.helper-button:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.helper-badge {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 10px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 12px;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
}
.helper-badge[hidden] {
  display: none;
}
.helper-badge.dot {
  min-width: 12px;
  height: 12px;
  top: 4px;
  right: 4px;
  padding: 0;
}
/* Got it stays in view at the bottom of a long what's-new list */
.helper-foot {
  position: sticky;
  bottom: -14px;
  margin: 0 -14px -14px;
  padding: 10px 14px 14px;
  background: var(--surface);
  border-top: 1px solid var(--border);
}
.helper-art {
  display: grid;
  place-items: center;
}
/* Spot: pixel art, two or three frames per pose (blinking, trotting, glancing around) */
.spot-art {
  display: block;
  overflow: visible;
}
.spot-frame1,
.spot-frame2 {
  opacity: 0;
}
.spot-front .spot-frame0 {
  animation: spot-a 4.5s steps(1) infinite;
}
.spot-front .spot-frame1 {
  animation: spot-b 4.5s steps(1) infinite;
}
.spot-trot .spot-frame0 {
  animation: spot-step-a 0.5s steps(1) infinite;
}
.spot-trot .spot-frame1 {
  animation: spot-step-b 0.5s steps(1) infinite;
}
.spot-scamper .spot-frame0 {
  animation: spot-step-a 0.3s steps(1) infinite;
}
.spot-scamper .spot-frame1 {
  animation: spot-step-b 0.3s steps(1) infinite;
}
.spot-nibble .spot-frame0,
.spot-scratch .spot-frame0 {
  animation: spot-step-a 0.24s steps(1) infinite;
}
.spot-nibble .spot-frame1,
.spot-scratch .spot-frame1 {
  animation: spot-step-b 0.24s steps(1) infinite;
}
.spot-cheer .spot-frame0 {
  animation: spot-step-a 0.36s steps(1) infinite;
}
.spot-cheer .spot-frame1 {
  animation: spot-step-b 0.36s steps(1) infinite;
}
/* the idle poses on the button: slow and mostly still */
.spot-ear .spot-frame0 {
  animation: spot-a 6s steps(1) infinite;
}
.spot-ear .spot-frame1 {
  animation: spot-b 6s steps(1) infinite;
}
.spot-wag .spot-frame0 {
  animation: spot-step-a 2.4s steps(1) infinite;
}
.spot-wag .spot-frame1 {
  animation: spot-step-b 2.4s steps(1) infinite;
}
.spot-doze .spot-frame0 {
  animation: spot-step-a 4s steps(1) infinite;
}
.spot-doze .spot-frame1 {
  animation: spot-step-b 4s steps(1) infinite;
}
.spot-yawn .spot-frame0 {
  animation: spot-yawn-a 8s steps(1) infinite;
}
.spot-yawn .spot-frame1 {
  animation: spot-yawn-b 8s steps(1) infinite;
}
.spot-look .spot-frame0 {
  animation: spot-look-0 9s steps(1) infinite;
}
.spot-look .spot-frame1 {
  animation: spot-look-1 9s steps(1) infinite;
}
.spot-look .spot-frame2 {
  animation: spot-look-2 9s steps(1) infinite;
}
@keyframes spot-yawn-a {
  0%, 85%, 100% { opacity: 1; }
  65% { opacity: 0; }
}
@keyframes spot-yawn-b {
  0%, 85%, 100% { opacity: 0; }
  65% { opacity: 1; }
}
/* ahead, left, ahead, right, ahead */
@keyframes spot-look-0 {
  0%, 45%, 85%, 100% { opacity: 1; }
  30%, 70% { opacity: 0; }
}
@keyframes spot-look-1 {
  0%, 45%, 100% { opacity: 0; }
  30% { opacity: 1; }
}
@keyframes spot-look-2 {
  0%, 85%, 100% { opacity: 0; }
  70% { opacity: 1; }
}
@keyframes spot-a {
  0%, 93%, 97%, 100% { opacity: 1; }
  94% { opacity: 0; }
}
@keyframes spot-b {
  0%, 93%, 97%, 100% { opacity: 0; }
  94% { opacity: 1; }
}
@keyframes spot-step-a {
  0% { opacity: 1; }
  50% { opacity: 0; }
}
@keyframes spot-step-b {
  0% { opacity: 0; }
  50% { opacity: 1; }
}
.helper-button.has-news .spot-art {
  animation: spot-peek 2.4s ease-in-out infinite;
}
@keyframes spot-peek {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}
.helper-avatar {
  display: inline-grid;
  place-items: center;
  background: #eff1f1;
  border: 1px solid #d3d9da;
  border-radius: 8px;
  padding: 3px 4px;
  flex: none;
}
@media (prefers-reduced-motion: reduce) {
  .spot-art, .spot-art g { animation: none !important; }
  .spot-frame1, .spot-frame2 { opacity: 0; }
}
/* The panel is pixel art like Spot herself, to stand apart from the page: a 3px "pixel" grid, a one-pixel frame with
   corners rounded in four steps, and a hard pixel shadow. The frame's straight edges are box-shadows (they stop
   three pixels short of each corner); the steps inside the corners come from the head (ink, clipped) and from
   .helper-dev (clipped, with ink pixels). The panel itself paints nothing, so its square corners stay empty. */
.helper-panel {
  --px: 3px;
  --pixel-ink: var(--text);
  --pixel: linear-gradient(var(--pixel-ink), var(--pixel-ink));
  width: min(380px, calc(100vw - 40px));
  max-height: min(620px, calc(100vh - 110px));
  display: flex;
  flex-direction: column;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 13px;
  box-shadow:
    0 calc(var(--px) * -4) 0 calc(var(--px) * -3) var(--pixel-ink),
    0 calc(var(--px) * 4) 0 calc(var(--px) * -3) var(--pixel-ink),
    calc(var(--px) * -4) 0 0 calc(var(--px) * -3) var(--pixel-ink),
    calc(var(--px) * 4) 0 0 calc(var(--px) * -3) var(--pixel-ink);
  filter: drop-shadow(var(--px) var(--px) 0 var(--shadow));
  overflow: hidden;
}
.helper-panel :is(button, input, .helper-card, .helper-you, .helper-draft) {
  border-radius: 0;
}
.helper-panel[hidden],
.helper-step[hidden],
.helper-highlight[hidden] {
  display: none;
}
/* a title bar in the frame's ink; its clipped top corners are the frame's upper steps */
.helper-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 10px 10px 14px;
  background: var(--pixel-ink);
  color: var(--surface);
  clip-path: polygon(calc(var(--px) * 2) 0, calc(100% - var(--px) * 2) 0, calc(100% - var(--px) * 2) var(--px),
    calc(100% - var(--px)) var(--px), calc(100% - var(--px)) calc(var(--px) * 2), 100% calc(var(--px) * 2),
    100% 100%, 0 100%, 0 calc(var(--px) * 2), var(--px) calc(var(--px) * 2), var(--px) var(--px),
    calc(var(--px) * 2) var(--px));
}
.helper-head .helper-icon {
  color: inherit;
}
.helper-head .helper-icon:hover {
  background: transparent;
  opacity: 0.7;
}
.helper-who {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-right: auto;
}
.helper-icon {
  border: 0;
  background: transparent;
  font-size: 18px;
  line-height: 1;
  padding: 4px 8px;
}
.helper-body {
  background: var(--surface);
  padding: 12px 14px 14px;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.helper-body p,
.helper-rich p,
.helper-step p {
  margin: 0;
  font-size: inherit;
  color: inherit;
}
.helper-body .helper-note {
  font-size: 12px;
  color: var(--muted);
}
.helper-say {
  font-weight: 550;
}
.helper-you {
  align-self: flex-end;
  max-width: 85%;
  padding: 6px 10px;
  border-radius: 10px 10px 2px 10px;
  background: var(--soft);
}
.helper-thinking {
  color: var(--muted);
}
.helper-rich {
  display: flex;
  flex-direction: column;
  gap: 8px;
  line-height: 1.45;
}
.helper-rich ul,
.helper-rich ol {
  margin: 0;
  padding-left: 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.helper-title {
  margin: 0;
  font-size: 16px;
}
.helper-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  background: var(--canvas);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.helper-card h3 {
  margin: 0;
  font-size: 15px;
}
.helper-minor summary {
  cursor: pointer;
  color: var(--secondary);
  font-weight: 550;
}
.helper-minor ul,
.helper-history,
.helper-index {
  margin: 6px 0 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.helper-history {
  list-style: none;
  padding-left: 0;
}
.helper-date {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 12px;
}
.helper-draft {
  display: inline-block;
  padding: 0 6px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
  vertical-align: middle;
}
.helper-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.helper-actions.end {
  justify-content: flex-end;
}
.helper-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin-top: 4px;
}
.helper-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.helper-chip {
  padding: 5px 10px;
  border-radius: 999px;
  font-weight: 500;
  text-align: left;
}
.helper-link {
  border: 0;
  background: none;
  padding: 2px 0;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 500;
}
.helper-link:hover {
  background: none;
  color: var(--accent-hover);
}
.helper-links {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 4px;
}
.helper-ask {
  display: flex;
  gap: 6px;
}
.helper-ask input {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  background: var(--canvas);
  color: var(--text);
}
.helper-note {
  font-size: 12px;
  color: var(--muted);
}
.helper-error {
  color: var(--danger);
}
.helper-highlight {
  position: fixed;
  z-index: 58;
  border-radius: 10px;
  border: 2px solid var(--focus);
  box-shadow: 0 0 0 9999px var(--scrim);
  pointer-events: none;
  transition: left 0.2s ease, top 0.2s ease, width 0.2s ease, height 0.2s ease;
}
/* the panel's bottom: clipped and inked into the frame's lower steps, holding Spot's speech bubble */
.helper-dev {
  flex: none;
  padding: 2px 14px 14px;
  background:
    var(--pixel) left calc(var(--px) * 2) bottom 0 / var(--px) var(--px) no-repeat,
    var(--pixel) left var(--px) bottom var(--px) / var(--px) var(--px) no-repeat,
    var(--pixel) left 0 bottom calc(var(--px) * 2) / var(--px) var(--px) no-repeat,
    var(--pixel) right calc(var(--px) * 2) bottom 0 / var(--px) var(--px) no-repeat,
    var(--pixel) right var(--px) bottom var(--px) / var(--px) var(--px) no-repeat,
    var(--pixel) right 0 bottom calc(var(--px) * 2) / var(--px) var(--px) no-repeat,
    var(--surface);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--px) * 2), calc(100% - var(--px)) calc(100% - var(--px) * 2),
    calc(100% - var(--px)) calc(100% - var(--px)), calc(100% - var(--px) * 2) calc(100% - var(--px)),
    calc(100% - var(--px) * 2) 100%, calc(var(--px) * 2) 100%, calc(var(--px) * 2) calc(100% - var(--px)),
    var(--px) calc(100% - var(--px)), var(--px) calc(100% - var(--px) * 2), 0 calc(100% - var(--px) * 2));
}
/* a pixel speech bubble (2px frame, notched corners) with a stepped tail pointing down at Spot */
.helper-bubble {
  position: relative;
  margin: 0;
  padding: 7px 10px;
  background: var(--surface-alt);
  box-shadow: 0 -2px 0 var(--pixel-ink), 0 2px 0 var(--pixel-ink), -2px 0 0 var(--pixel-ink), 2px 0 0 var(--pixel-ink);
  font-size: 12px;
  line-height: 1.45;
  color: var(--secondary);
}
.helper-bubble::after {
  content: '';
  position: absolute;
  top: calc(100% + 2px);
  right: 22px;
  width: 6px;
  height: 6px;
  background: var(--pixel-ink);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 4px 100%, 4px 4px, 2px 4px, 2px 2px, 0 2px);
}
.helper-bubble .helper-link {
  font-size: 12px;
}
.helper-step {
  position: fixed;
  z-index: 61;
  width: min(340px, calc(100vw - 32px));
  padding: 12px 14px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  box-shadow: 0 12px 36px var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.helper-step-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.helper-step-head .muted {
  margin-left: auto;
  font-size: 12px;
  white-space: nowrap;
}
.helper-step .helper-actions {
  justify-content: flex-end;
}
.helper-step .helper-actions .helper-link {
  margin-right: auto;
}
@media (prefers-reduced-motion: reduce) {
  .helper-highlight { transition: none; }
}
.helper-root.touring .helper-panel {
  display: none;
}
/* One Spot on the page (#112): where a page shows her big (Home, an empty page), the corner one steps away */
body:has(section:not([hidden]) > .spot-stage) .helper-button {
  visibility: hidden;
}
.spot-stage {
  display: grid;
  justify-items: center;
  gap: 12px;
  margin: 48px auto 0;
  text-align: center;
}
.spot-stage .spot-art {
  filter: drop-shadow(0 4px 4px var(--shadow));
}
button.spot-big {
  padding: 0;
  border: 0;
  background: transparent;
}
button.spot-big:hover {
  background: transparent;
  transform: translateY(-3px);
}
/* cooking (an empty page, #111, #144): she stirs one way then the other, steam rises from the pot */
.spot-cooking {
  position: relative;
  margin-top: 12vh;
}
.spot-cook .spot-frame0 {
  animation: spot-step-a 1.2s steps(1) infinite;
}
.spot-cook .spot-frame1 {
  animation: spot-step-b 1.2s steps(1) infinite;
}
.spot-steam {
  position: absolute;
  top: 40%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--border-strong);
  opacity: 0;
  animation: spot-steam 2.4s ease-out infinite;
}
.spot-steam:nth-of-type(1) { left: calc(50% - 36px); }
.spot-steam:nth-of-type(2) { left: calc(50% + 26px); animation-delay: 0.8s; }
.spot-steam:nth-of-type(3) { left: calc(50% - 6px); animation-delay: 1.6s; }
@keyframes spot-steam {
  0% { transform: translateY(0) scale(0.6); opacity: 0; }
  30% { opacity: 0.7; }
  100% { transform: translateY(-60px) scale(1.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .spot-steam { display: none; }
}
/* Home: big Spot and what's new since the last visit */
.spot-home {
  max-width: 640px;
}
.spot-home .helper-card,
.spot-home .helper-minor {
  width: 100%;
  text-align: left;
}
.spot-hello {
  margin: 0;
  font-size: 22px;
  font-weight: 650;
  color: var(--text);
}
/* Home (#177): Spot in the middle of the page, her ask bar and options right under her, her answers below them,
   drawn by the same views as her dialog (helper.js conversation) */
.spot-centre {
  margin-top: 10vh;
}
.spot-talk {
  width: 100%;
}
.spot-talk > .helper-body {
  padding: 0;
  background: none;
  overflow: visible;
}
.spot-talk .helper-chips {
  justify-content: center;
}
.spot-reply {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  background: var(--surface);
  text-align: left;
}
/* Home's welcome (#170), once per page load: Spot hops in waving (cheer), sparkles twinkle, then she sits (front).
   Transforms and opacity only, and the wave and sparkles sit on top of her (absolute): nothing else on Home moves. */
.spot-hop {
  position: relative;
  display: block;
}
.spot-arrive {
  transform-origin: 50% 100%;
  animation: spot-hop-in 1.6s ease-out both;
}
.spot-arrive > .spot-front {
  animation: spot-sit-down 1.6s linear both;
}
.spot-wave {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  animation: spot-wave-off 1.6s linear both;
}
.spot-twinkle {
  position: absolute;
  animation: spot-twinkle 0.9s steps(4, end) both;
}
.spot-twinkle0 { left: -34px; top: 34px; animation-delay: 0.3s; }
.spot-twinkle1 { right: -44px; top: -18px; animation-delay: 0.45s; }
.spot-twinkle2 { right: -30px; top: 82px; animation-delay: 0.65s; }
.spot-arriving > .spot-hello,
.spot-arriving > .helper-say {
  animation: spot-greet-in 0.5s 0.5s ease-out both;
}
@keyframes spot-hop-in {
  0% { opacity: 0; transform: translateY(48px) scale(0.7); }
  20% { opacity: 1; transform: translateY(-20px) scale(1); }
  32% { transform: translateY(0) scale(1.06, 0.94); }
  42% { transform: translateY(-10px) scale(1); }
  52%, 100% { transform: none; }
}
@keyframes spot-sit-down {
  0%, 75% { opacity: 0; }
  76%, 100% { opacity: 1; }
}
@keyframes spot-wave-off {
  0%, 75% { opacity: 1; }
  76%, 100% { opacity: 0; }
}
@keyframes spot-twinkle {
  0% { opacity: 0; transform: scale(0.2); }
  40% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.6); }
}
@keyframes spot-greet-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .spot-arrive, .spot-arrive > .spot-front, .spot-arriving > * { animation: none; }
  .spot-wave, .spot-twinkle { display: none; }
}
/* New / Updated on a sidebar entry or a tab (#107): the person hasn't used it since it shipped or changed */
a[data-fresh]::after {
  content: attr(data-fresh);
  margin-left: 6px;
  padding: 0 5px;
  border-radius: 5px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 9px;
  font-weight: 700;
  line-height: 14px;
  white-space: nowrap;
}
nav a[data-fresh]::after {
  margin-left: auto;
}
/* on a tab of a tab strip, in its upper right corner, so the tab keeps its width (WB.tabStrip: the bold-width copy is
   a span, not this ::after); inside the tab's own part (the next folder overlaps its last 6px), in the room the strip
   keeps above the folders (the strip clips what is above it) and in front of the folders beside it: only the current
   tab (z-index 2) covers it (#205) */
.tab-strip > a[data-fresh]::after {
  position: absolute;
  z-index: 1;
  top: -8px;
  right: 6px;
  margin: 0;
  pointer-events: none;
}
/* a walkthrough of something new must be finished before the page can be used (#108): the page takes no clicks */
.helper-block {
  position: fixed;
  inset: 0;
  z-index: 57;
}
.helper-block.dim {
  background: var(--scrim);
}
.helper-block[hidden] {
  display: none;
}
/* No Spot on phones (Haofeng, 2026-10-30): the bottom right is the mic's (dictate.js), the bottom left chat's; her
   walkthroughs don't start there either (helper.js). The page keeps room at its foot for the chat button and its two
   rows of sound buttons (16px + 96px + a gap), so the last of a page is never under them. The demo panel (local
   copies) goes to the top. */
@media (max-width: 720px) {
  :root { --spot-room: calc(124px + env(safe-area-inset-bottom)); }
  .helper-root, .helper-block, .helper-highlight, .helper-step { display: none !important; }
  .demo-tour { top: calc(56px + env(safe-area-inset-top)); bottom: auto; right: 12px; }
}

/* Part details -> Picture (wbench.illustrations) */
.record-picture .picture-body { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.record-picture .picture-frame { background: var(--surface-alt); border-radius: 8px; line-height: 0; }
.record-picture img.illustration, .record-picture img.photo { width: 220px; height: 220px; object-fit: contain; }
.record-picture img.illustration { filter: var(--picture-filter); }
.part-thumbnail.illustration img { filter: var(--picture-filter); }
.record-picture .picture-facts { flex: 1 1 220px; min-width: 0; }
.record-picture .picture-facts p { margin: 0 0 6px; }
.picture-report { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.picture-answers { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.picture-answers[open] { display: flex; flex-direction: column; gap: 6px; }
.picture-answers summary { cursor: pointer; font-weight: 600; }
.picture-answers input { width: 100%; }
.picture-answer-buttons { display: flex; flex-wrap: wrap; gap: 6px; }
/* ==== Projects (projects.js) and tabs (wbench/tabs/) ==== */
#projects-portal, #tab-portal, #discipline-files { padding: 4px 0 40px; }
.projects-head, .files-bar, .inline-form, .project-form { display: flex; flex-wrap: wrap; gap: 8px;
  align-items: center; }
.projects-head { margin-bottom: 16px; }
.project-form, .inline-form { margin: 10px 0; }
.project-form input, .inline-form input { min-width: 160px; }
.form-error { color: var(--danger); width: 100%; margin: 4px 0 0; }
/* All Projects' type filter above its one grid (#241) */
.projects-list > .projects-types { display: flex; width: fit-content; max-width: 100%; flex-wrap: wrap; margin: 0 0 8px; }
.project-link { font-weight: 550; color: var(--text); text-decoration: none; }
.project-link:hover { color: var(--accent); text-decoration: underline; }
.project-meta { color: var(--muted); font-size: 12.5px; }
.projects-head h2 { margin: 0 0 0 8px; font-size: 20px; }
/* Tiles (the standard, docs/UI_RULES.md "Tiles"; ui.js tileGrid, tile, blocks, block). Items of a collection are
   tiles of one size per grid: L (a state, lines and actions), M (an item you open: square, #127), S (a number); each
   grid fills its row, and on a phone L tiles are one to a row, M and S two. Sections of a page are blocks on six
   columns: full, half, third, two-thirds; under 1100px a third takes half and two-thirds all; on a phone every block
   takes the full width. Haofeng's picks (2026-10-29): L 360 · M 240 · S 180, the name and status in a head band,
   tight (8/10px inside tiles and blocks, 8px between tiles; blocks keep 16px), M at least square, blocks framed;
   every tile of a grid one height (the tallest's), a tag below the title rather than beside a squeezed one (#241).
   Radius 0 (data), no shadow; a block inside a block has no frame of its own. */
.tile-grid { --tile-min: 240px; display: grid; gap: 8px; margin: 0 0 8px; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--tile-min), 100%), 1fr)); grid-auto-rows: 1fr; }
.tile-grid[data-size='l'] { --tile-min: 360px; }
.tile-grid[data-size='s'] { --tile-min: 180px; }
/* an M tile is exactly 240px wide: the row leaves space at its end rather than stretch it (Haofeng, 2026-10-29:
   a fixed size is what a tile's interior can be laid out for) */
.tile-grid[data-size='m'] { grid-template-columns: repeat(auto-fill, min(240px, 100%)); }
.tile-grid.tile-rows { grid-template-columns: repeat(var(--cols, 3), minmax(0, 240px)); }
.tile { position: relative; display: flex; flex-direction: column; gap: 5px; min-width: 0; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 0; background: var(--surface); color: var(--text); overflow-wrap: anywhere; }
/* an M tile is at least square (240 × 240), taller when its content needs it (then its whole grid is); always its
   column's width: an item with a ratio isn't stretched across its column by default, so a tall one took its width
   from its height and ran off a phone (#880) */
.tile-grid[data-size='m'] > .tile, .tile-grid[data-size='m'] > .create-tile-item { aspect-ratio: 1; align-self: stretch; justify-self: stretch; }
/* the head band: the name and status in a filled strip across the top, edge to edge. The tags (one span) sit beside
   the title while the whole title fits beside them, else on their own line below it; in M and S tiles always below
   (#241: "Tags are less important than the title"), and a step smaller than other pills (9px, as the Proto tag).
   A title takes two lines at most, then "…" (its tooltip).
   Every head band of a grid is one height (#244: "All top bars should be the same height"): the title keeps room
   for its two lines, and in a grid with tags every head keeps room for a line of them too (4px apart, 16px high),
   so the bodies start level; the head's own font size is the title's (a page sizes its titles on the head). */
.tile-head { display: flex; align-items: flex-start; align-content: flex-start; gap: 4px 8px; min-width: 0;
  margin: -8px -10px 0; padding: 6px 10px; background: var(--soft); border-bottom: 1px solid var(--border); }
.tile-head.has-tag { flex-wrap: wrap; }
.tile-head > .tile-title, .tile-head > .pipeline-open { flex: 1 1 auto; min-width: 0; min-height: 2lh; }
.tile-grid:has(> .tile > .tile-head > .tile-tags) > .tile > .tile-head { min-height: calc(2lh + 4px + 16px + 13px); }
.tile-title { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  font-weight: 600; color: var(--text); text-decoration: none; overflow-wrap: anywhere; }
button.tile-title { padding: 0; border: 0; background: none; font: inherit; font-weight: 600; color: var(--text);
  text-align: left; cursor: pointer; }
.tile-tags { flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-height: 16px; max-width: 100%;
  margin-left: auto; }
.tile-grid:is([data-size='m'], [data-size='s']) .tile-tags { flex-basis: 100%; margin-left: 0; }
/* there the title shares its row only with the icon: it wraps inside it (two lines) rather than drop below the icon
   in a narrow tile (half a phone), which made that head taller than its neighbours' (#880) */
.tile-grid:is([data-size='m'], [data-size='s']) .tile-head > :is(.tile-title, .pipeline-open) { flex: 1 1 0; }
.tile .tile-tags > * { font-size: 9px; line-height: 12px; }
.tile .tile-tags > .pill { padding: 1px 4px; }
/* Quick view (tile({ quickView })): a side-sheet icon at the top right, above a link that covers the tile */
.tile-quick { position: absolute; top: 3px; right: 3px; z-index: 2; display: grid; place-items: center; width: 28px;
  height: 28px; padding: 0; border: 1px solid transparent; border-radius: 0; background: none; color: var(--muted);
  cursor: pointer; }
.tile-quick:is(:hover, :focus-visible) { border-color: var(--border-strong); background: var(--surface); color: var(--text); }
.tile.has-quick > .tile-head { padding-right: 36px; }
.quick-view-body { display: flex; flex-direction: column; gap: 8px; }
.quick-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.quick-facts { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 0; }
.quick-facts dt { color: var(--muted); }
.quick-facts dd { margin: 0; }
.tile-line { color: var(--muted); font-size: 12.5px; }
.tile-value { font-size: 22px; font-weight: 550; line-height: 1.2; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.tile-grid[data-size='s'] .tile-head { font-size: 12.5px; }
.tile-grid[data-size='s'] .tile-title { font-weight: 500; color: var(--secondary); }
/* the actions at the foot of every tile, whatever is above them (#165.5), above a link that covers the tile */
.tile-foot { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: 4px; margin-top: auto; padding-top: 4px; }
.tile-foot button { font-size: 11px; padding: 3px 6px; }
.tile-link .tile-title::after { content: ''; position: absolute; inset: 0; }
.tile-link .tile-title:hover { text-decoration: underline; }
.tile-open { cursor: pointer; }
.tile:is(.tile-link, .tile-open):hover, .tile:focus-within { border-color: var(--border-strong); }
.tile.selected { border-color: var(--border-strong); }
.blocks { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 16px; align-items: start; margin: 0 0 16px; }
.blocks > * { grid-column: span 6; min-width: 0; }
.blocks > [data-span='half'] { grid-column: span 3; }
.blocks > [data-span='third'] { grid-column: span 2; }
.blocks > [data-span='two-thirds'] { grid-column: span 4; }
.block { min-width: 0; padding: 8px 10px; border: 1px solid var(--border); border-radius: 0; background: var(--surface);
  overflow-x: auto; }
.block + .block { margin-top: 16px; }
.blocks > .block { margin: 0; }
.block > h3:first-child, .block-head h3 { margin: 0 0 8px; font-size: 14px; font-weight: 600; }
.block-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.block-head h3 { margin: 0; }
.block-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-left: auto; }
.block .block { padding: 0; border: 0; background: none; }
/* block({ scroll: true }): no taller than this, its body scrolls under the head and the table's header row (#199.1) */
.block[data-scroll] { display: flex; flex-direction: column; max-height: min(480px, 70dvh); overflow: hidden; }
.block-body { min-height: 0; overflow: auto; }
.block-body th { position: sticky; top: 0; z-index: 1; background: var(--surface); }
/* .scroll-left: a list that scrolls by itself (#267.1; a proposed pattern, not in UI_RULES yet): no taller than most
   of the window, so what is above it (the page's tabs, its find bar and sort) stays in view while the items scroll;
   its scroll bar is wide and on the left, beside where the items start. */
.scroll-left { max-height: 60dvh; overflow-y: auto; overscroll-behavior: contain; direction: rtl; padding-left: 8px; }
.scroll-left > * { direction: ltr; flex-shrink: 0; }   /* in a flex list, items keep their height and the list scrolls */
.scroll-left::-webkit-scrollbar { width: 16px; }
.scroll-left::-webkit-scrollbar-track { background: var(--soft); }
.scroll-left::-webkit-scrollbar-thumb { background: var(--border-strong); border: 3px solid var(--soft); }
@media (max-width: 1100px) {
  .blocks > [data-span='third'] { grid-column: span 3; }
  .blocks > [data-span='two-thirds'] { grid-column: span 6; }
  .blocks > [data-span='two-thirds'] + [data-span='third'] { grid-column: span 6; }   /* its side panel follows it */
}
@media (max-width: 720px) {
  .tile-grid:is([data-size='m'], [data-size='s']), .tile-grid.tile-rows { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tile-grid[data-size='l'] { grid-template-columns: minmax(0, 1fr); }
  .blocks > [data-span] { grid-column: span 6; }
}
/* The "Create new" / "Upload" tile (ui.js createTile, #162): the size of the tiles beside it, a dotted outline */
.create-tile { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  box-sizing: border-box; width: 100%; height: 100%; aspect-ratio: 1; padding: 16px; text-align: center;
  font: inherit; border: 1.5px dashed var(--border-strong); border-radius: 10px; background: none;
  color: var(--muted); text-decoration: none; cursor: pointer; }
.create-tile:hover, .create-tile:focus-visible, .create-tile.dragging, .create-tile[aria-pressed='true'] {
  border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, transparent); }
.create-tile-label { font-weight: 600; color: var(--text); white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.create-tile-hint { font-size: 11.5px; line-height: 1.4; white-space: normal; } /* a hint wraps inside its tile (buttons' labels don't) */
/* the create tile: last, square in an M grid, as tall as its row in the others */
.tile-grid > .create-tile-item { display: flex; min-width: 0; }
.tile-grid:not([data-size='m']) .create-tile { aspect-ratio: auto; min-height: 84px; }
/* Toasts (ui.js toast): the bottom left of the page beside the sidebar, newest last; under the phone drawer */
.toast-stack { position: fixed; left: calc(var(--sidebar-width) + 16px); bottom: 16px; z-index: 38; display: flex;
  flex-direction: column; gap: 8px; width: min(340px, calc(100vw - var(--sidebar-width) - 140px)); max-height: 50vh;
  overflow-y: auto; pointer-events: none; }
.toast-stack:empty { display: none; }
.toast { display: flex; align-items: flex-start; gap: 8px; padding: 10px 8px 10px 12px; pointer-events: auto;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent); border-radius: 8px; box-shadow: 0 6px 18px var(--shadow); font-size: 13.5px; }
.toast-body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.toast-text { font-weight: 600; color: var(--text); }
a.toast-text:hover { color: var(--accent); }
.toast-detail { color: var(--muted); font-size: 12.5px; }
.toast-action { align-self: flex-start; margin-top: 4px; }
.toast-close { flex: none; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 6px; background: none;
  color: var(--muted); font-size: 18px; line-height: 1; cursor: pointer; }
.toast-close:hover { background: var(--soft); color: var(--text); }
@media (max-width: 720px) { .toast-stack { left: 12px; width: calc(100vw - 124px); } }
/* the toasts carry the page's notices: above a sheet and its scrim (43), under the top bar (45) */
.toast-stack { z-index: 44; }
.template-form .setup-field { width: 100%; }
.template-docs { margin: 0; padding-left: 18px; }
.project-setup { width: min(560px, calc(100vw - 32px)); }
.project-setup h4 { margin: 4px 0 0; }
.setup-field { display: grid; gap: 4px; }
.setup-field > span { font-weight: 600; color: var(--secondary); }
.setup-drop { border: 2px dashed var(--border); border-radius: 8px; padding: 20px; text-align: center; color: var(--muted);
  cursor: pointer; }
.setup-drop.dragging { border-color: var(--accent); color: var(--accent); }
.project-crumbs { display: flex; gap: 6px; font-size: 13px; color: var(--muted); margin-bottom: 4px; }
.project-crumbs a { color: var(--accent); }
.project-tabs { margin: 6px 0 10px; }
.project-edit { display: inline-flex; padding: 2px; margin-left: -4px; }
/* the title's own text box while renaming (#126): the name's font and box, so nothing around it moves */
#crumb .crumb-name input.project-rename { display: block; font: inherit;
  letter-spacing: inherit; color: inherit; padding: 0; margin: 0; border: 0; border-radius: 3px; background: none;
  box-shadow: 0 0 0 2px var(--accent); field-sizing: content; }
/* Dev mode (#146): a sidebar link to a page or tab that is not Live gets a small tag at the end of the line
   (app.js markSidebar) */
.proto-pill.nav-launch { flex: none; width: auto; margin-left: auto; font-size: 9px; line-height: 12px; padding: 0 3px;
  pointer-events: none; }
/* after a count, or before a New/Updated tag, it shares the right end with it */
nav a > :is(b, .nav-badge) ~ .proto-pill.nav-launch { margin-left: 4px; }
nav a[data-fresh]:has(> .nav-launch)::after { margin-left: 4px; }
.notice-line { margin: 0 0 12px; }
.error-line { color: var(--danger); }
.project-description { margin: 2px 0 0; color: var(--secondary); }
.project-facts { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 2px 0 0; }
.block .project-table td, .block .project-table th { white-space: normal; }
.block .inline-form input { min-width: 0; flex: 1 1 120px; }
.block .inline-form input[type='number'] { flex: 0 0 64px; }
.block .inline-form select { flex: 1 1 160px; min-width: 0; }
.plain-list { list-style: none; margin: 0 0 8px; padding: 0; }
.plain-list li { padding: 3px 0; }
.muted { color: var(--muted); }
.project-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.project-table th { text-align: left; font-weight: 550; color: var(--muted); font-size: 12.5px;
  border-bottom: 1px solid var(--border); padding: 4px 6px; }
.project-table td { border-bottom: 1px solid var(--border); padding: 6px; vertical-align: middle; }
.project-table tr:last-child td { border-bottom: 0; }
/* Files: the Finder list (files.js fileFinder) | the selected file's preview (the right half), plain 1px lines
   between areas (#125) */
/* The view pane's tabs (files.js fileTabs): the AI Agent page's browser pane and a project's Files (#1073) */
.cb-tabs {
  display: flex; gap: 2px; align-items: stretch; overflow-x: auto; scrollbar-width: thin;
  border-bottom: 1px solid var(--border);
}
/* many tabs: each shrinks to 72px, then the strip scrolls */
.cb-tab { display: inline-flex; align-items: center; flex: 0 1 200px; min-width: 72px; border-radius: 5px 5px 0 0; }
.cb-tab-file .cb-tab-name { font-style: italic; }
.cb-tab.current { background: var(--selected-bg); }
.cb-tab button { border: 0; background: transparent; color: var(--text); font-weight: 400; }
.cb-tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1 1 auto;
  text-align: left; }
/* the close × at the tab's right end, after the name (#1099) */
.cb-tab-close { margin-left: auto; }
.cb-tab-close, .cb-tab-new { color: var(--muted) !important; padding: 2px 6px; flex: none; display: inline-flex; align-items: center; }
.cb-tab-new { border: 0; background: transparent; }
.project-files { font-size: 13px; background: var(--surface); color: var(--text); border: 1px solid var(--border); }
.project-files a:not(.button) { color: var(--link); text-decoration: underline; text-underline-offset: 2px; }
.project-files button, .project-files .button, .project-files .file-preview iframe,
.project-files .file-preview img { font-weight: 400; }
.project-files button:not(.linkish):not(.finder-toggle):not(.primary) { background: var(--surface); }
.project-files button:not(.linkish):not(.finder-toggle):not(.primary):hover { background: var(--soft); }
.project-files .notice-line { padding: 4px 10px; color: var(--muted); }
.project-files .muted { color: var(--muted); }
/* the inspector viewer (files B, owner 2026-10-31): three fixed columns, no divider to drag: the names (300px) | the
   file, filling its pane's width | the inspector (270px; Info hides it) */
.files-panes { display: grid; grid-template-columns: 300px minmax(0, 1fr) 270px;
  height: calc(100vh - 230px); min-height: 320px; border-bottom: 1px solid var(--border); }
.files-panes.no-info { grid-template-columns: 300px minmax(0, 1fr); }
/* a project's own Files (#1073): as My Files on the AI Agent page, a 260px list and the open files as tabs over the
   viewer (files.js fileTabs) */
@media (min-width: 1100px) {
  .files-workspace .files-panes { grid-template-columns: 260px minmax(0, 1fr) 270px; }
  .files-workspace .files-panes.no-info { grid-template-columns: 260px minmax(0, 1fr); }
}
.files-preview > .cb-tabs { flex: none; padding: 4px 6px 0; background: var(--surface); }
.files-list, .files-preview, .files-inspector { min-width: 0; overflow: auto; }
.files-preview { border-left: 1px solid var(--border); background: var(--canvas); }
/* a drag over the file list, the drop zone (#269): a solid line round it (no dashed outline, #189) and the drop
   text darkens */
.files-list.dragging { outline: 2px solid var(--accent); outline-offset: -2px; }
.project-files:has(.files-list.dragging) .files-drop-hint { color: var(--text); }
.file-row { cursor: default; }
.file-row:hover td { background: var(--focus-soft); }
.file-row.selected td { background: var(--focus-soft); font-weight: 550; }
.file-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* the centre is a minimal viewer (#193): its own line (‹ › · name · Draw · the fit · Expand · Info, one line, one
   control height, never wrapping: the name shortens), the file filling the rest of the pane's width. Pages (.paged:
   PDF, pictures, sketches) fit its width or its height (.fit-height); other files fill it as they are. Expand
   (.wide) covers the names and the inspector: the whole content area under the top bar (projects.js measures it). */
.files-preview { display: flex; flex-direction: column; overflow: hidden; container-type: inline-size; }
/* a narrow centre pane (a small window, Dev mode's stack): the line still fits on one line. Draw keeps its pen and
   loses its word, then the fit says Width / Height, then ‹ › go (the arrow keys still move) */
.files-fit .fit-short { display: none; }
@container (max-width: 560px) { .files-preview-head .kit-button { font-size: 0; gap: 0; } }
@container (max-width: 480px) { .files-fit .fit-long { display: none; } .files-fit .fit-short { display: inline; } }
@container (max-width: 380px) { .files-preview-head [data-focus^='preview:chevron'] { display: none; } }
.files-preview-head { display: flex; align-items: center; gap: 6px; flex: none; min-width: 0; height: 40px;
  padding: 0 10px 0 6px; border-bottom: 1px solid var(--border); background: var(--surface); white-space: nowrap; }
.files-preview-head > * { flex: none; }
.files-preview-head :is(button, .button) { height: var(--control-h-sm); min-height: var(--control-h-sm); }
.files-preview-head button.icon-button.small { width: var(--control-h-sm); }
.files-preview-head .view-toggle { margin: 0; order: initial; height: var(--control-h-sm); }
.files-preview-head .view-toggle button { min-width: 0; height: 100%; min-height: 0; }
.files-preview-head .kit-button svg { width: 16px; height: 16px; }
.files-preview-head [aria-pressed='true'] { background: var(--selected-bg); }
.files-preview h3 { margin: 0 auto 0 4px; flex: 0 1 auto; min-width: 48px; font-size: 14px; font-weight: 600; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* Rename sits right after the name (#1104), its word kept on a narrow pane; the rest of the line goes to the right */
.files-preview-head h3:has(+ .files-rename-pen) { margin-right: 0; }
.files-preview-head .files-rename-pen { margin-right: auto; }
.files-preview-head .kit-button.files-rename-pen { font-size: 12px; gap: 4px; }
.files-preview-head .files-rename { flex: 1 1 auto; min-width: 48px; height: var(--control-h-sm); font-size: 14px; font-weight: 600; }
.files-preview > .muted { margin: 14px; }
.files-preview > :is(.file-preview, .draw) { flex: 1; min-height: 0; overflow: auto; padding: 10px 12px; }
.files-preview.paged > :is(.file-preview, .draw) { padding: 0; }
.files-preview > .draw { gap: 0; overflow: hidden; }
.files-preview .file-preview iframe { min-height: 0; border: 0; border-radius: 0; }
.files-preview .file-preview pre { max-height: none; font: 12px/1.5 var(--font-mono); }
/* the pen's own line under it only while drawing (its colours, Eraser, Undo, Redo) or for a sketch's pages */
.files-preview .draw .draw-bar { display: none; flex-wrap: nowrap; overflow-x: auto; padding: 4px 8px;
  border-bottom: 1px solid var(--border); background: var(--surface); }
body.draw-mode .files-preview .draw .draw-bar:has(.draw-ink-tools),
.files-preview .draw .draw-bar:has(.draw-page-no:not(:empty), .draw-tools:not(.draw-ink-tools) button) { display: flex; }
body:not(.draw-mode) .files-preview .draw-ink-tools { display: none; }
/* Fit to width: the page as wide as the pane; Fit to height: a whole page in the pane's height, centred */
.files-preview.fit-width .file-preview img { width: 100%; max-width: none; max-height: none; height: auto; border-radius: 0; }
/* centred by auto margins, so a page wider than the pane starts at its left edge and scrolls */
.files-preview.fit-height .file-preview { align-items: flex-start; }
.files-preview.fit-height .file-preview img { height: 100%; width: auto; max-width: none; max-height: none;
  margin: 0 auto; object-fit: contain; border-radius: 0; }
.files-preview.fit-height .draw-pages { align-items: flex-start; }
.files-preview.fit-height .draw-page { width: auto; height: 100%; max-width: none; margin: 0 auto; }
.files-preview.paged .draw-pages { gap: 1px; background: var(--border); }
/* Zoom (#1122, files.js fileZoom): − the percentage + after the fit; zoomed, the pages are --zoom times the pane's
   width (100% = Fit to width), centred while narrower, scrolling both ways once wider. The percentage is a quiet
   button (back to Fit to width) of a fixed width, so the line doesn't move as it changes */
.files-zoom { display: inline-flex; align-items: center; gap: 2px; }
.files-preview-head .files-zoom-pct { width: 46px; min-width: 46px; padding: 0; border: 0; background: none; box-shadow: none;
  color: var(--text); font-size: 12px; font-variant-numeric: tabular-nums; cursor: pointer; }
.files-preview-head .files-zoom-pct:hover { background: var(--focus-soft); }
.files-preview[data-zoom] .draw-pages, .files-preview[data-zoom] > .file-preview { align-items: flex-start; }
.files-preview[data-zoom] .draw-page { width: calc(var(--zoom) * 100%); height: auto; max-width: none; margin: 0 auto; }
.files-preview[data-zoom] > .file-preview > img { width: calc(var(--zoom) * 100%); height: auto; max-width: none;
  max-height: none; margin: 0 auto; object-fit: contain; border-radius: 0; }
/* a BOM shown the one way (files.js bomTable, bomView, #205.3): money right-aligned, the total row bold */
.bom-view-head { margin: 0 0 6px; }
table.bom-lines { width: 100%; font-size: 12.5px; }
table.bom-lines :is(td, th) { padding: 6px 8px; }
table.bom-lines .bom-money { text-align: right; white-space: nowrap; }
table.bom-lines .bom-qty { white-space: nowrap; }
table.bom-lines .items-pn { overflow-wrap: anywhere; }
table.bom-lines .bom-status .pill { white-space: nowrap; }
table.bom-lines .bom-flag { color: var(--warning); font-size: 11.5px; }
table.bom-lines .bom-total td { font-weight: 600; border-top: 2px solid var(--border); }
/* its line starts below Dev Comments while it floats there (app.js placePopups, --corner-top), so Collapse and Info
   are never under it (#1041) */
.files-panes.wide > .files-preview { position: fixed; z-index: 25; top: var(--cover-top, 0px); left: var(--cover-left, 0px);
  right: 0; bottom: 0; border: 0; padding-top: max(0px, var(--corner-top, 0px) - var(--cover-top, 0px)); }
/* the inspector (files.js fileInspector): Info, History (one click opens a revision), Drawings, Actions with their
   labels, Delete apart at the end */
/* its own scroll ends at the bottom right, under Spot: its foot keeps her room, so the last rows (History's Download
   of an old version) scroll clear of her (#391) */
.files-inspector { border-left: 1px solid var(--border); font-size: 12.5px; padding-bottom: var(--spot-room); }
.files-inspector-empty { margin: 14px; }
.files-inspector-section { padding: 10px 14px; border-bottom: 1px solid var(--border); }
.files-inspector h4 { margin: 0 0 8px; font-size: 11px; font-weight: 700; color: var(--muted); }
.files-inspector dl { grid-template-columns: max-content minmax(0, 1fr); gap: 5px 12px; font-size: inherit; }
.files-inspector :is(dt, dd) { padding: 0; border: 0; line-height: 1.45; white-space: normal; }
.files-inspector dt { color: var(--muted); white-space: nowrap; overflow-wrap: normal; }
.files-revisions { display: grid; gap: 2px; }
.project-files button.files-revision { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 8px; width: 100%; border: 0;
  border-radius: 5px; padding: 4px 6px; background: transparent; color: var(--secondary); font: inherit; text-align: left;
  cursor: pointer; }
.project-files button.files-revision:hover { background: var(--soft); }
.project-files button.files-revision[aria-pressed='true'] { background: var(--selected-bg); color: var(--text); }
.files-revision-name { display: inline-flex; align-items: center; gap: 5px; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.files-revision-when { flex: none; color: var(--muted); }
/* a revision's note (its check-in's "What changed"), under its line */
.files-revision-note { flex: 1 1 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
/* Locks (UI_RULES "Locks"): a file someone else has checked out. In the list a lock and their avatar only (name on
   hover); in the inspector the lock, avatar and name, since when, the time left, Notify me and Ask to check in */
.files-lock-mark { display: inline-flex; flex: none; align-items: center; gap: 2px; margin-left: auto; color: var(--muted); }
.files-lock-mark .icon { width: 12px; height: 12px; }
.files-lock-state { display: grid; gap: 4px; }
.files-lock-who { display: flex; align-items: center; gap: 6px; }
.files-lock-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; margin-top: 4px; }
/* the note in an editor's Save area: one line, beside Save */
.markdown-bar .edit-note { flex: 1 1 auto; max-width: 320px; }
.markdown-bar .edit-note input { height: 100%; }
.files-inspector-section > .switch { margin: 0 0 6px; }
.files-inspector-actions { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.files-inspector-actions > :is(button, .button) { justify-content: flex-start; text-align: left; }
/* its downloads are links that look like the buttons beside them (keycap, one height) */
.files-inspector a.button { display: flex; align-items: center; gap: 6px; border: 1px solid var(--border);
  border-bottom: 2px solid var(--keycap-edge); background: var(--surface); color: var(--text); text-decoration: none;
  font-weight: 400; }
.files-inspector a.button:hover { background: var(--soft); }
.files-inspector-section > a.button { margin-top: 6px; }
.files-inspector-actions > .pill { align-self: flex-start; white-space: normal; }
.files-inspector-danger { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
/* Send for Review in the inspector (files.js reviewSender, wbench.file_reviews): the picker, the people picked as
   avatars, the message, the key action; then the file's requests (avatar, when, resolved or not). A review sent to
   the person (reviewReceived): the sender's avatar, the message, Mark as resolved. The key action keeps its accent
   inside a file browser (whose plain buttons are --surface). */
.project-files .files-inspector button.primary { background: var(--accent); color: var(--on-accent); }
.project-files .files-inspector button.primary:hover:not(:disabled) { background: var(--accent-hover); }
.review-form { display: grid; gap: 8px; }
.review-status:empty, .review-list:empty { display: none; }
/* the picker (files.js peoplePicker, #1023): the + (a small keycap, square) and whom it goes to as avatars, 4px
   apart (five fit the 200px right bar); the panel under it, the people's avatars then the projects' initials,
   scrolling past a few rows; a picked one is the soft fill, the rest greyed once the limit is reached */
.review-picked { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.review-form button.review-add { width: var(--control-h-sm); padding: 0; justify-content: center; }
.review-panel { display: grid; gap: 6px; max-height: 180px; overflow-y: auto; border: 1px solid var(--border); padding: 6px;
  background: var(--surface); }
.review-panel[hidden] { display: none; }
.review-faces { display: flex; flex-wrap: wrap; gap: 4px; }
.review-panel-head, .review-full, .review-faces > p { margin: 0; font-size: 11px; }
.review-form button.review-face { display: inline-grid; place-items: center; min-height: 0; padding: 2px; border: 0;
  border-radius: 5px; background: none; }
.review-form button.review-face:is(:hover, :focus-visible, [aria-pressed='true']) { background: var(--soft); }
.review-form button.review-face[aria-pressed='true'] { background: var(--selected-bg); }
.review-picked button.review-face { padding: 0; }   /* the + and five avatars on one line */
.review-chip-group { font-size: 12px; }
.review-form > .kit-button { justify-self: start; }
.review-status, .review-latest, .review-prototype { margin: 0; }
.review-requests { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 4px; }
.review-requests li { display: flex; align-items: center; gap: 8px; }
.review-requests li > .pill { margin-left: auto; }
.review-from { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.review-note { margin: 0 0 8px; white-space: pre-wrap; overflow-wrap: anywhere; }
.review-newer { margin: 0 0 8px; }
/* Share (files.js fileShare, wbench.file_rights): who has it (an avatar or a project), each with Remove */
.share-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.share-list li { display: flex; align-items: center; gap: 8px; min-width: 0; }
.share-list li > .kit-button { margin-left: auto; }
/* the Files view is a split pane at the window's height (UI_RULES Layouts): the page itself doesn't scroll, only the
   list and the preview do, each on its own (#239). Main is the window's height and passes it down to the panes. The
   section's own 40px bottom padding goes (set by an id rule above, so the ids here); the page's stays: Spot's room.
   Not under 1100px, where the panes stack and the page scrolls (below): the names (300px), the inspector (270px)
   and the sidebar left the file too little room for its line (#910; UI_RULES T3 stacks a side panel there too). */
@media (min-width: 1100px) {
  html:has(.content > section:not([hidden]) > .project-files) { overflow: hidden; }
  main:has(.content > section:not([hidden]) > .project-files) { display: flex; flex-direction: column; height: 100dvh; }
  main:has(.content > section:not([hidden]) > .project-files) > .content { flex: 1; min-height: 0; width: 100%;
    display: flex; flex-direction: column; }
  .content > section:not([hidden]):has(> .project-files) { flex: 1; min-height: 0; display: flex; flex-direction: column; }
  :is(#projects-portal, #discipline-files):has(> .project-files) { padding-bottom: 0; }
  .content > section > .project-files { flex: 1; min-height: 0; display: flex; flex-direction: column; }
  .content > section > .project-files > .files-panes { flex: 1; height: auto; min-height: 0; }
}
.files-menu, .header-menu-list { position: fixed; z-index: 1000; display: flex; flex-direction: column; min-width: 140px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 4px;
  box-shadow: var(--hard-shadow); }
.files-menu > * { background: none; border: 0; border-radius: 5px; padding: 6px 10px; text-align: left;
  color: var(--text); text-decoration: none; font: inherit; cursor: pointer; }
.files-menu > *:hover, .files-menu > *:focus-visible { background: var(--focus-soft); outline: none; }
.files-menu .danger-text { color: var(--danger); }
@media (max-width: 1099px) {
  .files-panes, .files-panes.no-info { grid-template-columns: 1fr; height: auto; }
  .files-list { max-height: 60vh; }
  .files-preview { border-left: 0; border-top: 1px solid var(--border); height: 80vh; }
  /* expanded: the whole screen under the top bar, its foot kept clear for the chat button (--spot-room) */
  .files-panes.wide > .files-preview { height: auto; padding-bottom: var(--spot-room); }
  .files-inspector { border-left: 0; border-top: 1px solid var(--border); }
  .files-preview .files-fit button { padding: 4px 6px; }
}
.files-bar { padding-bottom: 6px; }
.files-table { font-size: inherit; }
.files-table th { font-size: inherit; font-weight: 400; }
.files-foot { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; padding: 4px 10px;
  font-family: var(--font-mono); font-size: 12px; }
/* its last change never widens the page (a long name, French on a phone, #931): one line (P1), shortened to the
   window, the whole sentence on hover */
.files-foot > * { min-width: 0; max-width: 100%; }
.files-foot > .linkish { overflow: hidden; text-overflow: ellipsis; }
.files-status { margin: 0 0 0 auto; color: var(--muted); }
/* the last change is a button (one line, P1): a long one (French, a long name) is shortened, not wider than a phone (#794) */
.files-foot > .linkish { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* an upload's spinner and one status line in the Files tab's bottom line (files.js uploadStatus, #163) */
.upload-status { color: var(--muted); overflow-wrap: anywhere; }
.upload-status.busy::before {
  content: ''; display: inline-block; vertical-align: -2px; width: 10px; height: 10px; margin-right: 6px;
  border: 1.5px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%;
  animation: activity-spin 1s linear infinite;
}
.upload-status.failed { color: var(--danger); }
.upload-status-problems { margin: 2px 0 0; padding-left: 18px; }
.upload-status-problems:empty { display: none; }
.linkish { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; }
.linkish:hover { text-decoration: underline; background: none; }
.row-actions { display: inline-flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; width: 100%; }
.button.small, button.small { font-size: 12px; padding: 2px 8px; min-height: var(--control-h-sm); vertical-align: middle; }
/* a link that looks like a small button centres its label as the button beside it does (PCB's Open / History, #708) */
a.button.small { display: inline-flex; align-items: center; line-height: 1.4; }
.danger-text { color: var(--danger); }
.file-history { margin-top: 14px; }
#projects-portal input:not([type='file']):not([type='checkbox']), #projects-portal textarea,
#tab-portal input:not([type='file']):not([type='checkbox']), #tab-portal textarea {
  background: var(--surface); border: 1px solid var(--border); border-radius: 7px; color: var(--text);
  padding: 8px 10px; }
/* the kit's search field keeps room for its magnifier there too (ui.js searchField) */
:is(#projects-portal, #tab-portal) .search-field > input:not([type='file']):not([type='checkbox']) { padding-left: 28px; }
.settings-switches .dev-mode-toggle[aria-pressed='true'] { color: var(--warning); }
.settings-switches .deploy-freeze-toggle[aria-pressed='true'] { color: var(--danger); }
.nav-group[hidden], [data-sidebar-tab][hidden] { display: none !important; }
/* A Dev mode tool (an upload for review, ...): shown only while Dev mode is on. Not a launch status: pages and tabs
   have theirs in Admin -> Dev (docs/DEVELOPMENT.md, "Launch status") */
body:not(.dev-mode) [data-dev] { display: none !important; }
/* Dev mode: this page's or tab's launch status in the corner, a link to Admin -> Dev (app.js showLaunchMarker) */
.content { position: relative; }
.launch-marker { position: absolute; top: 6px; right: 26px; z-index: 5; white-space: nowrap; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600; text-decoration: none; border: 1px solid var(--border); background: var(--surface);
  color: var(--secondary); }
.launch-marker[hidden] { display: none; }
.launch-marker[data-status='paused'] { background: var(--warning-bg); border-color: var(--warning-border);
  color: var(--warning); }
.launch-marker[data-status='not_launched'] { background: var(--soft); border-color: var(--accent); color: var(--accent); }
/* Admin -> Dev: a grid of boxes, one per sidebar page with its tabs, several per row, under its sidebar group's
   heading; four status tiles each (dev.js renderLaunch); square, 1px rules, no soft shadow */
.launch-heading { flex-wrap: wrap; gap: 8px; }
.launch-heading .launch-company { width: auto; min-width: 180px; }
.launch-grid { columns: 300px; column-gap: 8px; margin-top: 6px; }
.launch-grid h3.launch-group { column-span: all; margin: 10px 0 4px; font-size: 11px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); }
.launch-grid h3.launch-group:first-child { margin-top: 0; }
.launch-box { break-inside: avoid; display: grid; gap: 3px; margin: 0 0 8px; padding: 5px 7px;
  border: 1px solid var(--border); border-radius: 0; background: var(--surface); }
.launch-tabs { display: grid; gap: 3px; padding: 3px 0 0 8px; border-top: 1px solid var(--border); }
.launch-tabs .launch-name { font-weight: 400; }
.launch-item { position: relative; display: grid; gap: 2px; }
.launch-line { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 4px; }
.launch-name { flex: 1 1 90px; min-width: 0; text-align: left; padding: 0; border: 0; background: none; font: inherit;
  font-size: 12px; font-weight: 600; color: var(--text); cursor: pointer; text-decoration: underline dotted var(--border-strong); }
/* this company's own status, over all companies': marked in ink, not the accent */
.launch-own { padding: 0 4px; border: 1px solid var(--border-strong); font-size: 10px; line-height: 14px; color: var(--text); }
.launch-reset { padding: 0 4px; border: 1px solid var(--border); border-radius: 5px; background: var(--surface);
  font: inherit; font-size: 11px; line-height: 14px; color: var(--secondary); cursor: pointer; }
.launch-reset:disabled { opacity: .4; cursor: not-allowed; }
.launch-item[data-own] .launch-name { color: var(--text); text-decoration-style: solid; }
.launch-tiles { display: flex; gap: 2px; margin-left: auto; }
.launch-tile { padding: 1px 5px; border: 1px solid var(--border); border-radius: 0; background: var(--surface);
  font: inherit; font-size: 10.5px; color: var(--secondary); cursor: pointer; }
.launch-tile:disabled { opacity: .4; cursor: not-allowed; }
.launch-tile[aria-pressed='true'] { opacity: 1; cursor: default; font-weight: 600; background: var(--soft);
  border-color: var(--accent); color: var(--accent); }
.launch-tile[aria-pressed='true'][data-status='paused'] { background: var(--warning-bg);
  border-color: var(--warning-border); color: var(--warning); }
.launch-tile[aria-pressed='true'][data-status='live'] { background: var(--surface); border-color: var(--text);
  color: var(--text); }
.launch-tile[aria-pressed='true'][data-status='disabled'] { background: var(--danger-bg);
  border-color: var(--danger-border); color: var(--danger); }
.launch-history { position: absolute; top: 100%; left: 0; z-index: 6; min-width: 260px; max-height: 240px;
  overflow: auto; margin-top: 4px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 0;
  background: var(--surface); box-shadow: none; font-size: 12px; }
.launch-history p { margin: 2px 0; }
/* the (i) of Dev mode sits inside the switch's outline, at its right end (#140); a sibling of the button, so pointing
   at it or clicking it doesn't switch Dev mode */
.dev-mode-switch { position: relative; display: inline-flex; align-items: center; }
.dev-mode-switch button.dev-mode-toggle { padding-right: 30px; }
#dev-mode-info { position: absolute; right: 9px; top: calc(50% - 7.5px); margin: 0; }
.dev-mode-switch:has(> [aria-pressed='true']) #dev-mode-info { color: var(--warning); border-color: var(--warning-border); }
#dev-mode-info[hidden] { display: none; }
/* One rule for every sidebar link, pages, projects and All Projects alike (2026-10-31): the same weight, colour, size
   and height; only the current one looks different (below, "the current page"). A link's count fits inside the line,
   so Library is as tall as its neighbours. A compact sidebar (#144): the links close together; weak, so the phone
   layout below still applies. */
.sidebar nav a { display: flex; align-items: center; height: 30px; padding: 0 10px; border-radius: 5px; font-size: 13px;
  line-height: 20px; font-weight: 500; color: var(--secondary); }
.sidebar nav a > .nav-text { font-size: inherit; }
.sidebar nav .nav-icon { color: var(--muted); }
.sidebar nav .nav-icon svg { width: 16px; height: 16px; }
/* nav A, the tidy list (owner, 2026-10-31): a count and the Proto tag are small muted text at the right of the row,
   no box and no fill */
.sidebar nav a > b, .sidebar nav a > .nav-badge, .sidebar nav a > .proto-pill.nav-launch {
  margin-left: auto; min-width: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
  color: var(--muted); font-size: 12px; font-weight: 500; line-height: 20px; }
.sidebar nav a > .proto-pill.nav-launch { font-size: 10px; }
.sidebar nav a > :is(b, .nav-badge) ~ .proto-pill.nav-launch { margin-left: 6px; }
/* on the current row's darker fill the muted ink is too faint */
.sidebar nav a.active > :is(b, .nav-badge, .proto-pill.nav-launch) { color: var(--text); }
nav a.nav-create { display: flex; justify-content: center; gap: 6px; margin: 4px 0 2px; padding: 5px 10px;
  border: 1px dashed var(--border-strong); border-radius: 7px; color: var(--accent); font-weight: 600; }
nav a.nav-create::before { content: '+'; font-size: 15px; line-height: 1; }
nav a.nav-create:hover { border-style: solid; border-color: var(--accent); background: var(--soft); }
/* a page with nothing on it yet (Prototype, #144): Spot cooking above the line (#111) */
#empty-portal .empty { padding: 8px 0 48px; text-align: center; font-size: 15px; }
/* On/off switches (Dev mode, Playground, Freeze deploys, a source's connection): a pill-shaped track with a round
   knob (#143; the square corners leave it alone, below) */
button.switch { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
button.switch::before { content: ''; flex: none; width: 24px; height: 14px; border-radius: 7px;
  background: var(--border-strong) radial-gradient(circle at 7px 7px, var(--surface) 5px, transparent 5.5px); }
button.switch:is([aria-pressed='true'], [aria-checked='true'])::before {
  background: var(--accent) radial-gradient(circle at 17px 7px, var(--surface) 5px, transparent 5.5px); }   /* on = accent, off = neutral (#326) */
/* Top bar menu (#132, app.js "Top bar menus"): Settings opens over the page (the panel of .files-menu above) on
   hover, focus or a tap on its button, so nothing on the bar moves. Phones: the drawer lists its contents inline. */
.header-menu { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.header-menu-list { display: none; }
.header-menu.open > .header-menu-list { display: flex; position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  min-width: max-content; gap: 6px; padding: 6px; }
.header-menu-list::before { content: ''; position: absolute; inset: -9px 0 auto; height: 9px; } /* the gap below the button */
/* Settings is the width of the light/dark button beside it (30px, .icon-button; #178, #249) */
.header-actions button.header-menu-button { color: var(--muted); }
.header-actions button.header-menu-button[aria-expanded='true'] { background: var(--soft); color: var(--text); }
@media (max-width: 720px) {
  .sidebar > .header-actions .header-menu, .sidebar > .header-actions .header-menu-list { display: contents; }
  .sidebar > .header-actions button.header-menu-button { display: none; }
  .sidebar > .header-actions .header-menu-list::before { content: none; }
}
/* Playground (Dev mode): projects open the company's playground vault; the banner says so on every page */
.settings-switches .playground-toggle[aria-pressed='true'] { color: var(--warning); }
.playground-banner { position: sticky; top: 56px; z-index: 29; display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 12px; padding: 8px 26px; background: var(--warning-bg); border-bottom: 1px solid var(--warning-border);
  color: var(--warning); font-size: 13px; }
.playground-banner[hidden] { display: none; }
.playground-banner button { margin-left: auto; padding: 4px 10px; border: 1px solid var(--warning-border);
  border-radius: 7px; background: var(--surface); color: var(--warning); font-weight: 600; cursor: pointer; }
@media (max-width: 720px) { .playground-banner { padding: 8px 16px; } }
/* Company switcher (super admins, app.js showCompanySwitch): a site dropdown (Dropdowns, above) as tall as the bar's
   buttons beside it (Report an issue, the 30px light/dark and Settings), its text centred in it (#173.1, #211.1).
   The line box is centred, but DM Sans's ascent is far taller than its descent, so the capitals sit about 1px above
   the middle; the buttons hide this with their 2px keycap edge, the dropdown has none. 2px on top brings its text
   onto the bar's text line (the name beside it) and the box's middle (#249). */
.header-select { height: var(--control-h-sm); max-width: 180px; padding: 2px 10px 0; display: inline-flex; align-items: center;
  font-size: 12px; }
.header-select[hidden] { display: none; }
@media (max-width: 720px) {
  .sidebar > .header-actions .header-select { height: 40px; max-width: none; flex: 1 1 100%; font-size: 14px; }
}
/* ==== Dev Comments (comments.js) ==== */
/* Dev mode, Freeze deploys and Playground: switches in Settings, under the toggles (owner, 2026-11-01) */
.settings-switches { display: grid; justify-items: start; gap: 2px; padding-top: 6px; border-top: 1px solid var(--border); }
.settings-switches:not(:has(> :not([hidden]))) { display: none; }
.settings-switches button.switch { width: 100%; height: var(--control-h-sm); padding: 0 8px; border: 0; background: none;
  color: var(--text); font-weight: 400; }
.settings-switches button.switch:hover { background: var(--soft); }
.settings-switches .dev-mode-switch { width: 100%; }
/* Dev Comments floats at the top right under the menu bar, above every page and popup (owner, 2026-11-01): one line,
   the switch with ⇧C, then the comments written and Submit, then Undo and Clear for the drawing. app.js placePopups
   sets its top (the bar's foot) and opens popups below it, so a sheet's × is never under it (H18). */
.dev-comments { position: fixed; top: var(--dc-top, 50px); right: var(--page-gutter, 14px); z-index: 903;
  display: flex; align-items: center; gap: 6px; white-space: nowrap; max-width: calc(100vw - 2 * var(--page-gutter, 14px));
  padding: 3px 4px 3px 8px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: 6px;
  box-shadow: var(--hard-shadow); font-size: 12.5px; }
.dev-comments[hidden] { display: none; }
.dev-comments button.switch { border: 0; background: none; padding: 0 4px 0 0; height: var(--control-h-sm); color: var(--text);
  font-weight: 500; }
.dev-comments kbd { font-family: inherit; font-size: 11px; color: var(--muted); }
.dev-comments .dc-count { min-width: 18px; padding: 0 5px; border-radius: 9px; background: var(--soft); text-align: center; }
/* the drawing: on the window, under the bubbles; it never takes a pointer (comments.js reads the right button) */
.dc-ink { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 901; }
.dc-ink[hidden] { display: none; }
/* a cross everywhere, over the page's own cursors too (the terminal's, the browser pane's; #1027); Dev Comments' own
   bubbles, tray and switch keep theirs */
body.dc-commenting,
body.dc-commenting :not(.dc-layer, .dc-layer *, .dc-tray, .dc-tray *, .dev-comments, .dev-comments *, #bug-dialog,
  #bug-dialog *) { cursor: crosshair !important; }
body.dc-commenting :is(.dc-layer, .dc-tray, .dev-comments, #bug-dialog) { cursor: auto; }
body.dc-commenting .dc-bubble, body.dc-commenting :is(.dc-tray, .dev-comments) button { cursor: pointer !important; }
body .dc-bubble.dc-movable { cursor: grab !important; touch-action: none; }
body .dc-pin.dc-dragging, body .dc-pin.dc-dragging * { cursor: grabbing !important; user-select: none; }
/* above the tray: a bubble near the bottom is never under it */
.dc-layer { position: fixed; inset: 0; pointer-events: none; z-index: 902; }
.dc-pin { position: absolute; pointer-events: auto; display: flex; align-items: flex-start; gap: 6px; }
.dc-pin[hidden] { display: none; }
.dc-pin.dc-left { flex-direction: row-reverse; }
.dc-pin.dc-up { align-items: flex-end; }
.dc-bubble { width: 26px; height: 26px; border-radius: 13px 13px 13px 3px; padding: 0; font-size: 12px; font-weight: 650;
  background: var(--accent); color: var(--on-accent); border: 2px solid var(--surface);
  box-shadow: 0 2px 6px var(--shadow); }
.dc-bubble.dc-new { background: var(--warning); }
.dc-peek { display: none; max-width: 260px; background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 9px; font-size: 12.5px; box-shadow: 0 2px 8px var(--shadow-soft); white-space: pre-wrap; }
.dc-pin:hover .dc-peek { display: block; }
.dc-box { width: 260px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: 8px;
  padding: 8px; box-shadow: 0 4px 16px var(--shadow); display: flex; flex-direction: column; gap: 6px; }
.dc-box textarea { width: 100%; box-sizing: border-box; resize: vertical; font: inherit; font-size: 13px;
  background: var(--canvas); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; }
.dc-actions { display: flex; justify-content: space-between; gap: 6px; }
.dc-tray { position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px; z-index: 901; display: flex;
  gap: 10px; align-items: center; flex-wrap: wrap; justify-content: center; max-width: calc(100vw - 32px);
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: 10px; padding: 8px 12px;
  box-shadow: 0 4px 18px var(--shadow); font-size: 13px; }
.dc-tray[hidden] { display: none; }
.dc-hint { color: var(--secondary); }
.dc-count { font-weight: 600; }
.dc-message { color: var(--accent); }
.dc-fast { cursor: pointer; white-space: nowrap; }
/* over the page, just below the top bar: showing it moves nothing (#132) */
.dc-fast-warning { position: fixed; top: 64px; left: 50%; transform: translateX(-50%); z-index: 901;
  max-width: calc(100vw - 32px); padding: 6px 12px; border: 1px solid var(--warning-border); border-radius: 8px;
  background: var(--warning-bg); color: var(--warning); font-size: 13px; box-shadow: 0 2px 8px var(--shadow-soft); }

/* ==== Part details (#10): summary first (picture, documents, key specs), specs in tabs, every field folded ==== */
#detail-dialog.part-dialog { width: min(1080px, calc(100vw - 48px)); }
.part-summary {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 22px;
  margin: 0 0 22px;
}
.part-summary[hidden] { display: none; }
.summary-media .record-picture { border: 0; padding: 0; margin: 0; }
.summary-media .record-picture > h3 { display: none; }
.summary-media .picture-body { flex-direction: column; }
.summary-media img.illustration, .summary-media img.photo { width: 240px; height: 240px; }
.summary-media .picture-facts { flex-basis: auto; font-size: 12px; }
#summary-description { margin: 0 0 12px; font-size: 15px; line-height: 1.45; }
.summary-facts { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-bottom: 12px; font-size: 13px; }
.summary-facts > div { display: flex; flex-direction: column; gap: 2px; }
.summary-facts .muted { font-size: 11px; }
.summary-docs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.doc-badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--muted); font-size: 13px; font-weight: 600; text-decoration: none;
}
.doc-badge.have { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
a.doc-badge.have:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
#key-specs h3, .spec-tabs h3 { margin: 0 0 8px; font-size: 14px; }
/* the border is on the rows: the global "tbody tr:last-child td { border-bottom: 0 }" took it off the last row's
   value cells only (#30, #48, #50) */
.spec-grid { width: 100%; border-collapse: collapse; font-size: 13px; white-space: normal; }
.spec-grid tr { border-bottom: 1px solid var(--border); cursor: auto; }
.spec-grid th, .spec-grid td { text-align: left; padding: 5px 10px; border-bottom: 0; vertical-align: top; max-width: none; }
.spec-grid th {
  width: 30%; font-weight: 500; color: var(--secondary); background: transparent; text-transform: none;
  letter-spacing: 0; font-size: 13px; position: static;
}
.spec-grid thead th { font-size: 11px; color: var(--muted); }
.spec-grid td { background: transparent; }
.spec-grid tbody tr:nth-child(even) > * { background: color-mix(in srgb, var(--text) 3%, transparent); }
.spec-grid td.muted { width: 30%; font-size: 12px; }
.spec-grid .source-link { text-decoration: none; margin-left: 4px; }
.spec-grid .source-link > svg.icon { vertical-align: -2px; }
.spec-tablist { display: flex; flex-wrap: wrap; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.spec-tablist button {
  border: 0; border-bottom: 2px solid transparent; border-radius: 0; background: transparent; padding: 7px 12px;
  font-size: 13px; color: var(--secondary);
}
.spec-tablist button[aria-selected='true'] { border-bottom-color: var(--accent); color: var(--text); font-weight: 600; }
.all-fields { margin-top: 6px; }
.all-fields > summary { cursor: pointer; font-weight: 600; padding: 8px 0; }
@media (max-width: 720px) {
  .part-summary { grid-template-columns: minmax(0, 1fr); }
  .summary-media img.illustration, .summary-media img.photo { width: 180px; height: 180px; }
}
/* ==== Requirements (requirements.js) ==== */
#requirements-portal,
.reqs-page {
  display: grid;
  gap: 12px;
}
#requirements-portal[hidden] {
  display: none;
}
.reqs-head,
.reqs-head-left,
.reqs-head-right,
.reqs-filters,
.reqs-summary,
.reqs-bulk,
.reqs-pager,
.reqs-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
/* who has a requirement checked out (a lock, their avatar, on the detail their name), and its last check-in */
.reqs-lock,
.reqs-checkin {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.reqs-head {
  justify-content: space-between;
}
.reqs-project-pick {
  font-weight: 600;
  min-width: 180px;
}
.button-link {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 8px 10px;
  background: var(--surface);
  font-weight: 550;
}
.button-link:hover {
  background: var(--soft);
}
#standards-portal {
  display: grid;
  gap: 10px;
}
#standards-portal[hidden] {
  display: none;
}
.std-table tr.clickable {
  cursor: pointer;
}
.std-table tr.clickable:hover td {
  background: var(--soft);
}
.std-documents.dragging {
  outline: 2px dashed var(--accent);
}
#standards-portal input:not([type='checkbox']):not([type='file']),
#requirements-portal input:not([type='checkbox']):not([type='file']),
#requirements-portal textarea,
.reqs-dialog input:not([type='checkbox']):not([type='file']) {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
}
.reqs-filters input[type='search'] {
  flex: 1 1 260px;
  min-width: 200px;
}
.reqs-filters select {
  max-width: 220px;
  font-size: 12px;
  padding: 6px 8px;
}
.reqs-filters select.active {
  border-color: var(--border-strong);
  background-color: var(--selected-bg);
  color: var(--text);
}
.reqs-check {
  font-size: 12px;
  color: var(--secondary);
  white-space: nowrap;
}
.reqs-summary {
  justify-content: space-between;
  font-size: 12px;
}
.reqs-main {
  display: grid;
  gap: 10px;
  min-width: 0;
}
.reqs-table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  max-height: calc(100dvh - 300px);
}
.reqs-table td {
  vertical-align: top;
}
.reqs-table tr.selected td {
  background: var(--focus-soft);
}
.reqs-sort {
  all: unset;
  cursor: pointer;
}
.reqs-sort > svg.icon { margin-left: 4px; vertical-align: -2px; }
.reqs-sort:focus-visible {
  outline: 2px solid var(--focus);
}
.reqs-select {
  width: 28px;
  padding-right: 0;
}
.reqs-id {
  white-space: nowrap;
  font-weight: 600;
}
.reqs-text-cell {
  white-space: normal;
  min-width: 300px;
  max-width: 560px;
}
.reqs-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 2px;
}
.reqs-text {
  color: var(--text);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.45;
}
.reqs-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}
.reqs-tags .pill {
  padding: 1px 6px;
}
.reqs-wrap {
  white-space: normal;
  min-width: 110px;
  max-width: 200px;
}
.pill.reqs-accepted,
.pill.reqs-satisfied {
  color: var(--accent);
}
.pill.reqs-pending,
.pill.reqs-proposed {
  color: var(--warning);
}
.pill.reqs-rejected,
.pill.reqs-not_satisfied {
  color: var(--danger);
}
.pill.reqs-deleted {
  text-decoration: line-through;
}
.reqs-pager {
  justify-content: flex-end;
  font-size: 12px;
}
.reqs-detail {
  position: sticky;
  top: 12px;
  display: grid;
  gap: 12px;
  max-height: calc(100dvh - 120px);
  overflow: auto;
  font-size: 13px;
}
.reqs-detail-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}
.reqs-detail-head > div {
  display: flex;
  align-items: center;
  gap: 8px;
}
.reqs-detail-id {
  font-size: 15px;
  font-weight: 700;
}
.reqs-close {
  padding: 2px 9px;
  font-size: 16px;
  line-height: 1;
}
.reqs-h {
  margin: 0;
  font-size: 12px;
  color: var(--secondary);
}
.reqs-detail p {
  margin: 0;
}
.reqs-proposal {
  display: grid;
  gap: 8px;
  border: 1px solid var(--warning-border);
  background: color-mix(in srgb, var(--warning-bg) 45%, var(--surface));
  border-radius: 8px;
  padding: 12px;
}
.reqs-proposal .reqs-actions input {
  flex: 1 1 140px;
}
.reqs-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  font-size: 13px;
}
.reqs-fields dt {
  font-size: 10px;
  color: var(--muted);
  margin-top: 6px;
}
.reqs-fields dd {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.45;
}
.reqs-statement {
  font-size: 14px;
  color: var(--text);
}
.reqs-pre {
  color: var(--secondary);
}
.reqs-diff {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.5;
}
.reqs-diff ins {
  color: #1f5fbf;
  background: #1f5fbf1a;
  text-decoration: underline;
}
.reqs-diff del {
  margin-right: 0.25em;
  color: #1f5fbf;
  text-decoration: line-through;
  opacity: 0.75;
}
:root[data-theme-mode='dark'] .reqs-diff ins,
:root[data-theme-mode='dark'] .reqs-diff del {
  color: #8fb8ff;
}
.reqs-diff-label {
  font-size: 10px;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 6px;
}
.reqs-sig {
  font-size: 12px;
}
.reqs-history summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--secondary);
}
.reqs-history-list {
  display: grid;
  gap: 8px;
  padding-left: 20px;
  font-size: 12px;
}
.reqs-form {
  display: grid;
  gap: 10px;
}
.reqs-form label,
.reqs-prefix {
  display: grid;
  gap: 4px;
  font-size: 12px;
}
.reqs-form label > span,
.reqs-prefix > span {
  font-weight: 600;
  color: var(--secondary);
}
.reqs-form textarea {
  resize: vertical;
}
.reqs-dialog form {
  display: grid;
  gap: 12px;
}
.reqs-dialog h3 {
  margin: 0;
}
.reqs-prefix {
  grid-template-columns: auto 1fr auto;
  align-items: center;
}
.reqs-empty p {
  margin: 0;
}
.reqs-standards {
  display: grid;
  gap: 6px;
  margin: 0;
  font-size: 12px;
}
.reqs-standards label {
  display: flex;
  gap: 8px;
  align-items: baseline;
}
@media (max-width: 1100px) {
  .reqs-detail {
    position: static;
    max-height: none;
  }
}

/* ==== Popups below the top bar (#15, #148); Report an issue as a panel (#18) ==== */
/* Every dialog but Report an issue's panel (a record's details, a file, Add our own standard, ...) opens non-modally
   (app.js openPopup) below the top bar, over a scrim that covers the page and the sidebar. The top bar (Report an
   issue), Dev Comments (.dev-comments, 903) and Spot (.helper-root, 60) stay above
   both and usable; Report an issue's panel (80) opens above them all. One rule for every popup: :where() keeps it
   weaker than a dialog's own size (dialog above sets the height that fits below the bar). Phones: the open drawer,
   which holds the top bar's controls there, still covers the top bar and the popup (nav-open, below).
   A popup's × is never under Dev Comments (#148.1): app.js placePopups opens popups below it while it shows
   (--popup-top); --popup-right stays 0. */
:where(dialog:not(.bug-panel)[open]) {
  position: fixed;
  inset: var(--popup-top, 72px) var(--popup-right, 0px) auto 0;
  margin: 0 auto;
  max-width: calc(100vw - var(--popup-right, 0px) - 24px);
  z-index: 43;
  overflow: auto;
}
body:has(dialog:not(.bug-panel)[open])::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 42;
  background: var(--scrim);
  backdrop-filter: blur(3px);
}
body:has(dialog:not(.bug-panel)[open]) main > header { z-index: 45; }
body.reporting { overflow: hidden; }
/* reporting is a snapshot of the page: everything but the report panel (z-index 80) is dimmed very slightly */
body.reporting::after { content: ''; position: fixed; inset: 0; z-index: 79; background: #0000000d; pointer-events: none; }
/* the part's 3D model and Reported Issues panels open at the same place (#79), above Spot: they are in their
   popup, which Spot floats over (#259); Report an issue's panel floats over her */
#bug-dialog.bug-panel[open], #detail-dialog .side-panel:not([hidden]) {
  position: fixed;
  inset: auto 20px 20px auto;
  margin: 0;
  z-index: 80;
  width: min(440px, calc(100vw - 40px));
  max-height: calc(100dvh - 100px);
  overflow: auto;
  box-shadow: 0 18px 60px var(--shadow);
}
#detail-dialog .side-panel:not([hidden]) { bottom: var(--spot-room);
  max-height: calc(100dvh - var(--popup-top, 72px) - var(--spot-room)); }
#detail-dialog .side-panel {
  padding: 18px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
}
.side-panel .issue-heading button { border: 0; font-size: 20px; padding: 0 6px; }
.dialog-heading .heading-actions { display: flex; align-items: center; gap: 8px; }
.dialog-heading #flag-issue { font-size: 12px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 999px; }
#flag-issue .issue-count { margin-left: 6px; padding: 0 6px; border-radius: 999px; background: var(--accent); color: var(--on-accent); }
.doc-badge.icon-only { padding: 6px 9px; cursor: pointer; background: none; }
.doc-badge.icon-only svg { display: block; }
.model-upload { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; font-size: 13px; }
.model-upload > strong { flex-basis: 100%; }
.model-upload input[type='url'] { flex: 1 1 220px; min-width: 0; }
/* 3D review: what a click does (View only / Spot / Feature), then its hint */
.review-cad-modes { justify-content: flex-start; flex-wrap: wrap; }
.review-cad-modes .view-toggle { margin: 0; order: initial; flex: none; }
#language-toggle[hidden] { display: none; }
/* Phones: the sidebar is a drawer behind the header's menu button, and the header's controls sit at its foot
   (app.js moves .header-actions there), so the header holds only the menu button and the page name. */
.nav-menu { display: none; }
@media (max-width: 720px) {
  main > header { justify-content: flex-start; padding-inline: 6px 14px; }
  .nav-menu {
    display: grid; place-items: center; flex: none; width: 44px; height: 44px; padding: 0;
    border: 0; border-radius: 8px; background: none; color: var(--text);
  }
  .nav-menu:hover { background: var(--soft); }
  .breadcrumb { flex: 1; min-width: 0; }
  .breadcrumb strong { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .sidebar {
    inset: 0 auto 0 0; width: min(300px, 86vw);
    padding: calc(12px + env(safe-area-inset-top)) 12px calc(16px + env(safe-area-inset-bottom));
    overflow-y: auto; overscroll-behavior: contain; cursor: auto;
    transform: translateX(-100%); visibility: hidden; transition: transform .2s ease, visibility .2s;
  }
  body.nav-open .sidebar { z-index: 47; transform: none; visibility: visible; box-shadow: 0 8px 24px var(--shadow); }
  /* the backdrop; iOS sends taps on it to app.js only when it looks clickable */
  body.nav-open { overflow: hidden; cursor: pointer; }
  body.nav-open::before { content: ''; position: fixed; inset: 0; z-index: 46; background: #00000059; }
  .sidebar nav, .nav-scroll { flex: none; overflow: visible; }
  .sidebar nav { margin-top: 8px; gap: 14px; }
  .sidebar .nav-heading { min-height: 40px; padding-block: 0; }
  .sidebar nav a { height: auto; min-height: 44px; padding: 8px 10px; font-size: 15px; }
  .sidebar nav a > .nav-text { display: inline; font-size: 15px; }
  .sidebar nav .nav-icon { display: grid; }
  .sidebar nav b { display: inline; }
  .sidebar nav a.nav-project { height: auto; min-height: 0; padding-block: 0; line-height: 40px; font-size: 14px; }
  .sidebar nav .admin-nav { margin-top: 0; position: static; }
  .sidebar > .header-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
  }
  .sidebar > .header-actions button,
  .sidebar > .header-actions button.icon-button,
  .sidebar > .header-actions button.report-issue,
  .sidebar > .header-actions .language-toggle { height: 40px; min-width: 40px; font-size: 13px; }
  .sidebar > .header-actions .language-toggle button { height: 32px; }
  /* the theme switch is two rows of two here: its own height, so Units sits below it, not over its second row (#953) */
  .sidebar > .header-actions .language-toggle.theme-switch { height: auto; }
  .sidebar > .header-actions button.report-issue span:last-child { display: inline; }
}

/* Parts tabs (#99): narrow Mfr / category quick searches, then the added filters take the rest of the row. */
.sheet-toolbar #filters.parts-filters { display: flex; flex-wrap: wrap; align-items: center; }
.sheet-toolbar #filters.parts-filters > select,
.sheet-toolbar #filters.parts-filters > .filter-suggest { flex: 0 1 150px; width: 150px; }
.where-filters { display: flex; flex: 1 1 300px; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.where-chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 3px 4px 3px 10px; font-size: 12px; border-radius: 7px;
  background: var(--focus-soft); border: 1px solid var(--accent); color: var(--text); max-width: 100%;
}
.where-chip button { display: inline-flex; border: 0; background: transparent; padding: 0 4px; font-size: 15px; line-height: 1; }
.where-form { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.sheet-toolbar #filters .where-form select { width: auto; max-width: 240px; }
.sheet-toolbar #filters .where-form input { width: 120px; min-width: 0; }
.where-form > span:not(.filter-suggest) { font-size: 12px; color: var(--muted); }
.sheet-toolbar #filters .where-form .filter-suggest { flex: 0 0 auto; }
.where-form .suggest-list { right: auto; min-width: 100%; width: max-content; max-width: 320px; }
/* the code scanner (scan.js: Check In Parts, Check Out Parts) */
.scan-dialog { width: min(480px, calc(100vw - 32px)); display: flex; flex-direction: column; gap: 10px; }
.scan-dialog:not([open]) { display: none; }
.scan-dialog h3 { margin: 0; }
.scan-video { width: 100%; max-height: 50vh; border-radius: 10px; background: #000; object-fit: cover; }
.scan-video[hidden] { display: none; }
.scan-status { margin: 0; color: var(--muted); font-size: 14px; }
.scan-row { display: flex; gap: 8px; }
.scan-row input { flex: 1; min-height: 48px; font-size: 16px; padding: 0 10px; border: 1px solid var(--border-strong);
  border-radius: 10px; background: var(--surface); color: var(--text); min-width: 0; }
.scan-dialog button { min-height: 48px; padding: 0 16px; border-radius: 10px; }

/* Markdown rendered by files.js markdown() (work instructions, .md previews) */
.markdown h2, .markdown h3, .markdown h4 { margin: 14px 0 6px; }
.markdown p, .markdown ul, .markdown ol { margin: 6px 0; }
.markdown .markdown-img { display: block; max-width: 100%; height: auto; margin: 6px 0; border: 1px solid var(--border); }
.markdown pre { overflow-x: auto; padding: 8px; border-radius: 6px; background: var(--surface-alt); }
/* a .md file in a viewer fills it, empty or long, and scrolls inside itself (#1069) */
:is(.file-preview, .markdown-editor, .translated-body) > .markdown { flex: 1 1 auto; min-height: 0; overflow: auto; }
.markdown-table-wrap { overflow-x: auto; }
.markdown-table { border-collapse: collapse; margin: 8px 0; font-size: 13.5px; }
.markdown-table th, .markdown-table td { padding: 4px 8px; border: 1px solid var(--border); text-align: left; vertical-align: top; }
/* a Markdown file's Edit / Save / Cancel line above it, and its source while editing (#194) */
/* as wide as its pane, never its sheet's width: a wide grid scrolls inside, the bar stays in view (#591) */
.markdown-editor { display: flex; flex-direction: column; gap: 6px; min-height: 0; min-width: 0; max-width: 100%;
  align-self: stretch; flex: 1; }
.markdown-bar { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; flex: none; }   /* wraps between controls in a narrow viewer, never inside a button */
.csv-editor textarea { flex: 1; min-height: 50vh; width: 100%; box-sizing: border-box; padding: 8px; resize: vertical;
  font: 12px/1.5 var(--font-mono); border: 1px solid var(--border); border-radius: 0; }
/* while editing: Vditor in place (files.js markdownEditor, vendor/vditor/), in the site's own colours in every theme */
.markdown-ir { flex: 1; min-height: 0; overflow: auto; }
.markdown-ir.vditor {
  --border-color: var(--border); --second-color: var(--border-strong); --panel-background-color: var(--surface);
  --panel-shadow: var(--hard-shadow); --toolbar-background-color: var(--surface-alt);
  --toolbar-icon-color: var(--secondary); --toolbar-icon-hover-color: var(--accent);
  --textarea-background-color: var(--surface); --textarea-text-color: var(--text);
  --ir-heading-color: var(--accent); --ir-title-color: var(--muted); --ir-bi-color: var(--accent);
  --ir-link-color: var(--link); --ir-bracket-color: var(--link); --ir-paren-color: var(--muted);
  --heading-border-color: var(--border); --blockquote-color: var(--muted); --count-background-color: var(--soft);
  border-radius: 0; font-family: var(--font-sans);
}
.markdown-ir .vditor-ir pre.vditor-reset { color: var(--text); font: 14px/1.6 var(--font-sans); }
.markdown-ir .vditor-reset table tr { background: transparent; border-color: var(--border); }
.markdown-ir .vditor-reset table th { background: var(--surface-alt); }
.markdown-ir .vditor-reset code:not(.hljs):not(.highlight-chroma) { background: var(--surface-alt); color: var(--text); }
.markdown-ir .vditor-reset pre > code { font-family: var(--font-mono); }
.markdown-ir .vditor-reset blockquote { border-left: 3px solid var(--border-strong); color: var(--muted); }
.markdown-ir .vditor-reset :is(th, td) { border-color: var(--border); }
.markdown-ir .vditor-reset a { color: var(--link); }
.markdown-ir .vditor-ir pre.vditor-reset:focus { background: var(--surface); }
/* a .md / .txt file read in another language (files.js translatedDocument): the toggle and "Original: 中文" on top */
.translated-doc { display: flex; flex-direction: column; gap: 6px; min-height: 0; flex: 1; }
.translated-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: none; }
.read-toggle { height: 28px; }
.read-toggle button { min-width: 0; padding: 2px 8px; font-size: 12.5px; }
.original-badge { font-size: 12px; color: var(--muted); }
.original-badge[hidden] { display: none; }
.translated-note[hidden] { display: none; }
.translated-body { display: flex; flex-direction: column; min-height: 0; flex: 1; }
/* a comment list's toggle (files.js commentLanguage), in the list's head; "Original: 中文" after a translated one */
.comment-language { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
.review-comments-head .comment-language, .issue-section-head .comment-language, .comment-language-head .comment-language { flex-wrap: wrap; }
.comment-language-head { display: flex; align-items: center; gap: 10px; }

/* draw.js: drawing on a file's pages (the project Files viewer, File Annotate) and on a sketch's paper. One line of
   tools that wraps on a phone: the pen button (drawing mode), the four colours, Eraser, Undo, Redo, then Show
   drawings and Download with drawings on the right. Off, the ink layer lets every pointer through (the page scrolls,
   File Annotate's boxes and points work); on, it takes every touch (touch-action: none) until a pen has touched,
   then a finger scrolls (palm rejection, draw.js). The four inks are the same in both themes: pages stay paper. */
.draw { display: flex; flex-direction: column; gap: 6px; min-height: 0; flex: 1; }
.draw-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; flex: none; }
.draw-tools { display: flex; align-items: center; gap: 4px; }
.draw-end { margin-left: auto; }
.draw-pen svg { width: 16px; height: 16px; vertical-align: -3px; }
.draw-bar button[aria-pressed="true"], .project-files .draw-bar button[aria-pressed="true"] { background: var(--selected-bg); font-weight: 600; }
.draw-swatches { display: flex; gap: 2px; }
.draw-swatch { width: 26px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.draw-swatch::after { content: ''; width: 14px; height: 14px; border-radius: 50%; background: currentColor; }
.draw-swatch[aria-pressed="true"]::after { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px currentColor; }
.ink-red { color: var(--ink-red); fill: var(--ink-red); }
.ink-blue { color: var(--ink-blue); fill: var(--ink-blue); }
.ink-green { color: var(--ink-green); fill: var(--ink-green); }
.ink-black { color: var(--ink-black); fill: var(--ink-black); }
.draw-problem:empty { display: none; }
/* the Text tool (#1123): a typed note in the page's font, and the box it is typed in, over the page where clicked */
.draw-note { font-family: var(--font-sans); white-space: pre; }
body.draw-mode .draw-ink.draw-typing { cursor: text; }
.draw-note-field { position: absolute; z-index: 2; margin: -1px 0 0 -1px; padding: 0; min-width: 3em; min-height: 0;
  font-family: var(--font-sans); line-height: 1.25; background: transparent; border: 1px dashed currentColor; border-radius: 2px;
  outline: none; resize: none; overflow: hidden; white-space: pre; box-shadow: none; }
.draw-pages { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 8px; }
.draw-page { position: relative; flex: none; width: 100%; background: var(--paper); border: 1px solid var(--border); }
.draw-page img { display: block; width: 100%; height: 100%; max-height: none; }
.draw-ink { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
body.draw-mode .draw-ink { pointer-events: auto; touch-action: none; cursor: crosshair; user-select: none;
  -webkit-user-select: none; -webkit-touch-callout: none; }
body.draw-mode.draw-pen-seen .draw-ink { touch-action: pan-x pan-y pinch-zoom; }
body.draw-mode .draw-ink.draw-readonly { pointer-events: none; touch-action: auto; cursor: auto; }
body.drawings-hidden:not(.draw-mode) .draw-ink, body.drawings-hidden:not(.draw-mode) .review-ink { visibility: hidden; }
.draw-thumb { display: block; width: 100%; max-width: 160px; height: 56px; margin: 4px 0; background: var(--paper);
  border: 1px solid var(--border); }
/* File Annotate: a drawn comment's number at its box, no outline (the strokes show it); its colour in the list */
.review-box.review-ink { border-color: transparent; background: none; }
/* the pen's bar on its own full-width line under the instructions, wrapping, so neither squeezes the other nor
   clips Download with drawings (#337, #383, #800) */
.review-stage-bar .draw-bar { margin-top: 4px; flex: 1 1 100%; }
.review-num[data-ink="red"] { background: var(--ink-red); border-color: var(--ink-red); }
.review-num[data-ink="blue"] { background: var(--ink-blue); border-color: var(--ink-blue); }
.review-num[data-ink="green"] { background: var(--ink-green); border-color: var(--ink-green); }
.review-num[data-ink="black"] { background: var(--ink-black); border-color: var(--ink-black); }

/* files.js sheetView: CSV, TSV and Excel as a spreadsheet's grid (#191.1, #194); the letters and numbers stay in view */
.sheet-view { display: flex; flex-direction: column; gap: 4px; min-height: 0; flex: 1; }
.sheet-scroll { flex: 1; min-height: 0; max-height: 70vh; overflow: auto; border: 1px solid var(--border); }
.files-preview .sheet-scroll, .file-viewer .sheet-scroll { max-height: none; }
.sheet-grid { border-collapse: separate; border-spacing: 0; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.sheet-grid td, .sheet-grid th { padding: 2px 6px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border);
  white-space: nowrap; max-width: 24em; overflow: hidden; text-overflow: ellipsis; text-align: left; }
.sheet-grid td.num { text-align: right; }
.sheet-grid tr:first-child th { position: sticky; top: 0; z-index: 1; }
.sheet-grid th:first-child { position: sticky; left: 0; text-align: right; }
.sheet-grid th { background: var(--surface-alt); color: var(--muted); font-weight: 400; }
.sheet-grid tr:first-child th:first-child { z-index: 2; }
.sheet-grid tr.sheet-first td { font-weight: 600; }
.csv-editor .sheet-grid textarea { font: inherit; min-height: 0; resize: vertical; white-space: pre; overflow: hidden; }
.sheet-tabs { display: flex; gap: 4px; flex-wrap: wrap; flex: none; }
.sheet-tabs button[aria-pressed='true'] { background: var(--focus-soft); font-weight: 600; }
.sheet-note:empty { display: none; }
.sheet-note { margin: 0; font-size: 12px; flex: none; }

/* Admin → Permissions (permissions.js) */
.perm-table th { font-size: 12.5px; vertical-align: bottom; }
.perm-cell { text-align: center; }
.perm-cell input { width: 18px; height: 18px; }
.perm-steps { margin-top: 16px; }
.perm-step { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.perm-step input:not([type='radio']) { flex: 1 1 200px; }
.perm-proc { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-size: 13px; }
/* Admin → Passwords (passwords.js): a login's sheet */
.pw-field { display: grid; gap: 4px; margin-bottom: 10px; font-size: 13px; }
.pw-field span { color: var(--muted); }
.pw-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 12px; }
.pw-grant { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.pw-revealed { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.pw-revealed code { user-select: all; word-break: break-all; }
.pw-versions { padding-left: 20px; font-size: 13px; }
/* ==== Mist and Dim slate (tokens at the top of this file) ====
   The sidebar's current page as one inverted row (the sidebar is its own branch), and the project
   Files browser as the Mac System 7 Finder's list (files.js fileFinder): the open folder's path in bold on the left
   (no striped title bar), an info line, Name / Size / Kind / Modified, folders that open in place, the selected row
   in the soft --selected-bg (#144; the standard, 2026-10-28). */
.sidebar { background: var(--surface-alt); }
/* the current page: the standard's soft fill (a shade darker than the sidebar's own surface), in bold, no accent */
nav a.active, nav a.nav-project.active {
  background: var(--nav-selected-bg); color: var(--text); }
.sidebar nav a.active .nav-icon { color: var(--text); }
/* one current row: an open project, not also All Projects above it */
.nav-group:has(a.nav-project.active) > a.active { background: none; color: var(--secondary); }
.nav-group:has(a.nav-project.active) > a.active .nav-icon { color: var(--muted); }
:root[data-theme-mode='dark'] .helper-button .spot-art {
  filter: drop-shadow(0 0 1px #c9ced6aa) drop-shadow(0 3px 3px var(--shadow)); }
/* a project's Recent Activity: one line per change, the rest on hover (#208) */
.activity-line { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* a person inline (ui.js who, #238): their avatar the height of the text, a rounded square (18%, P8: the .avatar
   rule below) like every avatar; initials on --soft when there is no picture */
.who { display: inline-flex; flex: none; align-items: center; justify-content: center; width: 1.4em; height: 1.4em;
  vertical-align: middle; overflow: hidden; background: var(--soft); color: var(--secondary); }
.who > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.who-initials { font-size: 0.6em; font-weight: 600; line-height: 1; }
.finder { display: flex; flex-direction: column; min-height: 100%; font-variant-numeric: tabular-nums; }
.finder-path { display: block; padding: 6px 10px 4px; text-align: left; font-weight: 700; overflow-wrap: anywhere; }
/* the path with New at its right (files.js newMenu, #1061): at the top of the list, never at its foot */
.finder-head { display: flex; align-items: center; gap: 8px; padding-right: 8px; }
.finder-head > .finder-path { flex: 1 1 auto; min-width: 0; }
.finder-head > .new-menu { flex: none; }
.new-menu { display: inline-flex; align-items: center; gap: 4px; }
.new-menu > .icon { flex: none; }
/* a new folder's name typed in place (files.js namingRow) */
.finder-rename { flex: 1 1 auto; min-width: 0; padding: 1px 4px; font: inherit; }
.finder-info { display: flex; gap: 6px 18px; flex-wrap: wrap; padding: 3px 10px; color: var(--muted);
  border-bottom: 1px solid var(--border); }
.finder-info > :last-child:not(:first-child):not(:nth-child(2)) { margin-left: auto; }
.finder-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.finder-table th { text-align: left; font-weight: 400; color: var(--muted); padding: 3px 6px;
  border-bottom: 1px solid var(--border); }
.finder-table th:first-child { padding-left: 10px; }
.finder-table th:nth-child(2) { width: 72px; }
.finder-table th:nth-child(3) { width: 104px; }
.finder-table th:nth-child(4) { width: 76px; }
.finder-table td { padding: 2px 6px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.finder-row { cursor: default; }
.finder-row:hover td { background: var(--focus-soft); }
.finder-row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.finder-table td.finder-name-cell { padding-left: calc(10px + var(--depth, 0) * 18px); color: var(--text); }
.finder-name-box { display: flex; align-items: center; gap: 5px; min-width: 0; }
.finder-table tr.finder-more td { padding-left: calc(33px + var(--depth, 0) * 18px); }
.finder-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* the disclosure triangle: right when closed, down when open */
.finder-toggle { flex: none; display: inline-grid; place-items: center; width: 12px; height: 14px; padding: 0;
  border: 0; background: none; cursor: pointer; }
button.finder-toggle::before { content: ''; border-left: 6px solid currentColor; border-top: 4px solid transparent;
  border-bottom: 4px solid transparent; }
button.finder-toggle.open::before { border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 6px solid currentColor; border-bottom: 0; }
button.finder-toggle:hover { background: none; }
/* a file is an outlined page with a turned corner, a folder a filled folder with its tab: told apart at a glance */
.finder-icon { position: relative; flex: none; width: 10px; height: 13px; border: 1px solid var(--muted);
  background: var(--surface); }
.finder-icon::after { content: ''; position: absolute; top: -1px; right: -1px; width: 3px; height: 3px;
  border-left: 1px solid var(--muted); border-bottom: 1px solid var(--muted); background: var(--surface); }
.finder-icon.folder { width: 15px; height: 10px; margin-top: 3px; border-color: var(--muted); background: var(--muted); }
.finder-icon.folder::after { top: -4px; right: auto; left: -1px; width: 6px; height: 3px; border: 0; background: var(--muted); }
/* a sketch: its page 1 as a tiny sheet of paper, turned when the sketch is landscape (projects.js sketchThumb) */
.sketch-thumb { flex: none; width: 12px; height: 17px; margin: -2px 0; border: 1px solid var(--muted);
  background: var(--paper); overflow: hidden; }
.sketch-thumb.landscape { width: 17px; height: 12px; margin: 0; }
.sketch-thumb .draw-thumb { width: 100%; max-width: none; height: 100%; margin: 0; border: 0; }
/* the selection: the soft fill (#144: the accent was too bright), its text in full ink */
.finder-row.selected td, .finder-row.selected:hover td { background: var(--selected-bg); color: var(--text); }
.finder-row.selected .finder-icon, .finder-row.selected .finder-icon::after { border-color: var(--text);
  background: var(--selected-bg); }
.finder-row.selected .finder-icon.folder, .finder-row.selected .finder-icon.folder::after { background: var(--text); }
.finder-row.selected:focus-visible { outline-color: var(--text); outline-offset: 1px; }
.finder-row.dragging td { background: var(--focus-soft); box-shadow: inset 0 0 0 1px var(--accent); }
@media (max-width: 520px) {
  .finder-table th:nth-child(3), .finder-table td:nth-child(3) { display: none; }
}
/* What the screen review found (tests/test_browser_look.py screens): the new --soft sits close to the surfaces (it
   has to, for muted text on it to keep 4.5:1), so a state that was a --soft fill with accent text faded. Fills that
   only mark a thing (badges, tracks, drop zones) get an edge instead. */
/* the selection (the standard, 2026-10-28: a soft fill, tokens at the top): selected, on or current states in
   --selected-bg and the text's ink, with no line of their own and no accent */
.import-type.selected { background: var(--selected-bg); }
.language-toggle button[aria-pressed='true'] { background: var(--selected-bg); color: var(--text); }
.language-toggle button:hover:not([aria-pressed='true']) { background: var(--soft); color: var(--text); }
.research-action.selected { background: var(--selected-bg); }
/* the report type: a row of option buttons, radio buttons underneath */
.bug-types { gap: 6px; padding: 0; border: 0; background: none; }
.bug-types label { padding: 5px 12px; border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--secondary); }
.bug-types label:hover { color: var(--text); }
.bug-types label:has(input:checked) { background: var(--selected-bg); color: var(--text); font-weight: 600; }
.bug-types label:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.sheet-toolbar #filters .filter-check input[type='checkbox'] { accent-color: var(--secondary); }
.view-toggle button { border: 1px solid transparent; padding: 4px 7px; }
.reqs-table tr.selected td, .maintenance-table tr.selected td, .maintenance-list li.selected { background: var(--selected-bg); }
.maintenance-table tr { cursor: pointer; }
.maintenance-table .pill { white-space: nowrap; }
.tile.selected { border-color: var(--border-strong); background: var(--selected-bg); }
/* Admin -> Dev: a page's chosen launch status is a setting (Paused, Live and Disabled keep their own colours) */
.launch-tile[aria-pressed='true']:not([data-status='paused'], [data-status='live'], [data-status='disabled']) {
  background: var(--selected-bg); border-color: var(--border-strong); color: var(--text); }
/* a setting that is on and a page's launch status are not key actions: no accent (the standard) */
.connection-switch[aria-checked='true'] {
  background: var(--selected-bg); border-color: var(--border-strong); color: var(--text); }   /* the track carries the accent (#326) */
.launch-marker[data-status='not_launched'] { background: var(--surface); border-color: var(--border-strong);
  color: var(--text); }
.pill.muted-pill, .pill.muted, .conn-pill,
.upload-icon { box-shadow: inset 0 0 0 1px var(--border-strong); }
:is(.bar-track, .job-progress, .research-track) { box-shadow: inset 0 0 0 1px var(--border); }
:is(.import-drop, .setup-drop) { border-color: var(--border-strong); }
body.reporting #report-issue {
  background: var(--soft); border-color: var(--border-strong); }
/* plain links in the page read as links next to the text: underlined */
main a:not([class]):not(:is(nav, [role='tablist']) a) { text-decoration: underline;
  text-decoration-color: var(--border-strong); text-underline-offset: 2px; }
main a:not([class]):not(:is(nav, [role='tablist']) a):hover { text-decoration-color: currentColor; }
/* a text box no rule styles takes the surface, never the browser's white */
:where(input:not([type='checkbox'], [type='radio'], [type='range'], [type='file'],
  [type='color']), textarea) { background-color: var(--surface); color: var(--text); border: 1px solid var(--border-strong); }
/* ==== Components (Haofeng, 2026-10-28: "very uncluttered"), with the standard of the same
   day (docs/UI_RULES.md; its own rules under "The standard" at the end of this file) ====
   One radius scale: 0 for data (status cards, tables, tiles, panels), 5px for controls, 6px for what floats (sheets,
   toasts, menus, popovers); Spot keeps her own, and round marks (dots, spinners, radio buttons, a switch) stay round.
   1px rules instead of fills, System 7's hard offset shadow only on what floats over the page (sheets, menus,
   popovers, Spot's panel and bubble), the accent fill only for the key action, a selection in the soft
   --selected-bg, square status pills outlined in their colour, notices with a 4px status edge, no motion that
   doesn't explain something, no uppercase labels, no decoration (gradients, icons that repeat their label). */
*, *::before, *::after { border-radius: 0 !important; }
/* round on purpose: status dots, the info mark, the spinners (#156), a review pin, a radio button, Create new's plus */
:is(.status-dot, .research-list .dot, .research-chip i, .research-legend i, .info-tip, .conn-dot,
  input[type='radio'], .create-tile-icon, .dictate-mark, .dictate-mic),
:is(.job-live .job-status, .review-pin, .upload-status.busy, .loading-spinner)::before,
#sheet:has(#grid[aria-busy='true']) #result-count::after { border-radius: 50% !important; }
button.switch::before { border-radius: 7px !important; }   /* a switch's track is a pill (#143) */
.avatar, .who { border-radius: 18% !important; }
.sidebar nav .nav-icon > .avatar { --avatar: 20px; }   /* people: a rounded square (P8) */
/* controls, 5px: what you click or type into, option buttons and chips, the segmented Compact / Images control */
:is(button, select, textarea, .button,
  input:not([type='checkbox'], [type='radio'], [type='range'])):not(th *),
:is(.bug-types label, .research-chip, .helper-chip, .sheet-toolbar #filters .filter-check, .view-toggle,
  .language-toggle) { border-radius: 5px !important; }
/* floating, 6px: sheets, toasts, menus, popovers */
::picker(select),
:is(dialog, .toast, #message .notice, .files-menu, .header-menu-list, .suggest-list, .select-list, .demo-tour,
  .spend-limits > .automation-form, .dc-peek, .dc-box, .dc-tray, #bug-dialog.bug-panel[open],
  #detail-dialog .side-panel:not([hidden]), [data-tip]:is(:hover, :focus)::after, .bug-point-bar) { border-radius: 6px !important; }
/* Spot's panel and walkthrough step keep soft corners, and so does what is on them (#153) */
:is(.helper-panel, .helper-step) { border-radius: 10px !important; }
:is(.helper-panel, .helper-step) :is(button, input, textarea, .helper-card) { border-radius: 6px !important; }
:is(.helper-panel, .helper-step) :is(.helper-chip, .helper-draft) { border-radius: 999px !important; }
.helper-panel .helper-you { border-radius: 10px 10px 2px 10px !important; }
.helper-badge { border-radius: 999px !important; }
.helper-highlight { border-radius: 8px !important; }
*, *::before, *::after { transition: none !important; }
.job-live .job-progress-fill { background-image: none; animation: none; }
.helper-button:hover { transform: none; }
/* what floats: one frame and a hard shadow */
:is(dialog, .files-menu, .header-menu-list, .suggest-list, .select-list, .demo-tour, .spend-limits > .automation-form,
  .dc-peek, .dc-box, .dc-tray, .helper-step, #bug-dialog.bug-panel[open], #detail-dialog .side-panel:not([hidden]),
  [data-tip]:focus::after, .bug-point-bar) { box-shadow: var(--hard-shadow); border: 1px solid var(--border-strong); }
::picker(select) { box-shadow: var(--hard-shadow); }
body.nav-open .sidebar { box-shadow: var(--hard-shadow); }
:is(.review-num, .signin-card) { box-shadow: none; }
.helper-panel { box-shadow: var(--hard-shadow); filter: none; border: 1px solid var(--border-strong);
  background: var(--surface); }
.helper-panel :is(.helper-head, .helper-dev) { clip-path: none; }
.helper-head { background: var(--surface-alt); color: var(--text); border-bottom: 1px solid var(--border-strong); }
.helper-dev { background: none; }
.helper-bubble { box-shadow: var(--hard-shadow); border: 1px solid var(--border-strong);
  background: var(--surface); }
.helper-bubble::after { background: var(--border-strong); }
/* in her panel the note is a section of the one frame, not a second box */
.helper-panel .helper-bubble { border: 0; box-shadow: none; background: transparent;
  border-top: 1px solid var(--border); }
.helper-panel .helper-bubble::after { content: none; }
/* buttons: outlined; the default (primary) one filled, with System 7's inner ring; ghost ones text only */
:is(button, select) { border-color: var(--border-strong); }
:is(.primary, button.primary, .button.primary) { background: var(--accent); color: var(--on-accent);
  border: 1px solid var(--accent); box-shadow: inset 0 0 0 1px var(--surface); }
:is(.primary, button.primary, .button.primary):hover { background: var(--accent-hover);
  border-color: var(--accent-hover); }
:is(.linkish, .link-button, .helper-icon, .finder-toggle) { border: 0; box-shadow: none; }
/* status pills: square, a 1px edge in their colour, no fill */
.pill:is(.muted-pill, .muted), .conn-pill { box-shadow: none;
  border-color: var(--border-strong); background: transparent; }
.proto-pill { background: var(--surface); }
.launch-marker { background: var(--surface); }
.notice:is(.danger, .error) { border-left-color: var(--danger); }
/* focus: 2px of the accent, 2px off */
:is(button, a, input, select, textarea, summary, [tabindex]):focus-visible { outline: 2px solid var(--accent);
  outline-offset: 2px; }
:is(.finder-row, .file-row):focus-visible { outline-offset: -2px; }
/* uncluttered: no sign-in glow, no icons that repeat the sidebar's labels, one frame per box */
#signin-screen { background: none; }
/* a link's name stays on one line (#175), cut short as the last resort */
.sidebar nav a { gap: 9px; }
.sidebar nav a > .nav-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the top bar: the title takes the room it needs and is the last thing cut */
.breadcrumb { flex: 1 1 auto; min-width: 120px; }
.header-actions { flex: 0 1 auto; min-width: 0; }
.report-issue span[aria-hidden] { display: none; }   /* the flag repeats the label */
/* a narrower top bar and less room round the page, so the work gets the space (#192.2, #192.3); phones keep theirs.
   What sits under the bar follows it: the playground banner and the floating controls here, popups by app.js placePopups (the bar's foot).
   The bar and the page share one side gutter, so the floating controls' right edge (the page's) lines up with Settings' (the bar's) */
@media (min-width: 721px) {
  :root { --page-gutter: 18px; }
  header { height: 44px; padding-inline: var(--page-gutter); }
  .playground-banner { top: 44px; }
  .content { padding: 14px var(--page-gutter) var(--spot-room); }
}

/* ==== The standard (Haofeng, 2026-10-28; docs/UI_RULES.md), the shared pieces ====
   Every page gets these from the shared markup (buttons, WB.tabStrip, openPopup, ui.js createTile / stepTrail / toast,
   el()'s loading placeholder), never page by page. The radius scale and the selection are in Components above. */
:root { --keycap-edge: color-mix(in srgb, var(--border-strong) 55%, var(--text)); }
/* Buttons: Keycap. A 1px frame with a heavier 2px bottom edge that presses in on a click (1px, and down 1px); quiet
   buttons (no frame: links, icons, ×, segmented options, tabs) have no edge. The key action of a page (.primary) is
   the only one in the accent, its edge a darker accent. As weak as `button`, so a quiet button's own rule wins. */
button,
:is(a.button, button.small, .report-issue) {
  border-bottom-width: 2px; border-bottom-color: var(--keycap-edge); }
button:active:not(:disabled),
:is(a.button, button.small, .report-issue, .primary):active:not(:disabled) {
  border-bottom-width: 1px; transform: translateY(1px); }
:is(.primary, button.primary, .button.primary) { box-shadow: none;
  border-bottom: 2px solid var(--accent-edge); }
:is(.primary, button.primary, .button.primary):hover {
  border-bottom-color: var(--accent-hover-edge); }
/* the accent marks only the key action: not a setting, a toggle, a hover or an icon */
.import-badge { color: var(--text); }
.wo-flow-step.wo-now.wo-rejected { color: var(--danger); }

/* Page tabs: stacked folders (T3). Every tab is a folder with 6px top corners, overlapping the next; the current one
   is brought to the front and joins the page below (its background covers the strip's line, the others stop 1px
   above it). Every WB.tabStrip but a project's second row, which is a row of sub-tabs (below). The first folder's
   outer edge is the page's left edge, in line with the title above it, and the 8px above the folders hold their
   New/Updated tags (#205). */
.tab-strip:not(.project-subtabs) { gap: 0; padding: 8px 0 0; }
.tab-strip:not(.project-subtabs) > a { margin: 0 -6px 1px 0; padding: 5px 18px 5px 14px; }
.tab-strip:not(.project-subtabs) > a[aria-current='page'] { z-index: 2; margin-bottom: 0; padding-bottom: 6px; }
/* the folders' look, also the cloud browser's tabs (#1021.2; tabs/bench.css sizes them for its compact strip) */
:is(.tab-strip:not(.project-subtabs) > a, .cb-tabs > .cb-tab) {
  border: 1px solid var(--border); border-bottom: 0; border-radius: 6px 6px 0 0 !important; background: var(--surface-alt);
  color: var(--secondary); }
:is(.tab-strip:not(.project-subtabs) > a, .cb-tabs > .cb-tab):hover { color: var(--text); }
:is(.tab-strip:not(.project-subtabs) > a[aria-current='page'], .cb-tabs > .cb-tab.current) { z-index: 2;
  background: var(--canvas); border-color: var(--border-strong); color: var(--text); font-weight: 600; }
.tab-strip > a > .proto-pill { z-index: 3; }
@media (max-width: 720px) {
  .tab-strip:not(.project-subtabs) > a { padding: 5px 12px 5px 9px; }
}
/* Sub-tabs: words with thin lines between them, the current one bold (S2): a project's second row, a work order's
   Summary / Editor, Admin -> Users' and a part's spec groups, Sources' groups, KPIs' views */
:is(.tab-strip.project-subtabs, .spec-tablist, .connection-tabs, .kpi-nav) {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; padding: 0; border: 0; background: none; }
:is(.tab-strip.project-subtabs > a, .spec-tablist > button, .connection-tabs > button, .kpi-nav > a) {
  margin: 0; padding: 1px 10px; border: 0; border-left: 1px solid var(--border-strong); border-radius: 0 !important;
  background: none; box-shadow: none; color: var(--secondary); font-weight: 400; line-height: 1.4; }
:is(.tab-strip.project-subtabs > a, .spec-tablist > button, .connection-tabs > button, .kpi-nav > a):first-child { padding-left: 0; border-left: 0; }
:is(.tab-strip.project-subtabs > a, .spec-tablist > button, .connection-tabs > button, .kpi-nav > a):hover { color: var(--text); background: none; }
:is(.tab-strip.project-subtabs > a[aria-current='page'], .spec-tablist > button[aria-selected='true'],
  .connection-tabs > button[aria-selected='true'], .kpi-nav > a.active) {
  color: var(--text); font-weight: 600; background: none; }
.tab-strip.project-subtabs { margin-bottom: 12px; }
/* Sequential steps (Quotation's, a work order's status flow, PCB Design's pipeline, Import's, Check In's): the sub-tabs' words
   with chevrons for dividers, to show the flow; the current step bold, those done in the secondary ink */
:is(.step-trail, .wo-flow-steps, .pcb-steps, .import-steps, .ci-steps) { display: flex; flex-wrap: wrap;
  align-items: center; gap: 4px 0; margin: 0 0 14px; padding: 0; list-style: none; border: 0; background: none;
  color: var(--muted); font-size: 13px; }
:is(.step-trail > li, .wo-flow-step, .pcb-steps > li, .import-steps > span, .ci-steps > li) { position: static;
  display: inline-flex; align-items: center; padding: 1px 0; border: 0; background: none; box-shadow: none;
  color: inherit; font-weight: 400; min-width: 0; }
:is(.step-trail > li, .wo-flow-step, .pcb-steps > li, .import-steps > span, .ci-steps > li) + :is(li, span)::before {
  content: ''; position: static; flex: none; width: 6px; height: 6px; margin: 0 12px 0 9px; border: solid var(--border-strong);
  border-width: 1px 1px 0 0; transform: rotate(45deg); }
:is(.step-trail > li.done, .wo-flow-step.wo-past, .pcb-steps > li:has(.done), .ci-steps > li.ci-past) { color: var(--secondary); }
:is(.step-trail > li[aria-current], .wo-flow-step.wo-now, .pcb-steps > li:has([aria-current]),
  .import-steps > .current, .ci-steps > li.ci-now) { color: var(--text); font-weight: 600; border: 0; background: none; }
.ci-steps > li { flex: none; }
.ci-steps > li:first-child::before { content: none; }
.pcb-step { gap: 6px; padding: 0; border: 0; background: none; box-shadow: none; color: inherit;
  font-weight: inherit; }
.pcb-step[href]:hover { color: var(--text); text-decoration: underline; }
.pcb-step :is(.pcb-num, .pcb-state) { min-width: 0; height: auto; padding: 0; background: none;
  color: inherit; line-height: inherit; font-size: inherit; }
.pcb-step .pcb-state { font-weight: 400; color: var(--muted); font-size: 11.5px; }   /* an icon, its word in its title (#198) */

/* Popups: side sheets only. Every popup (app.js openPopup) is a sheet from the right, from under the top bar to the
   bottom of the window, clear of the floating controls' column (--popup-right); its header (title and ×, app.js
   sheetHead) stays while the rest scrolls. A phone gets it full width. The top bar, Report an issue, Spot and Dev
   mode stay above it (the z-order of "Popups" above). A popup's own width (the file viewer's) still applies. */
:where(dialog:not(.bug-panel)[open]) { inset: var(--popup-top, 72px) var(--popup-right, 0px) 0 auto;
  margin: 0; width: min(580px, calc(100vw - var(--popup-right, 0px))); max-width: calc(100vw - var(--popup-right, 0px));
  height: auto; max-height: none; padding: 0 20px var(--spot-room); border-right: 0; border-bottom: 0;
  border-radius: 6px 0 0 0 !important; overflow: auto; overscroll-behavior: contain; }
/* a part's details: a wider sheet, still from the right (the browser's own dialog height is fit-content) */
#detail-dialog.part-dialog[open] { width: min(860px, calc(100vw - var(--popup-right, 0px))); }
dialog[open] > .dialog-heading { position: sticky; top: 0; z-index: 3; display: flex;
  align-items: center; gap: 10px; margin: 0 -20px 14px; padding: 10px 12px 10px 20px; background: var(--surface);
  border-bottom: 1px solid var(--border); }
dialog[open] > .dialog-heading :is(h2, h3) { flex: 1; min-width: 0; margin: 0; font-size: 16px; }
dialog[open] > .dialog-heading > :last-child { margin-left: auto; }
.sheet-close { border: 0; background: none; font-size: 22px; line-height: 1; padding: 2px 8px; color: var(--secondary); }
.sheet-close:hover { background: var(--soft); color: var(--text); }
@media (max-width: 720px) {
  :where(dialog:not(.bug-panel)[open]),
  #detail-dialog.part-dialog[open] { inset: var(--popup-top, 72px) 0 0 0; width: 100vw;
    max-width: 100vw; padding: 0 14px var(--spot-room); border-left: 0; border-radius: 0 !important; }
  dialog[open] > .dialog-heading { margin-inline: -14px; padding-left: 14px; }
}

/* Notifications: toasts only, at the bottom left, each with its ×; the page's notice (#message, app.js) is one of
   them. No list, no bell. */
.toast { border-left: 1px solid var(--border-strong); box-shadow: var(--hard-shadow); }
.toast-stack > #message { pointer-events: auto; }
.toast-stack > #message:empty { display: none; }
.toast-stack > #message:not(:empty) { margin: 0; }
.toast-stack > #message .notice { max-height: 30vh; padding: 10px 8px 10px 12px; font-size: 13.5px;
  border: 1px solid var(--border-strong); border-left: 4px solid var(--warning-border); box-shadow: var(--hard-shadow); }
.toast-stack > #message .notice:is(.danger, .error) { border-left-color: var(--danger); }
.toast-stack > #message .notice.success { border-left-color: var(--border-strong); }

/* Create new / Upload (ui.js createTile): a dashed tile with a circled plus (or arrow), the last tile of its grid, the
   size of the tiles beside it; in lists and tables the same as a one-line bar (o.bar). No accent. */
.create-tile { gap: 8px; border: 1px dashed var(--border-strong); background: transparent;
  color: var(--secondary); }
:is(.create-tile:hover, .create-tile:focus-visible, .create-tile.dragging, .create-tile[aria-pressed='true']) {
  border-color: var(--secondary); color: var(--text); background: var(--surface); }
.create-tile-icon { display: grid; place-items: center; width: 26px; height: 26px;
  border: 1px solid currentColor; }
.create-tile-icon svg { width: 15px; height: 15px; }
:is(.create-tile, .create-tile-item) { order: 99; }
.create-tile.create-bar { flex-direction: row; justify-content: flex-start; gap: 10px; width: 100%;
  height: auto; aspect-ratio: auto; margin: 8px 0 0; padding: 6px 12px; text-align: left; font-size: 12.5px; }
.create-bar .create-tile-icon { width: 20px; height: 20px; }
.create-bar .create-tile-icon svg { width: 12px; height: 12px; }
.create-bar .create-tile-hint { color: var(--muted); font-size: 12px; }

/* Loading (loading() in app.js, never the word): skeleton lines where a page or a part of it loads, one line for a
   heading waiting for its title, the circle spinner for an action or a small area (a line of text, a table
   refreshing, an upload). Still: no shimmer. */
.loading-placeholder { position: relative; min-height: 48px; max-width: 560px;
  background: linear-gradient(var(--border) 0 0) 0 4px / 55% 9px no-repeat,
  linear-gradient(var(--border) 0 0) 0 21px / 100% 9px no-repeat, linear-gradient(var(--border) 0 0) 0 38px / 80% 9px no-repeat; }
.loading-placeholder.line { min-height: 0; height: 1em; width: 40%; background: linear-gradient(var(--border) 0 0) 0 50% / 100% 9px no-repeat; }
.loading-spinner { position: relative; display: inline-block; }

/* ==== The kit (docs/UI_RULES.md "The kit"; ui.js "The kit", icons.js): the controls screens are built from, each
   drawn by one function and shown in every state on the UI kit page (#ui-kit, kit.js). Tokens only, the standard's
   radius scale (controls 5px, what floats 6px, data 0, avatars 18%), no blurred shadow, no motion. The patterns of
   2026-10-31 (UI_RULES P1-P8) are built here from the mock-up's rules (design/report-card-2026-10-31.html, R1-R9). */
/* icons: line icons in the text's colour, 16px (20px leading a tile or a big button) */
svg.icon { flex: none; vertical-align: -3px; }
/* R1 buttons never wrap: a label is one line; a row that runs out of room wraps between buttons. Not labels, so they
   wrap: a tile's title (a heading that opens the tile), a page's name in Admin -> Dev, a choice card with its line,
   a notification in the bell's sheet (its row: sentence, body, time; #871, #892) */
button:not(.tile-title, .launch-name, .ci-choice, .notify-open), .button { white-space: nowrap; }
.kit-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.kit-button > svg.icon { width: 16px; height: 16px; }
/* a button whose request is pending (ui.js busyWhile): clicks wait for it */
button[aria-busy='true'] { cursor: progress; opacity: .7; }
/* R2 the key action's shortcut, inside its button after the verb: a step smaller and quieter */
.key-hint { margin-left: 0; font: inherit; font-size: 0.9em; font-weight: 500; opacity: 0.75; }
button.quiet, a.button.quiet { border-color: transparent; border-bottom-width: 1px; background: none; color: var(--secondary); }
button.quiet:hover, a.button.quiet:hover { background: var(--soft); color: var(--text); }
:is(button, a.button).quiet:active:not(:disabled) { border-bottom-width: 1px; transform: none; }
button.danger, a.button.danger { color: var(--danger); }
button.danger:hover { border-color: var(--danger-border); background: var(--danger-bg); }
/* an icon-only button: quiet, square, its name in its tooltip */
button.icon-button { display: inline-grid; place-items: center; width: 32px; height: 32px; padding: 0;
  border-color: transparent; border-bottom-width: 1px; background: none; color: var(--secondary); }
button.icon-button.small { width: 26px; height: 26px; }
button.icon-button:hover { background: var(--soft); color: var(--text); }
button.icon-button[aria-pressed='true'] { background: var(--selected-bg); color: var(--text); }
button.icon-button:active:not(:disabled) { border-bottom-width: 1px; transform: none; }
/* R9 the on/off switch: its track in ink when on, the knob on the right; tokens only; no accent (a setting) */
button.switch:is([aria-pressed='true'], [aria-checked='true'])::before {
  background: var(--secondary) radial-gradient(circle at 17px 7px, var(--surface) 5px, transparent 5.5px); }
/* checkboxes and radio buttons: the secondary ink, never the accent */
.check-field { display: inline-flex; align-items: baseline; gap: 7px; flex-wrap: wrap; cursor: pointer; }
.check-field input { accent-color: var(--secondary); margin: 0; transform: translateY(1px); }
.check-field .field-note { flex-basis: 100%; padding-left: 20px; }
.radio-field { display: grid; gap: 6px; margin: 0; padding: 0; border: 0; }
.radio-field legend { margin-bottom: 4px; padding: 0; font-weight: 600; font-size: 12.5px; }
/* text fields: the label above, a note or an error under */
.text-field { display: grid; gap: 4px; max-width: 420px; }
.field-label { font-size: 12.5px; font-weight: 600; color: var(--text); }
.field-note { font-size: 12px; color: var(--muted); }
.field-error { font-size: 12px; color: var(--danger); }
.text-field.has-error :is(input, textarea) { border-color: var(--danger); }
.text-field :is(input, textarea) { width: 100%; box-sizing: border-box; }
/* R3 the dictation hint: bottom left inside every multi-line field, over a strip the field keeps for it (and for a
   counter at the bottom right), so text never runs under it; it stays while the person types */
.field-with-hint { position: relative; display: block; }
.field-with-hint > textarea { display: block; width: 100%; box-sizing: border-box; padding-bottom: 24px; resize: vertical; }
:is(.field-hint, .field-count) { position: absolute; bottom: 6px; font-size: 11px; line-height: 1.2; color: var(--muted);
  pointer-events: none; }
.field-hint { left: 10px; }
.field-count { right: 10px; font-variant-numeric: tabular-nums; }
/* beside a counter the hint keeps clear of it, shortened with … where the field is narrow (#1035: the AI Agent page's
   right bar) */
.field-with-hint:has(> .field-count) > .field-hint { right: calc(20px + 11ch); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
/* a search box: the magnifier inside it, at the left */
.search-field { position: relative; display: inline-flex; align-items: center; max-width: 320px; }
.search-field > svg.icon { position: absolute; left: 8px; color: var(--muted); pointer-events: none; }
.search-field > input { width: 100%; padding-left: 28px; }
/* a status card: a notice about one item, square, a 4px edge in its tone; its actions at the right */
.status-card { display: flex; align-items: center; gap: 12px; margin: 0; color: var(--text); }
.status-card-body { display: grid; gap: 2px; flex: 1; min-width: 0; }
.status-card.warning { border-left-color: var(--warning-border); }
.status-card.danger { border-left-color: var(--danger); }
.status-card.success { border-left-color: var(--live); }
.status-card.info { border-left-color: var(--border-strong); }
.status-card-actions { display: flex; gap: 6px; flex: none; }
/* R5 avatars: a rounded square, 18% of its side; initials on --soft until the picture loads */
.avatar { position: relative; display: inline-grid; place-items: center; flex: none; width: var(--avatar, 30px);
  height: var(--avatar, 30px); overflow: hidden; background: var(--soft); color: var(--secondary);
  font-size: calc(var(--avatar, 30px) * 0.37); font-weight: 650; line-height: 1; vertical-align: middle; }
.avatar > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.avatar-stack { display: inline-flex; align-items: center; }
.avatar-stack > .avatar + .avatar { margin-left: calc(var(--avatar, 24px) * -0.25); box-shadow: 0 0 0 2px var(--surface); }
/* R8 menus: what floats (6px, frame, hard shadow), compact rows (30px) with their icons, groups split by a 1px line and
   titled in muted text, the soft fill on hover; no accent */
.kit-menu { min-width: 200px; font-size: 13px; }
.kit-menu > button { display: flex; align-items: center; gap: 10px; min-height: 30px; padding: 4px 8px; }
.kit-menu > button:is(:hover, :focus-visible) { background: var(--soft); }
.kit-menu > button > svg.icon { width: 15px; height: 15px; color: var(--secondary); }
.kit-menu > button.danger-text > svg.icon { color: inherit; }
.kit-menu > button:disabled { opacity: 0.45; cursor: default; }
.files-menu > .menu-group { margin: 4px 0 0; padding: 6px 8px 2px; border-top: 1px solid var(--border); color: var(--muted);
  font-size: 11.5px; cursor: default; }
.files-menu > .menu-group:first-child { margin-top: 0; border-top: 0; }
.files-menu > .menu-group:hover { background: none; }
.files-menu > .menu-separator { height: 0; margin: 4px 0; padding: 0; border: 0; border-top: 1px solid var(--border); }
/* R6 the corner card (Report an issue's pattern): flush in the top-right corner under the top bar, its one free corner
   6px, the frame and the hard shadow of what floats. While Dev Comments shows, it opens just below it, so its × is
   never under it (app.js placePopups, --corner-top) */
.corner-card { position: fixed; inset: var(--corner-top, 44px) 0 auto auto; margin: 0; z-index: 80; display: flex; flex-direction: column; gap: 8px;
  width: min(340px, 100vw); box-sizing: border-box; padding: 10px 12px 12px; background: var(--surface);
  border: 1px solid var(--border-strong); border-top: 0; border-right: 0; box-shadow: var(--hard-shadow);
  border-radius: 0 0 0 6px !important; }
.corner-card-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.corner-card-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 14px; font-weight: 600; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.corner-card-head .count { color: var(--secondary); font-weight: 400; }
.corner-card-actions { display: flex; align-items: center; gap: 8px; }
.corner-card-actions .primary { margin-left: auto; }
.corner-card button:not(.sheet-close) { font-size: 12px; padding: 5px 10px; }
.corner-card .sheet-close { padding: 0 4px; font-size: 20px; }
.corner-card.collapsed { flex-direction: row; align-items: center; padding: 8px 10px 8px 12px; }
.corner-card.collapsed .corner-card-head { flex: 1; }
/* an empty state: what will be here, and the action that starts it */
.empty-state { display: grid; justify-items: center; gap: 8px; }
.empty-state > p { margin: 0; }

/* The UI kit page (kit.js): each sample over the call that draws it; floating pieces drawn in place */
.kit-sample { display: grid; gap: 6px; padding: 10px 0; border-top: 1px solid var(--border); }
.kit-sample:first-of-type { border-top: 0; }
.kit-stage { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; }
.kit-stage > .status-card, .kit-stage > .tile-grid, .kit-stage > .text-field { flex: 1 1 100%; }
.kit-stage > .view-toggle { margin-left: 0; order: 0; }
.kit-code { color: var(--muted); font: 11.5px var(--font-mono); overflow-wrap: anywhere; }
.kit-icons { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 2px 4px; }
.kit-icon-group { grid-column: 1 / -1; margin: 10px 0 2px; font-size: 12.5px; color: var(--secondary); }
button.kit-icon { display: grid; grid-template-columns: auto auto 1fr; align-items: center; gap: 8px; padding: 6px 8px;
  border-color: transparent; border-bottom-width: 1px; background: none; color: var(--text); font-weight: 400;
  text-align: left; }
button.kit-icon:hover { background: var(--soft); }
button.kit-icon:active:not(:disabled) { border-bottom-width: 1px; transform: none; }
.kit-icon-name { overflow: hidden; text-overflow: ellipsis; font: 11.5px var(--font-mono); color: var(--secondary); }
.kit .files-menu.kit-static { position: static; z-index: auto; }
.kit-corners { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px; }
.kit-corners > .corner-card { position: relative; top: auto; right: auto; z-index: auto; border-top: 1px solid var(--border-strong);
  border-right: 1px solid var(--border-strong); }

/* ==== Chat (chat.js, docs/CHAT.md): the icon at the bottom left beside the sidebar (it covered Admin, #173.3), its
   panel of up to four conversation tabs. The toasts there sit above the icon. Phones: the open drawer covers it. ==== */
.chat-root { position: fixed; left: calc(var(--sidebar-width) + 16px); bottom: 16px; z-index: 60; display: flex;
  flex-direction: column; align-items: flex-start; gap: 10px; font-size: 13px; }
.chat-i { width: 18px; height: 18px; flex: none; }
.chat-i.sm { width: 13px; height: 13px; }
.chat-launcher { position: relative; width: 44px; height: 44px; padding: 0; border-radius: 50%; display: grid;
  place-items: center; background: var(--surface); color: var(--accent); border: 1px solid var(--border-strong);
  box-shadow: 0 2px 10px var(--shadow-soft); }
.chat-launcher .chat-i { width: 22px; height: 22px; }
.chat-badge { position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px; background: var(--danger); color: var(--surface); font-size: 11px; font-weight: 700;
  line-height: 18px; text-align: center; }
.chat-panel { width: 360px; max-height: min(560px, calc(100dvh - 110px)); display: flex; flex-direction: column;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong); border-radius: 12px;
  box-shadow: 0 8px 28px var(--shadow); overflow: hidden; }
.chat-head { display: flex; align-items: center; gap: 8px; padding: 10px 10px 6px 14px; }
.chat-n { color: var(--muted); font-size: 12px; }
.chat-sp { flex: 1; }
.chat-ib { display: inline-grid; place-items: center; width: 30px; height: 30px; padding: 0; border: 0;
  border-radius: 8px; background: transparent; color: var(--secondary); }
.chat-ib:hover { background: var(--soft); color: var(--text); }
.chat-ib.sm { width: 22px; height: 22px; }
.chat-ib.pin.on { color: var(--text); }
.chat-tabs { flex: none; }
/* the open conversations are the site's folder tabs (WB.tabStrip, "Page tabs" below), the current one joined to the
   conversation; each folder's × sits in its right end, clear of the next folder's 6px overlap, and a small circled + follows the last folder */
.chat-tabs.tab-strip { margin: 0; padding: 6px 10px 0; align-items: flex-end; }
/* four long names: the folders give up width (down to 56px), never the + past the panel's edge (#853) */
.chat-tabs.tab-strip > a { flex-shrink: 1; min-width: 56px; max-width: 108px; margin-right: 0; padding: 5px 28px 5px 10px;
  overflow: hidden; font-size: 12.5px; cursor: pointer; }
.chat-tabs.tab-strip > a[aria-current='page'] { background: var(--surface); }
.chat-tab-x { position: relative; z-index: 3; flex: none; align-self: center; display: inline-grid; place-items: center;
  width: 16px; height: 16px; margin: 1px 3px 0 -25px; padding: 0; border: 0; border-radius: 5px; background: transparent;
  color: var(--muted); }
.chat-tab-x:hover { background: var(--soft); color: var(--text); }
.chat-newchat { flex: none; align-self: center; display: inline-grid; place-items: center; width: 22px; height: 22px;
  margin: 0 0 1px 10px; padding: 0; border: 1px solid var(--border-strong); border-radius: 50%; background: var(--surface);
  color: var(--secondary); }
.chat-newchat:hover { color: var(--text); border-color: var(--secondary); }
.chat-unread-dot { display: inline-block; width: 7px; height: 7px; margin-left: 4px; border-radius: 50%;
  background: var(--danger); vertical-align: middle; }
.chat-tabs > a > .chat-unread-dot { position: absolute; top: 3px; left: 3px; margin: 0; }
.chat-body { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; display: flex; flex-direction: column; }
.chat-sec { padding: 10px 14px 4px; color: var(--muted); font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; }
.chat-pr { display: flex; align-items: center; gap: 4px; padding: 0 8px 0 4px; }
.chat-pr:hover { background: var(--surface-alt); }
.chat-pr-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; padding: 7px 6px; border: 0;
  background: transparent; text-align: left; }
.chat-av { position: relative; flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid;
  place-items: center; background: var(--soft); color: var(--secondary); font-size: 11px; font-weight: 700; }
.chat-dot { position: absolute; right: -1px; bottom: -1px; width: 10px; height: 10px; border-radius: 50%;
  border: 2px solid var(--surface); background: var(--border-strong); }
.chat-dot.online { background: var(--chart-1); }
.chat-dot.idle { background: var(--warning-border); }
.chat-tx { display: flex; flex-direction: column; min-width: 0; }
.chat-nm { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-sub { color: var(--muted); font-size: 12px; }
.chat-unread { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--danger);
  color: var(--surface); font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; }
.chat-flash { margin: 12px 14px; color: var(--muted); }
.chat-flash.warn, .chat-hint.warn { color: var(--warning); }
.chat-cv { display: flex; flex-direction: column; min-height: 100%; }
.chat-cv-head { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--border); }
.chat-cv-head .chat-tx { flex: 1; }
.chat-msgs { flex: 1; min-height: 160px; overflow: auto; overscroll-behavior: contain; padding: 8px 12px; display: flex;
  flex-direction: column; gap: 8px; }   /* #251: scrolling past its end doesn't move the page */
.chat-eph { display: flex; align-items: center; justify-content: center; gap: 5px; color: var(--muted); font-size: 11px; }
.chat-msg { max-width: 82%; display: flex; flex-direction: column; gap: 2px; }
.chat-msg.me { align-self: flex-end; align-items: flex-end; }
/* a project chat (chat-projects.js): its square avatar, and whose message it is */
.chat-av.group { border-radius: 8px; }
.chat-from { padding: 0 4px; color: var(--muted); font-size: 11px; font-weight: 600; }
.chat-bubble { padding: 7px 10px; border-radius: 12px 12px 12px 4px; background: var(--surface-alt);
  border: 1px solid var(--border); white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-msg.me .chat-bubble { border-radius: 12px 12px 4px 12px; background: var(--focus-soft); }
.chat-pending { color: var(--muted); font-style: italic; }
.chat-failed { color: var(--danger); }
.chat-meta { display: flex; align-items: center; gap: 4px; color: var(--muted); font-size: 11px; }
.chat-rec { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-top: 1px solid var(--border); }
.chat-rec-row { display: flex; align-items: flex-end; gap: 8px; }
.chat-rec > .chat-hint { flex: none; }
/* the typing box grows with its lines (chat.js grow: up to 160px), then scrolls inside */
.chat-type { flex: 1; min-width: 0; height: 36px; min-height: 36px; max-height: 160px; padding: 8px 10px;
  border: 1px solid var(--border-strong); border-radius: 5px; background: var(--surface); color: var(--text);
  font: inherit; line-height: 18px; resize: none; overflow-y: hidden; }
.chat-paste { flex: none; display: inline-grid; place-items: center; width: 36px; height: 36px; padding: 0;
  border: 1px solid var(--border-strong); border-radius: 5px; background: var(--surface); color: var(--secondary); }
.chat-paste:hover { color: var(--text); background: var(--soft); }
.chat-hint { flex: 1; display: flex; align-items: center; flex-wrap: wrap; gap: 6px; color: var(--muted); font-size: 12px; }
.chat-recdot { width: 9px; height: 9px; border-radius: 50%; background: var(--danger); animation: chat-blink 1s infinite; }
@keyframes chat-blink { 50% { opacity: .25; } }
.chat-sheet { padding: 14px; display: flex; flex-direction: column; gap: 8px; }
.chat-q { font-weight: 700; }
.chat-why { color: var(--secondary); }
.chat-opt { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border: 1px solid var(--border);
  border-radius: 8px; background: transparent; text-align: left; }
.chat-opt.sel { border-color: var(--accent); background: var(--focus-soft); }
.chat-opt .chat-sub { margin-left: auto; }
.chat-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }
/* calls: the status icon (calling, connected with its time, ended, unreachable), the incoming call on the icon and
   as a message with Answer and Decline */
.chat-badge.call { display: grid; place-items: center; padding: 0; width: 20px; height: 20px; background: var(--chart-1);
  animation: chat-pulse 1.1s ease-in-out infinite; }
@keyframes chat-pulse { 50% { transform: scale(1.15); } }
.chat-chip { display: inline-flex; align-items: center; padding: 4px 8px; border: 1px solid var(--border-strong);
  border-radius: 999px; background: var(--surface); color: var(--text); box-shadow: 0 2px 10px var(--shadow-soft); }
.chat-cs { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; font: 500 11px var(--font-mono);
  color: var(--secondary); white-space: nowrap; vertical-align: middle; }
.chat-cs-ico { position: relative; width: 20px; height: 20px; display: grid; place-items: center; border-radius: 50%;
  border: 1px solid var(--border-strong); background: var(--surface); }
.chat-cs.calling .chat-cs-ico { color: var(--accent); border-color: var(--accent); }
.chat-cs.calling .chat-cs-ico::after { content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 1px solid var(--accent); animation: chat-ring 1.2s ease-out infinite; }
@keyframes chat-ring { from { opacity: .9; inset: -1px; } to { opacity: 0; inset: -7px; } }
.chat-cs.connected, .chat-cs.connected .chat-cs-ico { color: var(--chart-1); border-color: var(--chart-1); }
.chat-cs.ended, .chat-cs.ended .chat-cs-ico { color: var(--muted); }
.chat-cs.unreachable, .chat-cs.unreachable .chat-cs-ico { color: var(--warning); border-color: var(--warning-border); }
.chat-ib.hang { color: var(--danger); }
.chat-ib.hang:hover { background: var(--danger-bg); }
.chat-ib.on { color: var(--text); background: var(--selected-bg); }
.chat-ib:disabled { opacity: .35; cursor: default; }
.chat-tabs > a.ringing, .chat-cv-head.ringing { box-shadow: inset 0 -2px 0 var(--chart-1); }
.chat-msg.call { align-self: center; align-items: center; max-width: 92%; }
.chat-msg.call .chat-bubble { border-radius: 10px; background: var(--surface); color: var(--secondary); }
.chat-msg.call.ringing .chat-bubble { border-color: var(--chart-1); color: var(--text); box-shadow: 0 0 0 3px var(--soft); }
.chat-callline { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; }
.chat-msg.call .chat-row button { display: inline-flex; align-items: center; gap: 5px; }
.chat-msg.call button.danger { color: var(--danger); }
.chat-msg.call .chat-why { margin-top: 4px; font-size: 12px; }
.chat-audio, .chat-ptt-audio { display: none; }
/* Push to Talk: the switch in the panel's head, a person's tag (their switch), and in a conversation whether holding
   Space talks to them live (PTT), why not (PTT not available, the reason on hover), live, or them talking to me */
.chat-ptt-switch { height: 26px; padding: 0 8px; border: 1px solid var(--border-strong); border-radius: 5px;
  border-bottom: 2px solid var(--keycap-edge);   /* a keycap, as every button */
  background: transparent; color: var(--secondary); font: 700 11px var(--font-mono); letter-spacing: .04em; }
.chat-ptt-switch:active { border-bottom-width: 1px; transform: translateY(1px); }
.chat-ptt-switch.on { color: var(--text); }   /* its track (button.switch) shows on/off */
.chat-ptt-tag { margin-left: 6px; padding: 0 4px; border: 1px solid var(--border-strong); border-radius: 5px;
  color: var(--secondary); font: 700 9px/14px var(--font-mono); vertical-align: middle; }
.chat-ptt { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; padding: 0 6px; border-radius: 5px;
  font: 600 10.5px/16px var(--font-mono); white-space: nowrap; }
.chat-ptt.ok { color: var(--live); border: 1px solid var(--live); }
.chat-ptt.off { color: var(--muted); border: 1px dashed var(--border-strong); cursor: help; }
.chat-ptt.live { color: var(--surface); background: var(--danger); }
.chat-ptt.live.in { background: var(--live); }
.chat-chip.ptt { gap: 6px; border-color: var(--live); color: var(--live); font-weight: 600; }
.chat-chip.warn { max-width: 300px; color: var(--warning); border-color: var(--warning-border); text-align: left; }
/* the dock: the icon, and beside it four small buttons (#250): the microphone, the speaker (each red while off), the
   mode (Normal's bell, PTT, DND's moon) and Sound settings. The icon's ring while my microphone is open (danger) or a
   chat voice plays (live). Sound settings: which microphone and speaker, and what PTT and DND are */
.chat-dock { display: flex; align-items: center; gap: 8px; }
.chat-tools { display: flex; align-items: center; gap: 2px; padding: 2px; background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 10px; box-shadow: 0 2px 10px var(--shadow-soft); }
.chat-ib.off { color: var(--danger); }
.chat-ptt-btn { font: 700 10px var(--font-mono); letter-spacing: .04em; }
.chat-ptt-btn.on { color: var(--live); }
.chat-launcher.live::after, .chat-launcher.playing::after { content: ""; position: absolute; inset: -4px;
  border-radius: 50%; border: 2px solid var(--live); animation: chat-ring 1.2s ease-out infinite; pointer-events: none; }
/* the mic open: the ring is the microphone's live level (chat.js meter, #981), still in silence */
.chat-launcher.live::after { border-color: var(--danger); animation: none; transform: scale(calc(1 + var(--level, 0) * 0.45));
  transition: transform 60ms linear; }
.chat-snd-panel { width: 300px; background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: 12px; box-shadow: 0 8px 28px var(--shadow); padding-bottom: 6px;
  max-height: min(640px, calc(100dvh - 110px)); overflow-y: auto; }   /* as the chat panel: its head never off screen */
.chat-snd-sec { display: flex; flex-direction: column; gap: 6px; padding: 8px 14px; border-top: 1px solid var(--border); }
.chat-snd-l { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; }
.chat-snd-dev { color: var(--secondary); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the device dropdowns (the kit's select, #981): the full width, a long name cut with an ellipsis; the tests under
   them, kit buttons at the select's height with the live level beside Test microphone */
select.chat-dev { width: 100%; }
.chat-snd-test { display: flex; align-items: center; gap: 8px; }
.chat-snd-test-btn { align-self: flex-start; white-space: nowrap; min-height: var(--control-h); }   /* chat's 13px text: the token, as the select */
.chat-snd-test .chat-meter { height: 8px; }
.chat-snd-note { margin: 0; color: var(--secondary); font-size: 12px; line-height: 1.45; }
.chat-snd-note b { font: 700 10px var(--font-mono); letter-spacing: .04em; }
.chat-snd-note.warn { color: var(--warning); }
.chat-snd-btn, .chat-sw { align-self: flex-start; height: 26px; padding: 0 9px; border: 1px solid var(--border-strong); border-radius: 5px;
  border-bottom: 2px solid var(--keycap-edge); background: transparent; color: var(--text); font-size: 12px; }
.chat-snd-btn:active, .chat-sw:active { border-bottom-width: 1px; transform: translateY(1px); }
.chat-snd-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }   /* chat-screen's Take over switch */
.chat-sw { align-self: auto; min-width: 48px; color: var(--secondary); }
.chat-sw.on { color: var(--text); }   /* its track (button.switch) shows on/off */
/* Sound settings' Connection (chat-link.js): each call or press, its route and whether audio gets through */
.chat-link-row { display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
.chat-link-row .chat-snd-l { font-weight: 400; }
.chat-link-route { color: var(--secondary); }
.chat-link-route.failed, .chat-link-bad { color: var(--danger); }
.chat-link-ok { display: flex; align-items: center; gap: 8px; color: var(--secondary); }
.chat-meter { display: block; flex: 1; height: 6px; background: var(--soft); overflow: hidden; }
.chat-meter i { display: block; width: 0; height: 100%; background: var(--live); }
/* Toasts beside the chat (ui.js placeToasts): never over it. Above its button and chips; beside its panel while one
   is open; on a phone, hidden while its sheet is up (they come back when it closes) */
body:has(#chat-root) .toast-stack { bottom: calc(16px + var(--chat-h, 44px) + 12px); }
body.chat-wide:has(#chat-root) .toast-stack { bottom: 16px; left: calc(var(--sidebar-width) + 16px + var(--chat-w) + 12px);
  width: min(340px, calc(100vw - var(--sidebar-width) - var(--chat-w) - 160px)); }
@media (max-width: 720px) {
  body:has(#chat-root .chat-panel) .toast-stack, body:has(#chat-root.phone) .toast-stack { display: none; }
  .chat-root { left: 12px; bottom: 12px; }
  body.nav-open .chat-root { z-index: 45; }
  /* a sheet over the page; the icon stays in its corner */
  .chat-panel { position: fixed; left: 0; right: 0; bottom: 0; width: auto; max-height: 85dvh; border-radius: 14px 14px 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-recdot, .chat-badge.call, .chat-cs.calling .chat-cs-ico::after, .chat-launcher.live::after,
  .chat-launcher.playing::after { animation: none; }
}

/* Hold Option to dictate (dictate.js, UI_RULES "Keys: voice and pen input"): a round mark at the field's top right corner, the mic
   while listening (the state colour of a live mic, as chat's), then the circle spinner while the words come back */
.dictate-mark { position: fixed; inset: auto; margin: 0; padding: 0; width: 22px; height: 22px; display: grid;
  place-items: center; border: 0; border-radius: 50%; background: var(--danger); color: var(--surface);
  box-shadow: inset 0 0 0 calc(var(--level, 0) * 5px) var(--danger-bg); pointer-events: none; z-index: 2147483001;
  overflow: hidden; transition: box-shadow 60ms linear; }
.dictate-mark svg { width: 13px; height: 13px; transform: scale(calc(1 + var(--level, 0) * 0.2)); }
/* the live level (#981): a pale ring inside the red circle and the icon grow with the sound the microphone picks up,
   still in silence; never past the circle, which sits inside the field (#1019: it spilled over the text box) */
.dictate-mark.busy { background: var(--surface); box-shadow: none; border: 2px solid var(--border-strong);
  border-top-color: var(--accent); animation: activity-spin 1s linear infinite; }
/* Phones: the bell at the header's top right opens the notifications list (phone-bell.js); not on a desktop */
.phone-bell { display: none; }
@media (max-width: 720px) {
  .phone-bell:not([hidden]) { position: relative; display: grid; place-items: center; flex: none; width: 44px;
    height: 44px; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--text); }
  .phone-bell:hover { background: var(--soft); }
  .phone-bell-count { position: absolute; top: 4px; right: 2px; min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 9px; background: var(--danger); color: var(--surface); font-size: 11px; font-weight: 700;
    line-height: 18px; text-align: center; }
  .phone-bell-count[hidden] { display: none; }
}
/* Phones: hold the mic at the bottom right while a text field has the focus (dictate.js places it above the keyboard) */
.dictate-mic { position: fixed; inset: auto 12px 12px auto; margin: 0; width: 56px; height: 56px; padding: 0;
  display: grid; place-items: center; border: 1px solid var(--accent-edge); border-radius: 50%; background: var(--accent);
  color: var(--on-accent); box-shadow: var(--hard-shadow); touch-action: none; user-select: none;
  -webkit-user-select: none; -webkit-touch-callout: none; z-index: 2147483000; overflow: visible; }
.dictate-mic[hidden] { display: none; }
.dictate-mic svg { width: 26px; height: 26px; }
.dictate-mic.live { background: var(--danger); border-color: var(--danger);
  box-shadow: inset 0 0 0 calc(var(--level, 0) * 12px) var(--danger-bg); transition: box-shadow 60ms linear; }
/* Admin → Connections → Business Systems: a connector's pop-up (connections.js) */
/* in its sheet: the sheet is the frame (no second frame inside it) */
.connector-card { max-width: 980px; margin: 0 0 18px; }
.connector-card h3 { margin: 0 0 6px; }
.connector-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0; }
.connector-bar input[type='password'] { flex: 1 1 220px; min-width: 0; }
.connector-switch { display: inline-flex; align-items: center; gap: 4px; margin-right: 16px; }
.connector-small { font-size: 13px; margin: 4px 0; }
.connector-log { max-height: 260px; overflow-y: auto; padding-left: 18px; }
.connector-mapping select { max-width: 100%; }

/* ==== Onboarding (onboarding.js, docs/ONBOARDING.md): one card at a time over the dashboard, above everything (the
   server answers nothing else until it is done). Avatars, colour swatches and the spinner are round marks. ==== */
.onb-layer { position: fixed; inset: 0; z-index: 2147483002; display: grid; place-items: center; overflow: auto;
  padding: 24px 16px; background: var(--scrim); }
.onboarding-open body { overflow: hidden; }
.onb-card { width: min(580px, 100%); background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: 6px; box-shadow: none; display: grid; }   /* one frame, no offset edge, as the sign-in card */
.onb-head { padding: 16px 20px 0; display: grid; gap: 8px; }
.onb-head h2 { margin: 0; font-size: 19px; }
.onb-sub { margin: 0; color: var(--secondary); }
.onb-body { padding: 16px 20px; display: grid; gap: 14px; }
.onb-body p { margin: 0; }
.onb-error { margin: 0 20px 12px; color: var(--danger); }
.onb-foot { padding: 12px 20px 16px; display: flex; gap: 8px; justify-content: flex-end; align-items: center;
  flex-wrap: wrap; border-top: 1px solid var(--border); }
.onb-leave { margin-right: auto; }
.onb-foot button { white-space: nowrap; }
.onb-kbd { margin-left: 8px; font: inherit; font-size: 11px; }
.onb-titlebar { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.onb-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.onb-field { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: 4px; min-width: 0; }   /* the phone row never wider than the card */
.onb-field.wide { grid-column: 1 / -1; }
.onb-field label { font-size: 12px; font-weight: 600; color: var(--secondary); }
.onb-hint { font-size: 12px; color: var(--secondary); }
.onb-hint.onb-problem { color: var(--danger); }
.onb-phone { display: flex; gap: 6px; align-items: center; }
.onb-fields :is(input, select) { font-size: 14px; }
.onb-fields input { padding-left: 10px; padding-right: 10px; border: 1px solid var(--border-strong); background: var(--surface); color: var(--text); }
.onb-phone select { width: 150px; flex: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; align-items: center; }   /* base-select lays the value out as flex: stretch pins the text to the top */
.onb-phone input { flex: 1; min-width: 0; }
.onb-person { display: flex; gap: 10px; align-items: center; padding: 8px 10px; border: 1px solid var(--border);
  background: var(--canvas); }
.onb-person small { display: block; color: var(--muted); }
.onb-av { width: 28px; height: 28px; border-radius: 18% !important; image-rendering: pixelated; border: 1px solid var(--border-strong);
  flex: none; }
.onb-av.lg { width: 44px; height: 44px; }
.onb-photo-row { display: grid; grid-template-columns: 1fr 150px; gap: 14px; align-items: stretch; }
.onb-drop.create-tile { min-height: 180px; width: 100%; }
.onb-photo { max-height: 160px; max-width: 100%; object-fit: cover; }
.onb-example { margin: 0; display: grid; gap: 6px; align-content: start; font-size: 12px; color: var(--muted); }
.onb-example img { width: 100%; image-rendering: pixelated; border: 1px solid var(--border); }
.onb-note { font-size: 12px; color: var(--secondary); border-left: 2px solid var(--border-strong); padding-left: 10px; }
.onb-warn { font-size: 13px; color: var(--warning); background: var(--warning-bg); border-left: 4px solid var(--warning-border);
  padding: 6px 10px; }
.onb-pick { grid-template-columns: 200px 1fr; gap: 18px; align-items: start; }
.onb-big { display: grid; gap: 8px; justify-items: center; }
.onb-big-img, .onb-opt-img { aspect-ratio: 1; background: var(--soft) center / contain no-repeat; image-rendering: pixelated; }
.onb-big-img { width: 200px; border: 1px solid var(--border-strong); }
.onb-who { font-weight: 650; }
.onb-pick-right { display: grid; gap: 8px; min-width: 0; align-content: start; }
.onb-label { font-size: 12px; font-weight: 600; color: var(--muted); }
.onb-opts { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.onb-opt { border: 1px solid var(--border); padding: 3px; background: var(--canvas); cursor: pointer; display: grid; gap: 3px;
  font-size: 11px; color: var(--secondary); text-align: center; }
.onb-opt[aria-checked='true'] { background: var(--selected-bg); border-color: var(--secondary); color: var(--text); }
.onb-opt:not([data-status='ready']) { cursor: default; }
.onb-opt-img { display: grid; place-items: center; align-content: center; gap: 4px; }
.onb-opt-img small { color: var(--danger); }
.onb-opt-img button.small { font-size: 11px; padding: 1px 6px; }
.onb-spin { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--border); border-top-color: var(--secondary);
  animation: activity-spin 0.9s linear infinite; }
.onb-swatches { display: flex; flex-wrap: wrap; gap: 8px; }
.onb-swatch { width: 30px; height: 30px; padding: 0; border-radius: 50%; border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px var(--border-strong); cursor: pointer; }
.onb-swatch[aria-checked='true'] { box-shadow: 0 0 0 1px var(--surface), 0 0 0 3px var(--secondary); }
.onb-tries { font-size: 12px; color: var(--muted); }
.onb-done { display: grid; gap: 8px; }
.onb-mini-top { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border);
  background: var(--canvas); font-size: 13px; }
.onb-mini-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
:is(.onb-swatch, .onb-spin) { border-radius: 50% !important; }
.onb-card { border-radius: 6px !important; }
@media (max-width: 560px) {
  .onb-fields, .onb-photo-row, .onb-pick { grid-template-columns: 1fr; }
  .onb-opts { grid-template-columns: repeat(3, 1fr); }
  .onb-big-img { width: 160px; }
}
@media (prefers-reduced-motion: reduce) { .onb-spin { animation-duration: 3s; } }

/* Dev mode's build stamp: commit and deploy time, under Spot's corner (bottom 20px) and the floating controls; not on phones.
   Above every page's own layers (on AI Agent the browser and the right bar reach the window's foot; the stamp sits in
   the room kept for Spot under the address bar) and the floating chat and Spot (60-61), under the report panel and
   the corner card (80) and dialogs (owner, 2026-11-01: it had gone from AI Agent) */
.build-stamp { position: fixed; right: 8px; bottom: 3px; z-index: 78; margin: 0; padding: 0 4px; border: 0; background: var(--surface);
  color: var(--muted); font: 10px/14px ui-monospace, SFMono-Regular, Menlo, monospace; cursor: pointer; }
.build-stamp:hover { color: var(--text); }
.build-stamp[hidden] { display: none; }
.build-sizes { position: fixed; bottom: 3px; z-index: 78; display: flex; gap: 6px; padding: 0 4px; background: var(--surface);
  color: var(--muted); font: 10px/14px ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
.build-sizes button { margin: 0; padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer;
  text-decoration: underline dotted; }
.build-sizes button:hover { color: var(--text); }
.build-sizes[hidden] { display: none; }
@media (max-width: 720px) { .build-stamp, .build-sizes { display: none; } }
/* on a phone the stamp is the last line of the Settings menu, under Dev mode: tap copies the commit (#900) */
.build-stamp-menu { display: none; }
@media (max-width: 720px) {
  .build-stamp-menu:not([hidden]) { display: block; max-width: 100%; margin: 2px 0 0; padding: 0 8px; border: 0; background: none;
    color: var(--muted); font: 10px/16px ui-monospace, SFMono-Regular, Menlo, monospace; text-align: left;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; user-select: text; -webkit-user-select: text; cursor: pointer; }
}

/* Settings → Theme: all four themes side by side in one segmented control; the labels never wrap, and on a phone the
   control is two rows of two rather than a wider menu. */
.theme-switch button { white-space: nowrap; min-width: 0; padding: 4px 10px; }
@media (max-width: 720px) {
  .theme-switch { display: grid; grid-template-columns: 1fr 1fr; height: auto; }
}

/* ==== Notifications (notifications.js): the bell on the top bar with its unread count; its side sheet, newest first,
   unread in bold, each with its × ==== */
.header-actions button.notify-bell { position: relative; }
@media (max-width: 720px) { .header-actions button.notify-bell { display: none; } }   /* phones: #phone-bell */
.notify-count { position: absolute; top: -3px; right: -4px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 8px; background: var(--danger); color: var(--surface); font-size: 10.5px; font-weight: 700;
  line-height: 16px; text-align: center; pointer-events: none; }
.notify-bar { display: flex; justify-content: flex-end; padding: 0 0 8px; }
.notify-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.notify-item { display: flex; align-items: flex-start; gap: 4px; border-bottom: 1px solid var(--border); }
.notify-open { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding: 9px 6px; border: 0;
  border-radius: 0; background: none; color: var(--text); text-align: left; font: inherit; cursor: pointer; }
.notify-open:hover { background: var(--soft); }
.notify-title { font-weight: 400; overflow-wrap: anywhere; }
.notify-item.unread .notify-title { font-weight: 700; }
.notify-body { color: var(--secondary); font-size: 12.5px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.notify-when { color: var(--muted); font-size: 12px; }
.notify-dismiss { flex: none; width: 26px; height: 26px; margin-top: 6px; padding: 0; border: 0; border-radius: 5px;
  background: none; color: var(--muted); cursor: pointer; }
.notify-dismiss:hover { background: var(--soft); color: var(--text); }
.notify-empty { padding: 14px 6px; }
/* phones: no toasts for notifications (Haofeng, 2026-10-30), also those shown before the window narrowed (#865) */
@media (max-width: 720px) { .toast[data-group="notify"] { display: none; } }
.notify-more { margin: 10px 0; }
@media (max-width: 720px) {   /* phones: what a finger taps is 44px (UI_RULES T6, #994) */
  .notify-sheet button.icon-button.notify-dismiss { width: 44px; height: 44px; margin-top: 0; }
  .notify-sheet button.notify-all { min-height: 44px; }
}

/* Phones (Haofeng, 2026-10-30): the bottom corners are the chat's button (left) and the dictation mic (right), so the
   toasts come down from the top, under the top bar. Desktop: bottom left as above */
@media (max-width: 720px) {
  .toast-stack, body:has(#chat-root) .toast-stack, body.chat-wide:has(#chat-root) .toast-stack {
    top: calc(env(safe-area-inset-top, 0px) + 56px); bottom: auto; left: 12px; right: 12px; width: auto; }
}
/* ==== The menu bar (owner, 2026-10-31, bar B): one bar across the whole window, above the sidebar and the page, like an
   operating system's menu bar. Far left an empty zone as wide as the sidebar (the person's avatar is their own dock
   icon, #1065; who is signed in and Sign out are first in the Settings menu) (--sidebar-width, so it follows the rail too); the title starts exactly at the sidebar's right edge;
   the company, Report an issue and Settings on the right. The sidebar starts under it. Controls stay 30px
   (--control-h-sm), so the bar is 44px. ==== */
:root { --bar-h: 44px; }
#menubar { position: fixed; inset: 0 0 auto 0; height: var(--bar-h); margin: 0; padding: 0 18px 0 0; gap: 0;
  justify-content: flex-start; background: var(--surface); border-bottom: 1px solid var(--border); }
#menubar .bar-brand { flex: none; display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  width: var(--sidebar-width); height: 100%; padding: 0; overflow: visible; white-space: nowrap; }
#menubar .bar-name { flex: none; color: var(--text); font-size: 14px; font-weight: 700;
  letter-spacing: -0.2px; text-decoration: none; }
/* the breadcrumb: Workbench / the app / the view, one line, the separators muted */
#menubar .breadcrumb { display: flex; align-items: center; gap: 8px; }
#menubar .breadcrumb strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 14px; }
#menubar .crumb-sep, #menubar #crumb-tail::before { color: var(--muted); font-weight: 400; }
#menubar .crumb-sep:has(+ #crumb:empty) { display: none; }
#menubar #crumb-tail { flex: none; display: inline-flex; gap: 8px; font-size: 14px; font-weight: 600; }
#menubar #crumb-tail::before { content: '/'; }
#menubar #crumb:has(+ #crumb-tail:not([hidden])) { font-weight: 400; }
#menubar .launch-marker { position: static; align-self: center; }
#menubar #settings-menu-list .whoami { margin: 0; white-space: nowrap; font-size: 12px; }
#menubar .breadcrumb { flex: 1 1 auto; min-width: 0; padding-left: var(--page-gutter, 18px); }
/* a project's crumb (H14): Projects / Name, then its rename pen and tags on the title's line; a long name is cut last */
#crumb .project-title { display: flex; align-items: center; gap: 8px; min-width: 0; }
#crumb .project-title > * { flex: none; margin: 0; }
#crumb .crumb-up { color: var(--secondary); font-weight: 400; text-decoration: none; }
#crumb .crumb-up:hover { color: var(--text); text-decoration: underline; }
#crumb .crumb-sep { color: var(--muted); font-weight: 400; }
#crumb .project-title > .crumb-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
#crumb .project-edit { color: var(--muted); font-weight: 400; }
#menubar .breadcrumb:has(input.project-rename), #crumb .crumb-name:has(input.project-rename) { overflow: visible; }
/* a phone's bar has room for the name alone: Projects / is in the drawer; the name is cut, never squeezed away */
@media (max-width: 720px) {
  #crumb :is(.crumb-up, .crumb-sep) { display: none; }
  #crumb .project-title > .crumb-name { min-width: 48px; }
  #crumb .project-title { gap: 6px; }
}
#menubar .header-actions { margin-left: auto; }
main { padding-top: var(--bar-h); }
.sidebar { top: var(--bar-h); }
.playground-banner { top: var(--bar-h); }
html:lang(zh) #menubar .bar-name { letter-spacing: 0; }
@media (max-width: 720px) {
  /* the phone keeps its own top: the title, the menu right; the dock is the drawer over everything */
  #menubar { padding: 0 6px 0 0; }
  #menubar .bar-brand { width: auto; border-right: 0; padding: 0 8px 0 10px; }
  #menubar .breadcrumb { padding-left: 0; }
  #menubar .breadcrumb > :is(.bar-name, .crumb-sep) { display: none; }
  .sidebar { top: 0; }
}

/* ==== The dock (owner, 2026-11-01; docs/UI_RULES.md "The dock"): on a desktop the apps are a column of rounded-square
   icons, 64px wide, full height under the menu bar, like a computer's dock. Each icon is a 44px square (12px corners),
   flat: a light tint of the app's own colour behind a line glyph in that colour; no shadow, no gradient. Its name shows
   only as a tooltip on hover or focus (data-name, app.js; also its aria-label), a small bar left of the open app. A
   thin rule after My Projects; Admin alone at the foot. Hidden from it for now (data-dock="hidden"): Systems,
   Tools, Flight Ops, whose pages stay reachable by link. A phone's drawer lists the same apps with names.
   The palette is fixed: one colour per app, its tint mixed from it, so it reads in every theme. ==== */
:root {
  --sidebar-width: 64px;
  --app-me: #5b45d6; --app-projects: #2f6fdb; --app-pm: #d9670f; --app-hardware: #1e9150; --app-software: #2b333d;
  --app-mechanical: #cf3434; --app-database: #0f8792; --app-admin: #5f6b75; --app-mrp: #a8650a; --app-sales: #b8327a; --app-skills: #4d7c0f; --app-other: #64748b;
}
:root[data-theme-mode='dark'] { --app-software: #aeb8c2; --app-admin: #a3adb6; }
.sidebar nav a { --hue: var(--app-other); }
.sidebar nav a[data-tab='bench'] { --hue: var(--app-me); }
.sidebar nav a[href='#projects'] { --hue: var(--app-projects); }
.sidebar nav a[data-section='management'] { --hue: var(--app-pm); }
.sidebar nav a[data-section='electrical'] { --hue: var(--app-hardware); }
.sidebar nav a[data-tab='software'] { --hue: var(--app-software); }
.sidebar nav a[data-section='mechanical'] { --hue: var(--app-mechanical); }
.sidebar nav a[data-section='parts'] { --hue: var(--app-database); }
.sidebar nav a[data-section='mrp'] { --hue: var(--app-mrp); }
.sidebar nav a[data-section='sales'] { --hue: var(--app-sales); }
.sidebar nav a[data-tab='skills'] { --hue: var(--app-skills); }
.sidebar nav a[href='#admin'] { --hue: var(--app-admin); }
[data-dock='hidden'] { display: none !important; }
.dock-gap { display: none; }
@media (min-width: 721px) {
  .sidebar { padding: 10px 0; background: var(--surface); }
  .sidebar nav { margin: 0; gap: 8px; align-items: center; }
  .sidebar nav, .nav-scroll { overflow: visible; }
  .nav-scroll, .nav-group { display: flex; flex-direction: column; align-items: center; gap: 8px; }
  .nav-scroll > *, .nav-group > * { flex-shrink: 0; }
  .nav-scroll { gap: 8px; }
  .dock-gap { display: block; width: 28px; height: 1px; margin: 2px 0; background: var(--border); }
  .sidebar nav .admin-nav { padding: 0; border: 0; }
  .sidebar nav a, .sidebar nav a.active { position: relative; display: grid; place-items: center; box-sizing: border-box;
    width: 44px; height: 44px; padding: 0; gap: 0; border-radius: 12px !important;
    background: color-mix(in srgb, var(--hue) 13%, var(--surface)); color: var(--hue); }
  .sidebar nav .nav-icon, .sidebar nav a.active .nav-icon { display: grid; place-items: center; color: inherit; }
  :root[data-theme-mode='dark'] .sidebar nav a { color: color-mix(in srgb, var(--hue) 55%, white);
    background: color-mix(in srgb, var(--hue) 24%, var(--surface)); }
  .sidebar nav .nav-icon svg { width: 24px; height: 24px; stroke-width: 1.8; }
  /* the person's own icon is their picture (app.js myAvatar), exactly the tile's square: its box is the tile's, not
     the 20px glyph box, or the picture spills onto the next app (#1068) */
  .sidebar nav .nav-icon:has(> .avatar) { width: 100%; height: 100%; overflow: hidden; }
  .sidebar nav .nav-icon > .avatar { --avatar: 44px; }
  .sidebar nav a:hover, .sidebar nav a:focus-visible { outline: 2px solid color-mix(in srgb, var(--hue) 35%, transparent);
    outline-offset: 0; }
  /* the name, the count and the tags are not drawn on the icon: the tooltip carries the name */
  .sidebar nav a > :is(.nav-text, b) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
    white-space: nowrap; }
  /* what waits on the person, a Proto tag or New: a small dot at the icon's corner */
  .sidebar nav a > :is(.nav-badge, .nav-launch), .sidebar nav a[data-fresh]::after { position: absolute; top: -2px;
    right: -2px; width: 9px; height: 9px; min-width: 0; margin: 0; padding: 0; border: 2px solid var(--surface);
    border-radius: 50% !important; background: var(--warning); color: transparent !important; font-size: 0 !important;
    overflow: hidden; content: ''; box-sizing: border-box; }
  .sidebar nav a[data-fresh]::after { background: var(--accent); }
  /* the open app: a small bar at the dock's left edge */
  .sidebar nav a.active::after { content: ''; position: absolute; left: -10px; top: 12px; width: 3px; height: 20px;
    border-radius: 2px !important; background: var(--text); }
  .sidebar nav a.active[data-fresh]::after { left: -10px; top: 12px; right: auto; width: 3px; height: 20px; border: 0;
    border-radius: 2px !important; background: var(--text); }
  /* the tooltip: the app's name to the right of the icon */
  .sidebar nav a::before { content: attr(data-name); position: absolute; left: 54px; top: 50%; transform: translateY(-50%);
    padding: 4px 8px; border-radius: 5px !important; background: var(--text); color: var(--surface); font-size: 12px; font-weight: 500;
    line-height: 16px; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .12s; z-index: 50; }
  .sidebar nav a:is(:hover, :focus-visible)::before { opacity: 1; }
  .sidebar:has(nav a:hover) { z-index: 48; }
}
/* a short window (#1029): the apps scroll above Admin rather than run under it; a scroller clips the tooltip, so
   there the name is the link's aria-label only */
@media (min-width: 721px) and (max-height: 760px) {
  .sidebar nav .nav-scroll { padding-block: 2px; overflow: hidden auto; }
  .sidebar nav .nav-scroll a::before { display: none; }
}
/* the phone's drawer: the same apps with their names, each icon its small tile */
@media (max-width: 720px) {
  .sidebar nav .nav-icon { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px !important;
    background: color-mix(in srgb, var(--hue) 13%, var(--surface)); color: var(--hue); }
  .sidebar nav .nav-group[data-place] { display: grid; gap: 2px; }
}
/* ==== Every page is a full-window app (owner, 2026-11-01): no margin round the page; its tab strip sits flush under
   the menu bar and runs from the dock to the window's edge. ==== */
@media (min-width: 721px) {
  .content { padding-top: 14px; max-width: none; }
  :is(#subtabs, #admin-sections, #tab-portal > .project-tabs:first-child) {
    margin: -14px calc(-1 * var(--page-gutter)) 14px; padding-left: var(--page-gutter); padding-right: var(--page-gutter);
    background-color: var(--surface); }
}

/* a state over a pane (the cloud browser's: connecting, ended, an error), and over the whole page when Workbench is
   open in another window (app.js coverWindow): above everything, the top bar and popups included */
.cb-cover {
  position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 8px;
  background: var(--surface); text-align: center; padding: 16px;
}
.cb-cover[hidden] { display: none; }
.cb-cover.page-cover { position: fixed; z-index: 2147483000; background: var(--paper, var(--surface)); }
/* The site's Copy button beside selected text (mouse.js; docs/UI_RULES.md "Mouse"): one line, over everything */
.copy-float { position: fixed; z-index: 1000; padding: 4px 12px; border-radius: 999px; white-space: nowrap;
  box-shadow: 0 2px 8px var(--scrim); }
.copy-float[hidden] { display: none; }
/* Off limits to the AI (off-limits.js; UI_RULES "Off limits to the AI"): a page or control marked data-no-agent has a
   light red border, so everyone sees the AI (and a colleague in Take control) can't work there */
[data-no-agent]:not([data-no-agent='quiet']) { outline: 1px solid var(--no-agent); outline-offset: 1px; }
/* nothing in the top bar draws it: its controls stay refused to the AI, without the border (owner, 2026-11-01, #1071) */
#menubar [data-no-agent]:not(:focus-visible) { outline: none; }
/* data-no-agent="quiet": still refused to the AI, without the border (the person's Settings page, whose tab carries the
   mark; the chat's own controls; Admin's page, whose dock icon carries it: owner, 2026-11-01, #1060, #1065) */
/* the person's Settings: the line under the tab strip and the Settings tab's outline in light red, one folder tab */
.tab-strip.no-agent-strip { background: linear-gradient(to top, var(--no-agent) 1px, transparent 1px); }
.tab-strip.no-agent-strip > a[aria-current='page'] { border-color: var(--no-agent); }
/* The person's AI working this window (agent-window.js): where it clicks, types or scrolls, as Take over's cursor */
.agent-mark { position: fixed; z-index: 2147482400; left: 0; top: 0; pointer-events: none; }
.agent-mark::before { content: ''; position: absolute; left: -11px; top: -11px; width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--accent); background: color-mix(in srgb, var(--accent) 22%, transparent);
  animation: agent-mark 1.6s ease-out forwards; }
.agent-mark span { position: absolute; left: 12px; top: 10px; padding: 1px 6px; border-radius: 8px; background: var(--accent);
  color: var(--surface); font-size: 11px; font-weight: 600; white-space: nowrap; animation: agent-mark-label 1.6s ease-out forwards; }
@keyframes agent-mark { 0% { transform: scale(0.6); opacity: 1; } 60% { transform: scale(1.3); opacity: 1; } 100% { transform: scale(1.5); opacity: 0; } }
@keyframes agent-mark-label { 0%, 70% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .agent-mark::before, .agent-mark span { animation: none; } }
.chat-attachment { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; padding: 2px 6px; margin-top: 4px; border: 1px solid var(--border); border-radius: 5px; font-size: 11px; overflow-wrap: anywhere; }
.chat-quote { display: flex; flex-direction: column; gap: 3px; width: 100%; text-align: left; white-space: pre-wrap; overflow-wrap: anywhere; background: var(--soft); color: var(--muted); border: 0; border-left: 3px solid var(--muted); border-radius: 5px; padding: 6px 8px; margin-bottom: 6px; font-size: 12px; }
.chat-quote-composer { display: flex; gap: 6px; width: 100%; }
.chat-quote-composer .chat-quote { flex: 1; min-width: 0; }
.chat-quote-action { opacity: 0; font-size: 11px; padding: 2px 6px; }
.chat-msg:hover .chat-quote-action, .chat-msg:focus-within .chat-quote-action, .chat-msg.quoting .chat-quote-action { opacity: 1; }
@media (hover: none) { .chat-quote-action { opacity: 1; } }

/* ==== Phones: every control a finger taps is at least 40px (the phone basics, 2026-11-02; UI_RULES T6): the two
   control tokens grow, so rows still match; the folder tabs and sub-tabs (words with lines) get the same height.
   tests/test_browser_e2e_phone_basics.py measures it. ==== */
@media (max-width: 720px) {
  :root { --control-h: 44px; --control-h-sm: 40px; }
  .tab-strip > a, :is(.tab-strip.project-subtabs > a, .spec-tablist > button, .connection-tabs > button, .kpi-nav > a) {
    min-width: 40px; min-height: 40px; display: inline-flex; align-items: center; }
  button.icon-button, button.icon-button.small { width: 40px; height: 40px; }
  button:is(.linkish, .link-button) { min-height: 40px; }
  /* segmented controls (English / 中文 / Français, a view's choices): each choice is a target of its own */
  :is(.language-toggle, .view-toggle) { height: auto; }
  :is(.language-toggle, .view-toggle) button { min-height: 40px; }
  /* My Files: a row a finger can pick, its triangle the row's height */
  .finder-table td { height: 40px; }
  .finder-toggle { width: 40px; height: 40px; margin: -12px -12px -12px -14px; }
}

/* --- phone chrome: footer microphone, Report an issue */
/* A page's own footer microphone (dictate.js WBDictate.footerMic): round (an approved exception to H13's scale), at
   the footer's right; the accent while a text field it serves has the focus, grey otherwise ("Tap a text field
   first" over it while held), the live mic's red while it records, its ring moving with the level. Phones only: a
   computer talks with Option, so there the mic and a voice-first field's keyboard button don't show. */
button.footer-mic.footer-mic { flex: none; margin: 0 0 0 auto; width: 56px; height: 56px; padding: 0; display: grid;
  place-items: center; border: 1px solid var(--accent-edge); border-radius: 50% !important; background: var(--accent);
  color: var(--on-accent); touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
button.footer-mic.footer-mic[aria-disabled='true'] { background: var(--soft); border-color: var(--border); color: var(--muted); }
button.footer-mic.footer-mic.live { background: var(--danger); border-color: var(--danger); color: var(--surface);
  box-shadow: inset 0 0 0 calc(var(--level, 0) * 12px) var(--danger-bg); }
button.footer-mic svg { width: 26px; height: 26px; }
button.footer-mic.footer-mic[hidden] { display: none; }
.footer-mic-tip { position: fixed; inset: auto; margin: 0; padding: 6px 10px; border: 1px solid var(--border-strong);
  border-radius: 6px !important; background: var(--text); color: var(--surface); font-size: 13px;
  box-shadow: var(--hard-shadow); pointer-events: none; z-index: 2147483001; }
/* a voice-first field's keyboard button (WBDictate.voiceFirst): pressed, the keyboard is on */
button.voice-keyboard { flex: none; width: 48px; padding: 0; display: grid; place-items: center; }
button.voice-keyboard svg { width: 22px; height: 22px; }
button.voice-keyboard[aria-pressed='true'] { background: var(--selected-bg); }
button.voice-keyboard[hidden] { display: none; }
@media (min-width: 721px) {
  button.footer-mic.footer-mic, button.voice-keyboard { display: none; }
}
@media (max-width: 720px) {
  /* Report an issue on the top bar's right, left of the bell, as the computer's (transparent, 12px, bold, the label
     only), a finger's 40px tall */
  main > header > button.report-issue { flex: none; height: var(--control-h-sm); margin: 0 2px 0 4px; padding: 0 10px;
    background: transparent; border-color: var(--border) var(--border) var(--keycap-edge); color: var(--secondary);
    font-size: 12px; font-weight: 600; white-space: nowrap; }
  main > header > button.report-issue:hover { background: var(--soft); border-color: var(--border-strong) var(--border-strong) var(--keycap-edge); }
  main > header > button.report-issue span:last-child { display: inline; }
  /* its sheet (bug_report.js openSheet): flush under the bar, the full width, its buttons and the mic at its foot */
  #bug-sheet.bug-sheet[open] { inset: var(--corner-top, 56px) 0 0 0; width: 100vw; max-width: 100vw;
    padding: 0 14px; display: flex; flex-direction: column; border-radius: 6px 6px 0 0 !important; }
  .bug-sheet-form { flex: 1; display: flex; flex-direction: column; min-height: 0; }
  .bug-sheet-hint { margin: 0 0 10px; color: var(--secondary); font-size: 13.5px; }
  .bug-sheet-text { width: 100%; min-height: 180px; padding: 10px 12px; font: inherit; font-size: 16px; resize: vertical; }
  .bug-sheet-keys { display: flex; justify-content: flex-end; margin-top: 8px; }
  .bug-sheet-keys button.voice-keyboard { height: var(--control-h-sm); }
  .bug-sheet-form .bug-notice:empty { display: none; }
  .bug-sheet-foot { position: sticky; bottom: 0; display: flex; align-items: center; gap: 10px; margin: auto -14px 0;
    padding: 10px 14px calc(12px + env(safe-area-inset-bottom)); background: var(--surface); border-top: 1px solid var(--border); }
  .bug-sheet-buttons { flex: 1; display: flex; gap: 10px; min-width: 0; }
  .bug-sheet-buttons > .primary { flex: 1; min-width: 0; }
  /* chat's button (the bottom left) steps away while the sheet is open: its foot is Cancel / Send and the mic */
  body:has(#bug-sheet[open]) #chat-root { display: none; }
}
