/* ============================================================================
   ds/components.css — the design system's shared components (docs/DESIGN_SYSTEM.md §4).

   Loaded in BOTH interfaces (a page moved onto the design system changes for everybody),
   so nothing here is scoped to the shell, which is ds/shell.css. Every rule's
   first class is .ds-<component> (design_system.COMPONENTS); every value is a token.
   The boards each component follows: A_List (control panel, list, chips, views, pager),
   Final_Form (sheet, fields, status bar, tabs), the flat chatter (Adnan, 12 October), A_P_modal (dialog),
   A_P_record (panels, smart buttons, avatars).
   ============================================================================ */

/* ---- the control panel: the page's first row (in the New shell it shares the row with the search) */
.ds-cp{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); min-width: 0; }
.ds-cp-title{ display: flex; align-items: center; gap: var(--sp-2); min-width: 0; margin: 0; }
.ds-cp-title h1,
.ds-cp .ds-cp-title h1{ margin: 0; font: var(--fw-medium) var(--fs-title)/1.2 var(--font-head); color: var(--text);
  white-space: nowrap; letter-spacing: normal; }
.ds-cp-sub{ color: var(--text-muted); font-size: var(--fs-sm); white-space: nowrap; }
/* a line of its own is a sentence: it wraps */
:is(p, div).ds-cp-sub:not(.ds-cpbar *){ white-space: normal; }
.ds-cp-actions{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.ds-cp-spacer{ flex: 1 1 0; }
/* A list row's own forms: each box stays beside its button, the group sits at the row's end. */
.ds-list-forms{ display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--sp-2); }
.ds-list-form{ display: flex; align-items: center; gap: var(--sp-1); min-width: 0; }
.ds-list-form > input{ flex: 0 1 150px; width: 150px; min-width: 0; }
.ds-cp-end{ display: flex; align-items: center; gap: var(--sp-3); margin-left: auto; color: var(--text-muted); font-size: var(--fs-base); }

/* ---- buttons: one height, a 3:1 edge; the primary is the brand fill with the ink that reads ---- */
.ds-btn{
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: var(--ctl-h); box-sizing: border-box; padding: 0 var(--ctl-pad-x);
  border: 1px solid var(--border-control); border-radius: var(--ctl-radius);
  background: var(--surface-1); color: var(--text); cursor: pointer; text-decoration: none; white-space: nowrap;
  font: var(--fw-medium) var(--fs-base)/1 var(--font-body);
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
.ds-btn:hover{ background: var(--surface-hover); border-color: var(--text-muted); }
.ds-btn:focus-visible{ outline: none; box-shadow: var(--focus-ring); }
.ds-btn-primary{ background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: var(--fw-bold); }
.ds-btn-primary:hover{ background: var(--accent); border-color: var(--border-selected); box-shadow: inset 0 0 0 1px var(--border-selected); }
.ds-btn-quiet{ border-color: transparent; background: transparent; }
.ds-btn-quiet:hover{ background: var(--surface-hover); border-color: transparent; }
.ds-btn-danger{ color: var(--tint-danger-fg); border-color: var(--tint-danger-line); }
.ds-btn-danger:hover{ background: var(--tint-danger-bg); border-color: var(--tint-danger-fg); }
.ds-btn-sm{ height: var(--ctl-h-sm); padding: 0 var(--sp-3); font-size: var(--fs-sm); }
.ds-btn-pill{ border-radius: var(--pill-radius); }
.ds-btn-icon{ width: var(--ctl-h); padding: 0; }
.ds-btn:is(:disabled, [aria-disabled="true"]){
  background: var(--surface-2); border-color: var(--border-strong); color: var(--text-disabled); cursor: not-allowed; box-shadow: none;
}
.ds-btn .ds-n{ padding: 0 var(--sp-2); border-radius: var(--pill-radius); background: var(--surface-facet);
  color: var(--text); font: var(--fw-bold) var(--fs-xs)/var(--badge-h) var(--font-body); }

/* ---- the view switcher (list · kanban · calendar) and the pager ---- */
.ds-views{ display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--ctl-radius); overflow: hidden; flex: none; }
.ds-views a{ display: inline-flex; align-items: center; justify-content: center; width: var(--ctl-h); height: var(--ctl-h);
  box-sizing: border-box; color: var(--text-muted); background: var(--surface-1); text-decoration: none; }
.ds-views a + a{ border-left: 1px solid var(--border-strong); }
.ds-views a:hover{ background: var(--surface-hover); color: var(--text); }
.ds-views a.on{ background: var(--surface-facet); color: var(--text); box-shadow: inset 0 0 0 1px var(--border-selected); }
.ds-pager{ display: inline-flex; align-items: center; gap: var(--sp-2); color: var(--text-muted); font-size: var(--fs-base);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.ds-pager a{ display: inline-flex; align-items: center; justify-content: center; width: var(--ctl-h); height: var(--ctl-h);
  border: 1px solid var(--border-strong); border-radius: var(--ctl-radius); color: var(--text); text-decoration: none; }
.ds-pager a:hover{ background: var(--surface-hover); }
/* the menu panel (moved from ds.css, which only base.html links): the top bar's menus on Home too */
.ds-menu-panel{ position: absolute; z-index: var(--z-menu); top: calc(100% + var(--sp-1)); left: 0; min-width: var(--menu-min); max-width: var(--menu-max);
  padding: var(--sp-1); background: var(--surface-3); border: 1px solid var(--border); border-radius: var(--radius-menu); box-shadow: var(--shadow-elev); }
.ds-menu-panel a{ display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); height: var(--menu-item-h);
  padding: 0 var(--sp-3); border-radius: var(--ctl-radius); color: var(--text); text-decoration: none; font-size: var(--fs-base); }
.ds-menu-panel a:is(:hover, :focus-visible){ background: var(--surface-hover); outline: none; }
.ds-menu.tb-group > summary{ display: inline-flex; align-items: center; padding: var(--sp-1) var(--sp-3); border-radius: var(--ctl-radius); font-weight: var(--fw-medium); cursor: pointer; }
.ds-menu-h{ padding: var(--sp-2) var(--sp-3) var(--sp-1); color: var(--text-muted); font: var(--fw-medium) var(--fs-sm)/1.2 var(--font-body); }
.ds-menu-h ~ a{ padding-left: var(--sp-5); }
.ds-menu-panel a{ font-weight: var(--fw-regular); }
.ds-menu-panel a.on{ background: var(--surface-selected); }
.ds-menu-panel.ds-menu-fixed{ position: fixed; left: var(--x); top: var(--y); }
.ds-pager a[aria-disabled="true"]{ color: var(--text-disabled); cursor: default; }

/* ---- the list (board A_List): 38 px rows, the header in bold ink, numbers to the right ----
   A page's table is `<table class="ds-list">`; static/js/mobilecards.js turns it into cards on a phone. */
.ds-list{ width: 100%; border-collapse: collapse; font-size: var(--fs-base); color: var(--text); background: var(--surface-1); }
.ds-list th{ height: var(--th-h); padding: 0 var(--sp-3); text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--border-strong); font: var(--fw-bold) var(--fs-base)/1.2 var(--font-body); color: var(--text); }
.ds-list td{ height: var(--row-h); padding: var(--sp-1) var(--sp-3); border-bottom: 1px solid var(--border); vertical-align: middle; }
.ds-list :is(th, td).num{ text-align: right; font-variant-numeric: tabular-nums; }
.ds-list tbody tr:hover td{ background: var(--surface-hover); }
.ds-list tbody tr.on td, .ds-list tbody tr[aria-selected="true"] td{ background: var(--surface-selected); }
.ds-list tbody tr[data-href]{ cursor: pointer; }
.ds-list td a{ color: var(--text); text-decoration: none; }
.ds-list td a:hover{ color: var(--accent-text); }
.ds-list td.strong, .ds-list td .strong{ font-weight: var(--fw-medium); }
.ds-list-wrap{ overflow-x: auto; background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius-menu); }
.ds-list-scroll{overflow-x:auto}
.ds-list-group td{ background: var(--surface-2); font-weight: var(--fw-bold); }
/* A word too long for its place (a pasted web address, a part #) wraps there, in the cell mobilecards.js
   marked: a row of one spanning cell on a desk, so it cannot stretch the table, and any cell of a phone's
   card. On a phone a row of one spanning cell also reads as one text, as it does on a desk: as a flex row,
   each word and link beside a tag stood in a column of its own and ran past the card. */
.ds-list td.m-longword[colspan]:only-child{ overflow-wrap: anywhere; }
@media (max-width: 767px){
  table.m-cards > :is(tbody, tfoot) > tr > td.m-longword{ overflow-wrap: anywhere; }
  table.m-cards > :is(tbody, tfoot) > tr > td.m-nolabel[colspan]:only-child{ display: block; }
}

/* ---- status chips: a word on a tint, never colour alone ---- */
.ds-chip{ display: inline-flex; align-items: center; flex: none; gap: var(--sp-1); padding: var(--chip-pad); border-radius: var(--chip-radius);
  font: var(--fw-medium) var(--fs-sm)/1.4 var(--font-body); white-space: nowrap;
  background: var(--tint-neutral-bg); color: var(--tint-neutral-fg); text-decoration: none; }
.ds-chip.tone-accent{ background: var(--tint-accent-bg); color: var(--tint-accent-fg); }
.ds-chip.tone-success{ background: var(--tint-success-bg); color: var(--tint-success-fg); }
.ds-chip.tone-danger{ background: var(--tint-danger-bg); color: var(--tint-danger-fg); }
.ds-chip.tone-warning{ background: var(--tint-warning-bg); color: var(--tint-warning-fg); }
/* an availability option's chip: wraps on balanced lines; its colours are the option's own, a tint of its dark ink in Dark */
.ds-chip.avail-chip{ display: inline-block; max-width: 100%; white-space: normal; text-wrap: balance; }
body.dark .ds-chip.avail-chip{ background: color-mix(in srgb, var(--avail-dark) 22%, transparent); color: var(--avail-dark); }
.ds-chip.tone-info{ background: var(--tint-info-bg); color: var(--tint-info-fg); }
.ds-chip.tone-violet{ background: var(--tint-violet-bg); color: var(--tint-violet-fg); }
.ds-chip.tone-muted{ background: var(--tint-muted-bg); color: var(--tint-muted-fg); }
/* ---- a tag chip: the colour is the reusable tags subsystem's own data (CORE_MODULE.md,
   "Reusable tags subsystem"), carried as the --tag-colour data property (never typed here —
   lock-in rule #8) — a swatch beside neutral ink, so an admin's arbitrary colour choice never
   has to pass a contrast check against chip text. ---- */
.ds-chip.tag{ background: var(--surface-2); color: var(--text); border: 1px solid var(--border); gap: var(--sp-2); }
.ds-chip.tag::before{ content: ''; flex: none; width: 10px; height: 10px; border-radius: var(--pill-radius); background: var(--tag-colour, var(--tint-neutral-fg));; outline: 1px solid var(--border-strong); }  /* a colour near the chip's own ground still shows as a swatch */

/* ---- panels: a white card with an optional head ---- */
.ds-panel{ background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius-card); min-width: 0; }
.ds-panel-head{ display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border); }
.ds-panel-head h2, .ds-panel-title{ margin: 0; font: var(--fw-medium) var(--fs-md)/1.3 var(--font-head); color: var(--text); }
.ds-panel-head .ds-panel-sub{ color: var(--text-muted); font-size: var(--fs-sm); }
.ds-panel-body{ padding: var(--sp-4); }

/* ---- the record sheet (board Final_Form): title, a two-column field grid, tabs ---- */
.ds-sheet{ background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius-menu);
  padding: var(--sheet-pad); min-width: 0; }
.ds-sheet-title{ margin: 0 0 var(--sp-4); font: var(--fw-medium) var(--fs-record)/1.2 var(--font-head); color: var(--text); }
.ds-fields{ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-1) var(--sp-6); }
.ds-field{ display: grid; grid-template-columns: var(--field-label-w) minmax(0, 1fr); align-items: baseline; gap: var(--sp-3);
  min-height: var(--row-h); padding: var(--sp-1) 0; }
.ds-field > label, .ds-field > .ds-field-label{ color: var(--text-muted); font-size: var(--fs-base); }
.ds-field > .ds-field-value{ color: var(--text); font-size: var(--fs-base); min-width: 0; overflow-wrap: anywhere; }
.ds-field-value a{ color: var(--accent-text); text-decoration: none; }
.ds-field-wide{ grid-column: 1 / -1; }
/* a multi-line box scrolls, so its baseline is its bottom edge and a baseline-aligned label would sit
   at the box's foot: beside a text area the label lines up with the box's first line instead */
.ds-field:has(textarea){ align-items: start; }
.ds-field:has(textarea) > :is(label, .ds-field-label){ padding-top: var(--sp-2); }
/* a one-line value with no length limit (a product's name): the box grows with its text instead
   of scrolling it out of sight; its rows attribute is the least it shows */
textarea[data-autogrow]{ field-sizing: content; resize: none; }

/* ---- the status bar: the record's states as one segmented control ---- */
.ds-statusbar{ display: inline-flex; flex-wrap: wrap; max-width: 100%; border: 1px solid var(--border-strong); border-radius: var(--ctl-radius); overflow: hidden; }
.ds-statusbar > *{ display: inline-flex; align-items: center; flex: none; white-space: nowrap; height: var(--ctl-h); padding: 0 var(--sp-4);
  color: var(--text); background: var(--surface-1); font-size: var(--fs-base); text-decoration: none; }
.ds-statusbar > * + *{ border-left: 1px solid var(--border-strong); }
.ds-statusbar > .done{ color: var(--tint-success-fg); }
.ds-statusbar > .on{ background: var(--accent); color: var(--on-accent); font-weight: var(--fw-bold); }

/* ---- the record page (board Final_Form; Adnan, 12 October — the quotation-page spec) ----
   An action row above the sheet: the actions at its left (one primary), the status bar at its
   right. Then ONE sheet: what needs attention as slim strips, the title with the related-record
   buttons at its right, the fields in two columns, the tabs and their panes. No card inside it. */
/* the breadcrumb, spaced in Classic too (New: shell.css) */
.ds-crumb{ display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2); min-width: 0; }
.ds-crumb > span{ color: var(--text-muted); }
.ds-recbar{ display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-2) var(--sp-4); margin: 0 0 var(--sp-2); min-width: 0; }
.ds-recbar-acts{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); min-width: 0; }
/* a form in the action row (an address box and its Send) stays on one line: a wrapping flex
   form is sized by its widest part, so the button dropped under the box (26 Oct 2026) */
.ds-recbar-acts form{ display: inline-flex; flex-wrap: nowrap; align-items: center; gap: var(--sp-2); margin: 0; }
.ds-recbar-acts form > .erp-input{ min-width: 0; }
.ds-recbar > .ds-statusbar{ margin-left: auto; }
.ds-sheet-head{ display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-2) var(--sp-4); margin: 0 0 var(--sp-4); }
.ds-sheet-head > .ds-sheet-title{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin: 0; }
.ds-sheet-buttons{ display: flex; justify-content: flex-end; flex: 1 1 auto; min-width: 0; }
.ds-fields-col{ display: flex; flex-direction: column; min-width: 0; }
.ds-strip-list{ display: flex; flex-direction: column; gap: var(--sp-2); margin: 0 0 var(--sp-4); }
.ds-strip-list:not(:has(> *)){ display: none; }
.ds-strip-list > :is(div, section, p, form){ margin: 0; border-radius: var(--radius-menu); box-shadow: none; }
/* a strip is a sentence; a row only with a spacer (DESIGN_SYSTEM.md, 18 Oct) */
.ds-strip{ display: block; padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-menu); background: var(--tint-info-bg); color: var(--text); font-size: var(--fs-base); }
.ds-strip:has(> .ds-cp-spacer){ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-3); }
.ds-strip > .ds-ic:first-child{ margin-right: var(--sp-2); vertical-align: text-bottom; }
.ds-strip:not(:has(> .ds-cp-spacer)) > :is(.ds-btn, button){ margin-left: var(--sp-2); }
.ds-strip.tone-warning{ background: var(--tint-warning-bg); }
.ds-strip.tone-danger{ background: var(--tint-danger-bg); }
.ds-strip.tone-success{ background: var(--tint-success-bg); }
.ds-strip.tone-muted{ background: var(--surface-2); }
.ds-pane[hidden]{ display: none; }
.ds-kpi.tone-success .ds-kpi-n{ color: var(--tint-success-fg); }
.ds-kpi.tone-danger .ds-kpi-n{ color: var(--tint-danger-fg); }
.ds-kpi.tone-warning .ds-kpi-n{ color: var(--tint-warning-fg); }
.ds-kpi.tone-accent .ds-kpi-n{ color: var(--tint-accent-fg); }
.ds-sheet-part + .ds-sheet-part{ border-top: 1px solid var(--border); margin-top: var(--sp-5); padding-top: var(--sp-5); }
.ds-menu-panel form{ margin: 0; }
.ds-menu-item{ display: flex; align-items: center; width: 100%; height: var(--menu-item-h); padding: 0 var(--sp-3); border: 0;
  border-radius: var(--radius-menu); background: none; color: var(--text); font: inherit; text-align: start; cursor: pointer; }
.ds-menu-item:is(:hover, :focus-visible){ background: var(--surface-hover); outline: none; }
.ds-menu-item:disabled{ color: var(--text-disabled); background: none; cursor: not-allowed; }
/* A menu entry that opens a nested list in place — the search panel's people checklist
   (2026-10-21): <details class="ds-menu-sub"><summary class="ds-menu-item">…</summary>…</details>.
   The nested list scrolls after eight rows, so the menu keeps its place under its button. */
.ds-menu-sub > summary{ list-style: none; }
.ds-menu-sub > summary::-webkit-details-marker{ display: none; }
.ds-menu-scroll{ max-height: calc(var(--menu-item-h) * 8); overflow-y: auto; }
/* A list's "Select all" on a phone (static/js/mobilecards.js): the table's header — and the
   select-all box in it — is hidden when its rows become cards, so this line stands in for it. */
.ds-list-selall{ display: none; }
@media (max-width: 767px){
  .ds-list-selall{ display: flex; align-items: center; gap: var(--sp-2); min-height: var(--ctl-h); padding: 0 var(--sp-3);
    font-size: var(--fs-base); color: var(--text); cursor: pointer; }
  .ds-list-selall input{ width: var(--sp-5); height: var(--sp-5); margin: 0; }
  /* A row become a card (mobilecards.js): each cell takes its content's height. The desk's
     fixed row height cut a cell's second line off under the next one — a label stacked above
     its value, a salesperson on two lines, an empty-state sentence, a status picker
     (2026-10-27: Website → Categories, Sales, Purchase, Tasks). The New shell's classic list
     (.lst) has its own row height: shell.css lifts it the same way. */
  .m-cards > :is(tbody, tfoot) > tr > td{ height: auto; }
}
.ds-pane-note{ margin: 0 0 var(--sp-3); color: var(--text-muted); font-size: var(--fs-sm); }
.ds-field[hidden]{ display: none; }
.ds-lines-adds{ display: flex; flex-wrap: wrap; gap: var(--sp-5); margin-top: var(--sp-2); }
.ds-lines-adds > button{ border: 0; background: none; padding: var(--sp-1) 0; color: var(--accent-text); font: inherit; cursor: pointer; }
.ds-lines-adds > button:hover{ text-decoration: underline; }
.ds-totals-row{ display: flex; justify-content: flex-end; margin-top: var(--sp-5); }

/* ---- tabs (a record's notebook) ---- */
.ds-tabs{ display: flex; gap: var(--sp-1); border-bottom: 1px solid var(--border); margin: var(--sp-5) 0 var(--sp-3); }
.ds-tabs > a, .ds-tabs > button{ display: inline-flex; align-items: center; height: var(--tab-h); padding: 0 var(--tab-pad-x);
  border: 0; border-bottom: 2px solid transparent; background: transparent; color: var(--text); cursor: pointer;
  font: var(--fw-regular) var(--fs-base)/1 var(--font-body); text-decoration: none; margin-bottom: var(--edge-overlap); }
.ds-tabs > :is(a, button):hover{ background: var(--surface-hover); }
.ds-tabs > .on, .ds-tabs > [aria-selected="true"]{ border-bottom-color: var(--accent); font-weight: var(--fw-bold); }
/* a long record keeps its notebook in reach: the tabs pin under the top band (bar + line + the
   control row and its one-pixel edge) while the reader scrolls. Desktop only: on a phone the control row wraps. */
.ds-tabs-pinned{ position: sticky; top: calc(var(--bar-h) + var(--bar-line-h) + var(--search-h) + 2 * var(--sp-3) - var(--edge-overlap));
  z-index: var(--z-bar); background: var(--surface-1); }
@media (max-width: 767px){ .ds-tabs-pinned{ position: static; } }

/* ---- notices and empty states ---- */
/* a notice is a sentence; a row only when it holds blocks */
.ds-notice{ display: block; padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--tint-info-line); border-radius: var(--radius-menu);
  background: var(--tint-info-bg); color: var(--text); font-size: var(--fs-base); }
.ds-notice:has(> :is(div, p, form, ul, ol, section, table)){ display: flex; align-items: flex-start; gap: var(--sp-2); }
.ds-notice:not(:has(> :is(div, p, form, ul, ol, section, table))) > .ds-ic:first-child{ margin-right: var(--sp-2); vertical-align: text-bottom; }
.ds-notice.tone-warning{ border-color: var(--tint-warning-line); background: var(--tint-warning-bg); }
.ds-notice.tone-danger{ border-color: var(--tint-danger-line); background: var(--tint-danger-bg); }
.ds-notice.tone-success{ border-color: var(--tint-success-line); background: var(--tint-success-bg); }
.ds-notice-link{ color: inherit; font-weight: var(--fw-bold); text-decoration: underline; }
.ds-empty{ padding: var(--sp-6) var(--sp-4); text-align: center; color: var(--text-muted); font-size: var(--fs-base); }
/* what to do about it: a sentence kept readable, then its buttons */
.ds-empty-text{ max-width: var(--modal-md); }
.ds-empty-acts{ display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-1); }

/* ---- avatars (initials) ---- */
.ds-avatar{ display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--avatar-bar); height: var(--avatar-bar); border-radius: var(--pill-radius);
  background: var(--green); color: var(--brand-dark); font: var(--fw-bold) var(--fs-xs) var(--font-body); }

/* ---- the chatter (Adnan, 12 October: flat, no boxes) — every record's notes and history ----
   A row of buttons (one primary) with search, files and followers at its right, which stays at
   the top while the history scrolls; a panel opens under it only while asked for; then date
   separators and entries — avatar, name, time, what kind, the text. No card, no panel, no edge. */
.ds-chatter{ color: var(--text); padding: 0 var(--sp-4) var(--chatter-pad-end) 0; min-width: 0; }
.ds-chatter-bar{ position: sticky; top: 0; z-index: var(--z-bar); display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2); padding: var(--sp-2) 0; background: var(--surface-app); }
.ds-chatter-end{ margin-left: auto; display: flex; align-items: center; gap: var(--sp-1); }
.ds-chatter-tool{ display: inline-flex; align-items: center; gap: var(--sp-1); height: var(--ctl-h); padding: 0 var(--sp-2);
  border: 0; border-radius: var(--ctl-radius); background: none; color: var(--text-muted); font: inherit;
  font-size: var(--fs-sm); cursor: pointer; text-decoration: none; }
.ds-chatter-tool:hover, .ds-chatter-tool[aria-expanded="true"]{ background: var(--surface-hover); color: var(--text); }
.ds-chatter-panel{ padding: var(--sp-2) 0 var(--sp-3); }
.ds-chatter-panel[hidden]{ display: none; }
.ds-chatter-panel textarea{ width: 100%; min-height: var(--chatter-compose-h); resize: vertical; }
.ds-chatter-row{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2); }
.ds-chatter-note{ color: var(--text-muted); font-size: var(--fs-sm); margin: 0 0 var(--sp-2); }
.ds-chatter-sep{ display: flex; align-items: center; gap: var(--sp-3); margin: var(--sp-4) 0 var(--sp-2);
  color: var(--text-muted); font-size: var(--fs-sm); font-weight: var(--fw-bold); }
.ds-chatter-sep::before, .ds-chatter-sep::after{ content: ''; flex: 1; border-top: 1px solid var(--border); }
.ds-chatter-entry, .ds-chatter-act{ display: grid; grid-template-columns: var(--chatter-avatar) minmax(0, 1fr);
  column-gap: var(--sp-4); padding: var(--sp-2) 0; }
.ds-chatter-act{ grid-template-columns: var(--chatter-avatar) minmax(0, 1fr) auto; align-items: center; }
.ds-chatter-entry[hidden]{ display: none; }
.ds-chatter-entry > .ds-avatar, .ds-chatter-act > .ds-avatar{ width: var(--chatter-avatar); height: var(--chatter-avatar); }
.ds-chatter-head{ display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--sp-2); }
.ds-chatter-who{ font-weight: var(--fw-bold); }
.ds-chatter-when, .ds-chatter-kind{ color: var(--text-muted); font-size: var(--fs-sm); }
.ds-chatter-text{ margin-top: var(--sp-1); line-height: var(--lh-base); overflow-wrap: anywhere; }
.ds-chatter-text img{ max-width: 100%; height: auto; }
.ds-chatter-file{ display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) 0; color: var(--text); text-decoration: none; }
.ds-chatter-file:hover .ds-chatter-fname{ text-decoration: underline; }
.ds-chatter-fname{ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-chatter-fmeta{ margin-left: auto; color: var(--text-muted); font-size: var(--fs-sm); white-space: nowrap; }
.ds-chatter-follower{ display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) 0; }
.ds-chatter-follower > .ds-avatar{ width: var(--chatter-avatar-sm); height: var(--chatter-avatar-sm); }
.ds-chatter-empty{ color: var(--text-muted); padding: var(--sp-3) 0; }
.ds-chatter-track{ margin-top: var(--sp-1); }
.ds-chatter-thumb{ display: block; max-width: 100%; max-height: var(--chatter-thumb-h); border-radius: var(--radius-menu); }
/* a list of files handed in on a record, each with its upload stamp (Tasks, 2026-10-26): the stamp is the
   proof, so it reads in the page's ink; a file taken off keeps its line, struck through */
.ds-chatter-flist{ margin-top: var(--sp-1); }
.ds-chatter-fitem{ padding: var(--sp-2) 0; border-bottom: 1px solid var(--border); }
.ds-chatter-fitem:last-child{ border-bottom: 0; }
.ds-chatter-fitem .ds-chatter-thumb{ margin-top: var(--sp-1); }
.ds-chatter-fitem .ds-chatter-fname{ white-space: normal; overflow-wrap: anywhere; }
.ds-chatter-stamp{ font-size: var(--fs-sm); line-height: var(--lh-base); overflow-wrap: anywhere; }
.ds-chatter-file.is-removed .ds-chatter-fname{ text-decoration: line-through; }
.ds-chatter-sub{ margin: var(--sp-3) 0 var(--sp-1); font-weight: var(--fw-bold); }
.ds-chatter-rm{ margin-top: var(--sp-1); }
.ds-chatter-rm > summary{ cursor: pointer; display: inline-flex; }
.ds-chatter-rm form{ margin-top: var(--sp-2); }
.ds-chatter-text.is-clamped{ display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; }

/* ---- the dialog (board A_P_modal) ---- */
.ds-dialog-overlay{ position: fixed; inset: 0; z-index: var(--z-modal); display: flex; align-items: center; justify-content: center;
  padding: var(--gutter); background: var(--overlay); }
.ds-dialog-overlay[hidden]{ display: none; }
.ds-dialog{ width: min(var(--modal-md), 100%); max-height: calc(100vh - 2 * var(--gutter)); display: flex; flex-direction: column;
  background: var(--surface-3); color: var(--text); border-radius: var(--radius-modal); box-shadow: var(--shadow-elev); }
.ds-dialog-head{ display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border); }
.ds-dialog-head h2{ margin: 0; flex: 1; font: var(--fw-medium) var(--fs-lg)/1.3 var(--font-head); }
.ds-dialog-body{ padding: var(--sp-5); overflow: auto; }
.ds-dialog-foot{ display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--border); }
/* the same dialog as a native <dialog> opened with showModal() (the automatic sign-out's warning,
   templates/core/_idle_signout.html): the browser puts it on top and centres it; closed stays closed,
   which display:flex above would otherwise undo */
dialog.ds-dialog{ padding: 0; border: 0; margin: auto; }
dialog.ds-dialog:not([open]){ display: none; }
dialog.ds-dialog::backdrop{ background: var(--overlay); }

/* document lines: add ds-lines beside sol-table; sol-/d-/ls-/p-* stay the module's. */
.ds-lines{ width: 100%; border-collapse: collapse; font-size: var(--fs-base); color: var(--text); }
.ds-lines th{ height: var(--th-h); padding: 0 var(--sp-2); text-align: left; white-space: nowrap; font: var(--fw-bold) var(--fs-base)/1.2 var(--font-body); color: var(--text); border-bottom: 1px solid var(--border-strong); }
.ds-lines td{ padding: var(--sp-1) var(--sp-2); border-bottom: 1px solid var(--border); vertical-align: middle; }
.ds-lines tbody tr:last-child td{ border-bottom: 0; }
.ds-lines tbody tr:hover td{ background: var(--surface-hover); }
.ds-lines :is(th, td):is(.r, .sol-num, .sol-tools, .sol-x), .ds-lines .ls-tools{ text-align: right; }
.ds-lines th.sol-tools, .ds-lines .ls-tools{ white-space: nowrap; }
.ds-lines .erp-input{ font: inherit; border-radius: var(--radius-facet); }
.ds-lines tbody td :is(.erp-input, input:not([type=checkbox]):not([type=radio]), select, textarea):not([readonly]):not(:disabled){
  border-width: 0 0 1px; border-style: solid; border-color: var(--border-control); border-radius: 0;
  background: transparent; box-shadow: none; }
.ds-lines tbody td :is(.erp-input, input, select, textarea):hover{ background: var(--surface-hover); }
.ds-lines tbody td :is(.erp-input, input, select, textarea):focus{ border-width: 1px; border-color: var(--border-selected);
  border-radius: var(--radius-facet); background: var(--input-bg); box-shadow: var(--focus-ring); }
.ds-lines .erp-input:focus{ border-color: var(--border-selected); box-shadow: var(--focus-ring); }
/* a figure typed in a line table lines up on the right like the figures printed beside it (CLAUDE.md
   rule 10: money and numbers are right-aligned) - any numeric box, not only the quantity classes */
.ds-lines input:is(.p-qty, .o-qty, [inputmode="decimal"], [inputmode="numeric"]){ text-align: right; }
.ds-lines .sol-locked, .ds-lines .erp-input:is([readonly], :disabled){ background: var(--surface-2); color: var(--text-disabled); cursor: not-allowed; }
.ds-lines :is(.sol-section, .d-section) td{ background: var(--tint-accent-bg); font: var(--fw-bold) var(--fs-base)/1.2 var(--font-body); color: var(--text); }
.ds-lines :is(.sol-note, .d-note) td{ font-style: italic; color: var(--text); }
.ds-lines td:is(.sol-num, .r){ font-variant-numeric: tabular-nums; }
.ds-lines .p-sub{ font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; }
.ds-lines.view td{ color: var(--text); }
.ds-lines + .sol-actions{ margin-top: var(--sp-2); }
.ds-lines + .sol-actions .sol-add{ height: var(--ctl-h); padding: 0 var(--sp-4); border: 1px dashed var(--border-control); border-radius: var(--ctl-radius); background: var(--tint-accent-bg); color: var(--accent-text); font: var(--fw-bold) var(--fs-base)/1 var(--font-body); cursor: pointer; }
.ds-lines + .sol-actions .sol-add:hover{ background: var(--surface-hover); }
.ds-lines + .sol-actions .sol-add.ghost{ background: var(--surface-1); border-color: var(--border-strong); color: var(--text-muted); }
/* a configured item's components: rows of the item's own line table, each cell under the item's
   column — part number, description, quantity (sales/_line_configuration_rows.html, 2026-10-27);
   the accent bar on the first cell marks them as the item's. */
.ds-lines tr.ds-lines-cfg > td{ padding-top: var(--sp-1); padding-bottom: var(--sp-1); vertical-align: top; font-size: var(--fs-sm); font-weight: var(--fw-regular); color: var(--text); }
.ds-lines tr.ds-lines-cfg > td:first-child{ box-shadow: inset 3px 0 0 var(--accent-text); }
.ds-lines tr.ds-lines-cfg > td.ds-lines-cfg-open{ border-bottom: 0; }
.ds-lines tr.ds-lines-cfg > td.ds-lines-cfg-qty{ text-align: right; white-space: nowrap; }
.ds-lines tr.ds-lines-cfg-head > td{ padding-bottom: 0; border-bottom: 0; font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--accent-text); }
.ds-lines-cfg-desc{ display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--sp-3); }
.ds-lines-cfg-desc.lbl{ grid-template-columns: 8rem minmax(0, 1fr); }
.ds-lines-cfg-desc.pn{ grid-template-columns: 7rem minmax(0, 1fr); }
.ds-lines-cfg-desc.lbl.pn{ grid-template-columns: 8rem 7rem minmax(0, 1fr); }
.ds-lines-cfg-label{ font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: .04em; text-transform: uppercase; color: var(--accent-text); overflow-wrap: anywhere; }
.ds-lines-cfg-part{ font-weight: var(--fw-medium); overflow-wrap: anywhere; }
.ds-lines-cfg-name{ overflow-wrap: anywhere; }
/* a Part # column as wide as its longest code (2026-10-27), so "4SS11AV#ABA" reads on one line.
   The table carries --n, that code's length in characters (the view counts it:
   sales configuration.part_column_chars), and <col class="ds-lines-code"> takes .68 of the small
   size a character (every part code measured needs at most .66) plus its cell's padding. The
   other columns keep their percentages, scaled to what is left — Chromium, Firefox and WebKit all
   serve a fixed column first. A code past the view's cap wraps, before its "#" suffix first. */
.ds-lines-fit{ --code-w: calc(var(--n) * .68 * var(--fs-sm) + var(--sp-5)); }
.ds-lines-fit > colgroup > col.ds-lines-code{ width: var(--code-w); }
/* on a phone the quotation's line table scrolls at 640px (static/css/responsive.css); with a fitted
   Part # column it grows by that column, so every other column keeps its phone width */
@media (max-width: 760px){ .ds-lines.ds-lines-fit.view{ min-width: calc(640px + var(--code-w)); } }
/* ...and there it sizes its columns to what they hold (2026-10-28): a figure that never breaks
   ("− cost EGP 297,542.00" in the Margin cell) widens its column, and the table scrolls further,
   where the fixed shares of 640px ran it under the next column. A wide screen keeps the page's
   fixed layout, every column on its share. */
@media (max-width: 760px){ .ds-lines.view.sol-table{ table-layout: auto; } }
/* a table that sizes itself (the quick view; the quotation page on a phone) keeps its Part #
   heading at least that wide, so a code reads on one line and the table scrolls sideways in its
   wrapper (.ds-list-scroll) instead; a code past the view's cap still breaks before its "#" */
.ds-lines-fit th.ds-lines-code{ width: var(--code-w); min-width: var(--code-w); }

/* totals: add ds-totals beside qf-totals/qv-totals; rows stay .tot-row/.tot-grand. */
.ds-totals{ display: flex; flex-direction: column; align-items: flex-end; gap: var(--sp-1); font-size: var(--fs-base); font-variant-numeric: tabular-nums; }
.ds-totals .tot-row{ display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-6); min-width: 240px; color: var(--text-muted); font: var(--fw-medium) var(--fs-base)/1.4 var(--font-body); }
.ds-totals .tot-row[hidden]{ display: none; }
.ds-totals .tot-row b{ color: var(--text); font-weight: var(--fw-regular); }
.ds-totals .tot-row.disc b{ color: var(--tint-danger-fg); }
.ds-totals .tot-ship{ align-items: center; }
.ds-totals :is(.tot-grand, .tot-row.grand){ display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-6); min-width: 240px; margin-top: var(--sp-1); padding-top: var(--sp-2); border-top: 1px solid var(--border-strong); font: var(--fw-bold) var(--fs-lg)/1.3 var(--font-head); color: var(--text); }
.ds-totals :is(.tot-grand, .tot-row.grand) b{ font-weight: var(--fw-bold); }

/* ---- a configured item's components (static/js/sales_configure.js) — one panel under its line on the
   quotation editor, the RFQ request form and the RFQ pricing grid. It is a muted strip holding a line editor;
   these are its own parts. .ds-cfg-tr sits under the line (a table row, or a block under a grid of divs:
   .ds-cfg-div), and stays hidden on a phone, where mobilecards.js gives every row display:block. ---- */
tr.ds-cfg-tr > td{ padding-top: 0; border-top: 0; }
tr.ds-cfg-tr[hidden]{ display: none; }
/* under a grid of divs, whose columns have a floor and which may scroll sideways: the panel has no floor,
   it is the width the reader can see wherever the grid is scrolled to */
.ds-cfg-div{ position: sticky; left: 0; width: 100cqw; box-sizing: border-box; padding: 0 var(--sp-2); }
.ds-cfg .cfg-table .cfg-l{ width: 1%; }
/* the component column is as wide as its longest label: a real sheet's are long ("Country of Origin
   Restrictions"), and a fixed column cut them off under the select's arrow */
.ds-cfg .cfg-table .c-label{ width: auto; min-width: max(140px, 100%); max-width: 340px; }
.ds-cfg .cfg-table .cfg-p{ width: 170px; }
.ds-cfg .cfg-table .cfg-q{ width: 70px; }
.ds-cfg .cfg-table .cfg-x{ width: 88px; white-space: nowrap; }
.ds-cfg .c-item{ padding: var(--sp-1) var(--sp-2); border-radius: var(--radius-facet); cursor: pointer; }
.ds-cfg .c-item:hover{ text-decoration: underline; }

/* ---- icons: the one set (apps/core/icons.py), the ink of whatever holds them ---- */
.ds-ic{ vertical-align: -0.18em; flex: none; }
/* An app icon whose theme follows the computer: a <picture> holding the light and the dark drawing
   (templates/core/_app_icon.html, apps/core/appicons.py). It takes no box, so the <img> inside stays
   the box every page already sizes and lays out. */
picture.ds-ic-app{ display: contents; }

@media (max-width: 767px){
  /* on a phone each component is a card, and its cells are as wide as the card */
  .ds-cfg .cfg-table :is(.cfg-l, .cfg-p, .cfg-q, .cfg-x){ width: auto; }
  .ds-cfg .cfg-table .c-label{ width: 100%; max-width: none; }
  .ds-fields{ grid-template-columns: minmax(0, 1fr); }
  .ds-field{ grid-template-columns: minmax(0, 1fr); gap: 0; }
  .ds-field > .ds-btn{ justify-self: start; }
  .ds-tabs:not(.ovf-ready){ flex-wrap: wrap; }
  .ds-cp-title h1{ font-size: var(--fs-lg); }
}

/* .rec-lines-wide: a line editor's chatter stays under it (Classic; New's shell.css outranks this) */
.rec-wrap.rec-lines-wide{ flex-direction: column; align-items: stretch; }
.rec-lines-wide > .chatter{ flex: none; width: auto; position: static; max-height: none; overflow: visible;
  border-left: 0; padding-left: 0; border-top: 1px solid var(--border); padding-top: var(--sp-3); }
/* optional columns: the ⇄ menu ending a list's header row; a column off is absent, card included (CORE §6.aas) */
.ds-cols-th{ text-align: end; white-space: nowrap; }
/* the ⇄ cell closes the header row and no row draws a cell under it: a filler carries the row's line to the end */
@media (min-width: 768px){
  table:has(> thead .ds-cols-th) > tbody > tr:not(:has(> td[colspan]))::after{ content: ""; display: table-cell; border-bottom: 1px solid var(--border); }
}
.ds-cols-btn{ gap: var(--sp-1); padding: 0 var(--sp-2); }
.ds-cols-opt{ gap: var(--sp-2); }
.ds-cols-note{ margin: 0; padding: var(--sp-1) var(--sp-3); color: var(--text-muted); font-size: var(--fs-sm); }
:is(th, td)[data-col][hidden]{ display: none; }
@media (max-width: 767px){
  .m-cards > thead:has(.ds-cols){ display: block; }
  /* a cell written as lines (<br>): in flex its parts would stand side by side and run off the screen,
     cut by the page's overflow. Its label stays on the left, its lines stack on the right. */
  /* a card has no column to widen: a long chip wraps inside it (a split's names ran off the screen) */
  /* a phone has no hover to read the rest of a tile's label */
  .ds-kpi .ds-kpi-l, .ds-kpi .ds-kpi-v{ white-space: normal; overflow: visible; }
  table.m-cards > :is(tbody, tfoot) > tr > td > * { min-width: 0; }
  table.m-cards .ds-chip{ flex: 0 1 auto; max-width: 100%; white-space: normal; }
  table.m-cards > :is(tbody, tfoot) > tr > td:has(> br){ display:block; }
  table.m-cards > :is(tbody, tfoot) > tr > td:has(> br)::before{ float:left; width:40%; }
  table.m-cards > :is(tbody, tfoot) > tr > td:has(> br)::after{ content:''; display:block; clear:both; }
  .m-cards > thead:has(.ds-cols) > tr{ display: flex; justify-content: flex-end; }
  .m-cards > thead:has(.ds-cols) > tr > th:not(.ds-cols-th){ display: none; }
}
/* the activity bar: late / today / future as one segmented bar and its figures (CORE §6.aax) */
.ds-actbar{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2); margin: 0 0 var(--sp-2); }
.ds-actbar-track{ display: flex; flex: 1 1 var(--sp-6); min-width: var(--sp-6); height: var(--sp-1); border-radius: var(--pill-radius);
  background: var(--border); overflow: hidden; }
.ds-actbar-seg{ display: block; height: 100%; width: var(--pct); }
.ds-actbar-seg.tone-danger{ background: var(--tint-danger-fg); }
.ds-actbar-seg.tone-warning{ background: var(--tint-warning-fg); }
.ds-actbar-seg.tone-success{ background: var(--tint-success-fg); }
.ds-actbar-total{ flex: none; font-size: var(--fs-sm); color: var(--text); font-variant-numeric: tabular-nums; }
/* the activity view: records x activity types (CORE §6.aax) */
.ds-actview th, .ds-actview td{ vertical-align: top; }
.ds-actview-type{ min-width: calc(var(--sp-6) * 4); }
.ds-actview-type .ds-actbar{ margin: var(--sp-1) 0 0; }
.ds-actview-name{ display: block; white-space: nowrap; }
.ds-actview-none{ display: block; margin-top: var(--sp-1); font-size: var(--fs-sm); color: var(--text-muted); font-weight: var(--fw-regular); }
.ds-actview-record{ text-align: start; font-weight: var(--fw-regular); }
.ds-actview-sub{ display: block; margin-top: var(--sp-1); font-size: var(--fs-sm); color: var(--text); }
.ds-actview-cell{ white-space: nowrap; }
.ds-actcell{ display: flex; flex-direction: column; gap: var(--sp-2); }
.ds-actcell-head{ margin: 0; }
.ds-actcell-form{ display: flex; flex-direction: column; gap: var(--sp-2); padding-top: var(--sp-2); border-top: 1px solid var(--border); }
/* a strip read in full (a report): wider figures, and its labels and sub-lines wrap, never cut */
.ds-kpis.is-wrapped{grid-template-columns:repeat(auto-fit,minmax(min(280px,45%),1fr))}
.ds-kpis.is-wrapped .ds-kpi-l,.ds-kpis.is-wrapped .ds-kpi-v{white-space:normal;overflow:visible}
/* a report table whose cells print their currency: the code and its number stay on one line */
.ds-report-table.is-money td.num{white-space:nowrap}
.ds-report-table.is-money th,.ds-report-table.is-money td{padding-inline:var(--sp-2)}
/* a report table with every column a reader asked for beside each other: closer, so they are all on the screen at once */
.ds-report-table.is-money.is-tight th,.ds-report-table.is-money.is-tight td{padding-inline:var(--sp-1)}
/* a picture in a box of a fixed shape: the box holds its shape (--w / --h, numbers, default 16 / 9), the
   picture is cropped to fill it. .is-sized: --w is also its width in px. .is-logo: a record's square, the
   whole logo on the paper white it was drawn for. No display on the picture: it may carry [hidden]. */
.ds-picture{ display: flex; align-items: center; justify-content: center; overflow: hidden; aspect-ratio: var(--w, 16) / var(--h, 9);
  background: var(--tint-neutral-bg); color: var(--tint-neutral-fg); }
.ds-picture > :is(img, svg){ width: 100%; height: 100%; object-fit: cover; }
.ds-picture.is-sized{ flex: none; width: calc(var(--w) * 1px); }
.ds-picture.is-logo{ flex: none; width: var(--icon-home); aspect-ratio: 1; border: 1px solid var(--tint-neutral-line); border-radius: var(--radius-card); }
.ds-picture.is-logo > img{ object-fit: contain; background: var(--surface-keep); }
label.ds-picture{ cursor: pointer; }
/* a picture with places marked on it: the scroll box, the page (the spots are placed against it, so a page
   taller than the window keeps them on their words), a spot per place from its data, --x --y --w --h in % */
.ds-imagemap{ overflow: auto; max-height: 76vh; }
.ds-imagemap-page{ position: relative; }
.ds-imagemap-page > img{ display: block; width: 100%; height: auto; }
.ds-imagemap-spot{ position: absolute; left: var(--x); top: var(--y); width: var(--w); height: var(--h); border: 2px solid var(--tint-warning-fg); }
.ds-imagemap-spot.tone-accent{ border-color: var(--tint-accent-fg); }
.ds-imagemap-spot.tone-danger{ border-color: var(--tint-danger-fg); }
.ds-imagemap-spot > .ds-chip{ position: absolute; left: 0; bottom: 100%; }
/* work in progress; a reader who asked for no motion sees it still (--motion-base is 0 for them) */
@keyframes ds-spin{ to{ transform: rotate(360deg); } }
.ds-spinner{ display: inline-block; flex: none; box-sizing: border-box; width: 1em; height: 1em; vertical-align: -.125em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: var(--pill-radius);
  animation: ds-spin calc(var(--motion-base) * 4) linear infinite; }
/* a fieldset that only groups (a whole form's disabled, a legend for a reader's screen): no box of its own */
.ds-fieldset{ min-width: 0; margin: 0; padding: 0; border: 0; }
/* a field with its button at the end of the same row (Copy, Check, Send a code) */
.ds-field-with{ display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.ds-field-with > :is(input, select, textarea){ flex: 1 1 auto; width: auto; min-width: 0; }
.ds-field-with > :is(button, a, label){ flex: none; }
/* a row this reader cannot act on, with a title saying why: grey means blocked (CLAUDE.md rule 11) */
.ds-list tbody tr.is-disabled > td{ color: var(--text-disabled); }
/* two more dialogs: .is-wide for a list that needs the room; .is-floating has no backdrop and leaves the
   page in use, at the bottom corner above the page's menus */
.ds-dialog.is-wide{ width: min(var(--modal-lg), 100%); }
.ds-dialog.is-floating{ position: fixed; right: var(--gutter); bottom: var(--gutter); z-index: var(--z-menu);
  width: min(var(--modal-sm), calc(100% - 2 * var(--gutter))); }
/* .is-picker chooses one from sections of pictured choices: a rail of sections, a grid of cards, each
   card showing a live miniature. The miniature keeps its own white ground in Dark: it shows the page
   as visitors will see it, not the ERP. The frame is laid out as a desktop page; the page's script scales it. */
.ds-dialog.is-picker{ width: min(1120px, 100%); }
.ds-dialog-pick{ display: flex; flex: 1; min-height: 0; overflow: hidden; }
.ds-dialog-pick[hidden]{ display: none; }
.ds-dialog-rail{ flex: 0 0 var(--menu-min); display: flex; flex-direction: column; flex-wrap: nowrap; gap: var(--sp-1); padding: var(--sp-3); overflow-y: auto; border-inline-end: 1px solid var(--border); }
.ds-dialog-rail-field{ display: flex; flex-direction: column; gap: var(--sp-1); padding-bottom: var(--sp-2); margin-bottom: var(--sp-2); border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.ds-dialog-tab{ appearance: none; border: 0; background: transparent; color: var(--text); text-align: start; font: inherit; font-size: var(--fs-sm); padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-menu); cursor: pointer; }
.ds-dialog-tab:hover{ background: var(--surface-hover); }
.ds-dialog-tab[aria-selected="true"]{ background: var(--surface-selected); font-weight: var(--fw-medium); }
.ds-dialog-tab:focus-visible, .ds-dialog-card:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.ds-dialog-panes{ flex: 1; min-width: 0; overflow-y: auto; padding: var(--sp-4); }
.ds-dialog-grid{ display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4); align-items: start; }
.ds-dialog-grid[hidden]{ display: none; }
.ds-dialog-card{ appearance: none; display: flex; flex-direction: column; min-width: 0; padding: 0; background: var(--surface-1); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-card); overflow: hidden; cursor: pointer; font: inherit; text-align: start; }
.ds-dialog-card:hover{ border-color: var(--accent); }
.ds-dialog-shot{ position: relative; display: block; width: 100%; aspect-ratio: 4 / 3; overflow: hidden; background: var(--surface-keep); }
.ds-dialog-shot[style*="height"]{ aspect-ratio: auto; }
.ds-dialog-shot.is-blank{ display: flex; align-items: center; justify-content: center; background: var(--surface-2); color: var(--text); font-size: var(--fs-sm); }
.ds-dialog-frame{ position: absolute; top: 0; left: 0; width: 1280px; height: 900px; border: 0; transform: scale(.2); transform-origin: 0 0; pointer-events: none; }
.ds-dialog-card-name{ display: block; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); font-weight: var(--fw-medium); border-top: 1px solid var(--border); }
@media (max-width: 767px){
  .ds-dialog-pick{ flex-direction: column; overflow-y: auto; }
  .ds-dialog-rail{ flex: none; flex-direction: row; flex-wrap: wrap; border-inline-end: 0; border-bottom: 1px solid var(--border); overflow: visible; }
  .ds-dialog-rail-field{ flex-basis: 100%; }
  .ds-dialog-panes{ overflow: visible; padding: var(--sp-3); }
  .ds-dialog-grid{ grid-template-columns: repeat(auto-fill, minmax(var(--menu-min), 1fr)); gap: var(--sp-2); }
}
/* hidden means hidden: a component's own display beat [hidden] (12 sites, 13 Oct 2026) */
[hidden]:is([class^="ds-"], [class*=" ds-"]){ display: none; }

/* ---- the document: the browser's own controls follow the theme, and a Save being sent says so
   (static/js/submitonce.js marks the form; on every page document, from templates/_theme_head.html) ---- */
html:is(.dark){ color-scheme: dark; }
html:not(.dark){ color-scheme: light; }
form:is(.is-submitting) :is(.is-busy){ cursor: progress; opacity: .7; }
form:is(.is-submitting) :is(.is-pressed){ position: relative; }
form:is(.is-submitting) :is(.is-pressed)::after{
  content: ''; display: inline-block; width: .8em; height: .8em; margin-left: var(--sp-1); vertical-align: -.1em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: var(--pill-radius);
  animation: ds-spin .7s linear infinite;
}
@media (prefers-reduced-motion: reduce){
  form:is(.is-submitting) :is(.is-pressed)::after{ animation: none; }
}

/* ---- the code card: a small page of its own asking for a one-time code (two-factor sign-in) ---- */
.ds-codecard-page{
  display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; padding: var(--sp-5);
  background: var(--surface-2); color: var(--text); font-family: var(--font-body);
}
.ds-codecard-page, .ds-codecard-page *{ box-sizing: border-box; }
.ds-codecard{
  width: min(440px, 100%); padding: var(--sp-6) var(--sp-5); border: 1px solid var(--border); border-radius: var(--radius-card);
  background: var(--surface-1); box-shadow: var(--shadow-elev);
}
.ds-codecard h1{ margin: 0 0 var(--sp-1); font: var(--fw-bold) var(--fs-title)/1.3 var(--font-head); }
.ds-codecard p{ color: var(--text-muted); font-size: var(--fs-base); line-height: var(--lh-base); }
.ds-codecard .ds-btn{ width: 100%; }
.ds-codecard-qr{ display: flex; justify-content: center; margin: var(--sp-4) 0; }
/* a camera reads it: dark modules on a light quiet zone in every theme */
.ds-codecard-qr svg{
  width: 190px; height: 190px; padding: var(--sp-3); border-radius: var(--radius-menu); box-sizing: content-box;
  background: var(--surface-keep); color-scheme: light;
}
.ds-codecard-secret{
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-menu); background: var(--surface-2);
  font-family: var(--font-mono); font-size: var(--fs-sm); text-align: center; letter-spacing: 1px; word-break: break-all;
}
.ds-codecard-input{
  width: 100%; margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4); border: 1px solid var(--border-control); border-radius: var(--radius-menu);
  background: var(--input-bg); color: var(--text); font-size: var(--fs-lg); letter-spacing: 4px; text-align: center;
}
.ds-codecard-input:focus{ outline: none; border-color: var(--border-selected); box-shadow: var(--focus-ring); }
.ds-codecard-check{ display: flex; align-items: center; gap: var(--sp-2); margin: var(--sp-2) 0; color: var(--text-muted); font-size: var(--fs-sm); }
.ds-codecard-altform{ margin-top: var(--sp-2); }
.ds-codecard-link{ display: block; margin-top: var(--sp-4); color: var(--accent-text); font-size: var(--fs-sm); text-align: center; text-decoration: none; }
.ds-codecard-link.is-button{ width: 100%; border: 0; background: none; cursor: pointer; font: inherit; }
.ds-codecard-meta{ margin-top: var(--sp-2); color: var(--text-muted); font-size: var(--fs-xs); }
.ds-codecard-err{
  margin-bottom: var(--sp-3); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--tint-danger-line); border-radius: var(--radius-menu);
  background: var(--tint-danger-bg); color: var(--tint-danger-fg); font-size: var(--fs-sm);
}
.ds-codecard-codes{ display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); margin: var(--sp-4) 0; }
.ds-codecard-codes code{ padding: var(--sp-2); border-radius: var(--radius-menu); background: var(--surface-2); font-size: var(--fs-base); text-align: center; letter-spacing: 1px; }

/* ---- the tag field: chips and a type-to-add box, its list, the manage dialog's rows
   (templates/core/_tags_field.html; the chips are .ds-chip.tag) ---- */
.ds-tags-field > :first-child{ display: block; }
.ds-tags{ position: relative; }
.ds-tags-box{
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1); min-height: var(--ctl-h); padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--border-control); border-radius: var(--radius-menu); background: var(--surface-1); cursor: text;
}
.ds-tags-box:focus-within{ border-color: var(--border-selected); box-shadow: var(--focus-ring); }
.ds-tags-box > input[type=text]{ flex: 1; min-width: 90px; padding: var(--sp-1); border: 0; outline: 0; background: transparent; font-size: var(--fs-base); }
.ds-tags-x{ cursor: pointer; font-weight: var(--fw-bold); }
.ds-tags-pop{
  position: absolute; left: 0; right: 0; top: 100%; z-index: var(--z-modal); max-height: 230px; margin-top: var(--sp-1); overflow: auto;
  border: 1px solid var(--border-strong); border-radius: var(--radius-menu); background: var(--surface-3); box-shadow: var(--shadow-elev);
}
.ds-tags-pop[hidden]{ display: none; }
.ds-tags-pop.is-floating{ position: fixed; left: 0; top: 0; right: auto; margin-top: 0; }
.ds-tags-opt{ display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); cursor: pointer; }
.ds-tags-opt:hover, .ds-tags-opt.on{ background: var(--surface-hover); }
.ds-tags-opt.create{ color: var(--tint-accent-fg); font-weight: var(--fw-bold); }
.ds-tags-dot{ flex: none; width: 11px; height: 11px; border-radius: var(--pill-radius); background: var(--tag-colour, var(--tint-neutral-fg));; outline: 1px solid var(--border-strong); }
.ds-tags-manage{ display: inline-block; margin-top: var(--sp-1); color: var(--accent-text); font-size: var(--fs-xs); text-decoration: none; }
.ds-tags-add{ display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border); }
.ds-tags-row{ display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) 0; border-bottom: 1px solid var(--border); }
.ds-tags-row input[type=text]{ flex: 1; }
:is(.ds-tags-row, .ds-tags-add) input[type=color]{ width: 30px; height: 28px; padding: 0; border: 1px solid var(--border-strong); border-radius: var(--radius-facet); background: none; cursor: pointer; }

/* ---- small additions to shared parts ---- */
/* a list's person cell: the name, then the sign-in name and the e-mail a step smaller */
.ds-list-person > div{ font-size: var(--fs-sm); }
.ds-list-person > div > span{ font-size: var(--fs-xs); }
/* a list cell of small one-button forms */
.ds-list-actions form{ display: inline-flex; align-items: center; gap: var(--sp-1); margin: 0 var(--sp-2) var(--sp-1) 0; vertical-align: middle; }
.ds-list-actions input[type=number]{ width: 70px; }
.ds-pane-note code{ padding: 0 var(--sp-1); border-radius: var(--radius-facet); background: var(--surface-2); color: var(--text); }
.ds-pane-note.is-tight{ margin: 0; font-size: var(--fs-xs); }
.ds-pane-note.is-reading{ color: var(--text); }
.ds-pager-total{ margin-left: var(--sp-2); color: var(--text-muted); font-weight: var(--fw-medium); }
.ds-chip-row{ display: inline-flex; flex-wrap: wrap; gap: var(--sp-1); vertical-align: middle; }
.ds-chip-row.sm .ds-chip{ padding: 0 var(--sp-1); font-size: var(--fs-xs); }
.ds-strip-list.is-narrow{ max-width: var(--modal-lg); margin-inline: auto; }
.ds-strip.is-plain{ border: 1px solid var(--border); background: var(--surface-1); }
.ds-strip-mark{ font-size: var(--fs-lg); }
.ds-menu-panel.is-inline{ position: static; }
.ds-field-error{ margin-top: var(--sp-1); color: var(--tint-danger-fg); font-size: var(--fs-xs); }
/* panels' inner rows, and the samples of the Component gallery (Settings, Technical) */
.ds-panel-stack{ display: flex; flex-direction: column; gap: var(--sp-4); }
.ds-panel-row > .ds-menu{ position: relative; }
.ds-panel-row{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin: var(--sp-2) 0; }
.ds-panel-inset{ padding: var(--sp-3); border: 1px solid var(--border); border-radius: var(--radius-menu); background: var(--surface-2); }
.ds-panel-code{ margin: 0; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--border); border-radius: var(--radius-menu); background: var(--surface-2); color: var(--text); font: var(--fs-sm)/1.5 var(--font-mono); overflow: auto; }
.ds-panel-blocked{ color: var(--text-disabled); }
.ds-panel-tip{ display: inline-block; padding: var(--sp-1) var(--sp-2); border-radius: var(--radius-facet); background: var(--bar-bg); color: var(--bar-ink); font-size: var(--fs-xs); }
.ds-panel-skeleton{ width: var(--w, 100%); height: 14px; margin: var(--sp-1) 0; border-radius: var(--radius-facet); }
.ds-panel-chart{ height: 220px; }

/* ---- a segmented choice: radio buttons reading as one control (the permission levels; each
   option's own class says which tint it takes when chosen) ---- */
.ds-seg{ display: inline-flex; border: 1px solid var(--border-control); border-radius: var(--ctl-radius); background: var(--surface-1); overflow: hidden; }
.ds-seg > label{ position: relative; margin: 0; cursor: pointer; }
.ds-seg > label > input{ position: absolute; width: 0; height: 0; opacity: 0; }
.ds-seg > label > span{ display: block; padding: var(--sp-1) var(--sp-3); border-left: 1px solid var(--border-control); color: var(--text); font-size: var(--fs-sm); white-space: nowrap; }
.ds-seg > label:first-child > span{ border-left: 0; }
.ds-seg > label > input:focus-visible + span{ box-shadow: var(--focus-ring); }
.ds-seg > label > input:checked + span{ font-weight: var(--fw-bold); }
.ds-seg > :is(.pseg-none, .pseg-inherit) > input:checked + span{ background: var(--tint-muted-bg); color: var(--tint-muted-fg); }
.ds-seg > .pseg-view > input:checked + span{ background: var(--tint-info-bg); color: var(--tint-info-fg); }
.ds-seg > .pseg-add > input:checked + span{ background: var(--tint-warning-bg); color: var(--tint-warning-fg); }
.ds-seg > .pseg-edit > input:checked + span{ background: var(--accent); color: var(--on-accent); }
/* the level the role resolves to, ringed while the row is on Inherit */
.ds-seg > .pseg-ghost > span{ box-shadow: var(--focus-ring); }
.ds-seg.pseg-locked > label{ cursor: not-allowed; }
.ds-seg.pseg-locked > label > input:checked + span{ background: var(--surface-2); color: var(--text-disabled); }

/* ---- the promotional strip on Home: fill, ink, height and corners are the data its administrator
   chose (--tag-colour, --tone, --h, --n), never typed here ---- */
.ds-promo{ margin-top: var(--sp-4); }
.ds-promo .hb-banner{ min-height: var(--h); padding: var(--sp-3) var(--sp-5); border-radius: var(--n); background: var(--tag-colour); color: var(--tone); }
.ds-promo .hb-edged{ border: 1px solid var(--border); }
.ds-promo .hb-title{ font: var(--fw-bold) var(--fs-title)/1.2 var(--font-head); }
.ds-promo .hb-click{ cursor: pointer; }
.ds-promo :is(.hb-custom, .hb-imgwrap){ display: block; border-radius: var(--n); overflow: hidden; }
.ds-promo .hb-img{ display: block; width: 100%; height: auto; }
.ds-dialog .hb-bk-img{ max-width: 100%; height: auto; }
.ds-dialog .hb-bk-video{ width: 100%; height: auto; aspect-ratio: 16 / 9; }

/* ---- a row being dragged to a new place: it fades, a line shows where it lands ---- */
.ds-list tbody tr.dragging{ opacity: .45; }
.ds-list tbody tr.drop-above{ box-shadow: inset 0 2px 0 0 var(--accent); }
.ds-list tbody tr.drop-below{ box-shadow: inset 0 -2px 0 0 var(--accent); }

.ds-panel.is-sticky{ position: sticky; top: var(--sp-3); }
.ds-panel.is-narrow{ max-width: 480px; margin-inline: auto; }
.ds-notice.is-floating{ position: fixed; left: 50%; bottom: var(--sp-5); z-index: var(--z-modal); transform: translateX(-50%); box-shadow: var(--shadow-elev); }
