/* Screen/view layout (login, dashboard shell, manifest table, detail panel
   content, analytics, fabric composition) — extracted from
   41.01-pipeline.html (Task 2, frontend rebuild). */
  #app { display: none; }
  #app.ready { display: block; }
  #loginScreen {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(180deg, var(--surface-2), var(--bg) 55%);
  }
  #loginScreen.hidden { display: none; }
  .login-card { width: 360px; text-align: left; }
  .login-mark {
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.14em;
    color: var(--accent);
    text-transform: uppercase;
    margin-bottom: 10px;
  }
  .login-card h1 { font-size: 24px; margin: 0 0 4px; letter-spacing: -0.01em; }
  .login-card p.sub { margin: 0 0 26px; color: var(--ink-dim); font-size: 13.5px; }
  .name-list { display: flex; flex-direction: column; gap: 6px; }
  .name-row {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px;
    background: var(--surface); cursor: pointer;
    transition: border-color 0.12s ease, background 0.12s ease;
  }
  .name-row:hover, .name-row:focus-visible { border-color: var(--accent); background: var(--accent-wash); }
  .name-row .who { font-size: 14px; font-weight: 600; }
  .name-row .role { font-size: 12px; color: var(--ink-dim); margin-left: auto; }
  .login-empty, .login-error { font-size: 13px; color: var(--ink-dim); padding: 10px 0; }
  /* Passcode step (Auth Part B, Task 5) — ported from 41.02-login-preview.html's
     inline <style>, now the permanent login flow's markup, not a throwaway
     preview page's. `.hidden` itself is NOT duplicated here — that utility
     already exists globally at 42.01-tokens.css:67. */
  /* OTP-style PIN entry: 6 individual single-digit boxes, auto-advancing
     as you type (see wireOtpRow in 42.13-app.js) — not one text input. */
  .otp-row { display: flex; justify-content: center; gap: 8px; margin-bottom: 12px; }
  .otp-box {
    width: 42px; height: 52px; box-sizing: border-box;
    text-align: center; font-size: 22px; font-variant-numeric: tabular-nums;
    border: 1px solid var(--border); border-radius: 6px;
    background: var(--surface); color: var(--ink);
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
  }
  .otp-box:focus {
    outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-wash);
  }
  .login-back {
    background: none; border: none; color: var(--ink-dim); font-size: 13px;
    cursor: pointer; padding: 0; margin-bottom: 16px; display: block;
  }
  .login-submit {
    width: 100%; padding: 10px; border-radius: 6px; border: none;
    background: var(--accent); color: #fff; font-size: 14px; font-weight: 600;
    cursor: pointer;
  }
  .login-submit:disabled { opacity: 0.5; cursor: default; }
  .topbar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 16px 24px; border-bottom: 1px solid var(--border);
    background: var(--surface);
  }
  .brand { display: flex; align-items: baseline; gap: 10px; }
  .brand .mark { font-family: var(--mono); font-size: 12px; color: var(--accent); letter-spacing: 0.1em; }
  .brand h1 { font-size: 17px; margin: 0; letter-spacing: -0.005em; }
  .banner {
    margin: 0; padding: 10px 24px; font-size: 13px;
    background: var(--bad-wash); border-bottom: 1px solid var(--border);
  }
  .banner.hidden { display: none; }
  .kpi-strip { display: grid; grid-template-columns: repeat(5, 1fr); border-bottom: 1px solid var(--border); background: var(--surface); }
  .filter-bar {
    display: flex; align-items: center; gap: 10px; padding: 12px 24px;
    border-bottom: 1px solid var(--border); background: var(--surface-2); flex-wrap: wrap;
  }
  /* .dbtn's base rule carries margin-top:8px (spacing for its other uses —
     detail-view footers, lifecycle actions) which also drags Filter/+Add
     style down 8px below this row's other items even under
     align-items:center, since the margin is part of the flex item's own
     box. Toolbar-scoped override, not a change to .dbtn itself (P69,
     Parthiv). */
  .filter-bar .dbtn { margin-top: 0; }
  .table-wrap { overflow-x: auto; background: var(--surface); }
  /* table-layout:fixed + explicit column widths (not auto) — DP272's
     .mf-style-clip is position:absolute (P69, needed for a definite
     height — see 42.02-components.css), and absolutely positioned content
     doesn't count toward a table's auto-layout content-width calculation.
     Under table-layout:auto that left the Style column effectively
     width-less, so the browser gave it almost no space: Buyer crowded in
     right next to it and longer style numbers/colours wrapped to 2 lines
     mid-slide. Fixed, explicit widths restore predictable, evenly-spaced
     columns matching the locked mockup regardless of any one column's
     own content quirks. */
  table.styles-table { width: 100%; border-collapse: collapse; min-width: 760px; table-layout: fixed; }
  .th-style, .styles-table td.style-cell { width: 34%; }
  .th-buyer, .styles-table td:nth-child(2) { width: 16%; }
  .th-supplier, .styles-table td:nth-child(3) { width: 16%; }
  .th-delivery, .styles-table td:nth-child(4) { width: 19%; }
  .styles-table th {
    text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--ink-dim); font-weight: 600; padding: 10px 14px; border-bottom: 1px solid var(--border);
    white-space: nowrap; position: sticky; top: 0; background: var(--surface);
  }
  .styles-table td { padding: 11px 14px; border-bottom: 1px solid var(--border); font-size: 13.5px; vertical-align: middle; }
  .styles-table tbody tr { cursor: pointer; transition: background 0.1s ease; }
  .styles-table tbody tr:hover { background: var(--surface-2); }
  .styles-table tbody tr:focus-visible { background: var(--accent-wash); }
  .style-id { font-family: var(--mono); font-weight: 600; font-size: 13.5px; }
  .style-sub { color: var(--ink-dim); font-size: 12px; margin-top: 2px; }
  .styles-table tbody tr.status-dimmed { opacity: 0.55; }
  tr.colour-group-row .style-id { font-weight: 700; }
  /* Row-wide tint (not just the Style cell — see components.css's
     .mf-style-text overrides for that half) so a clubbed group reads as
     one tinted block across every column, not just its first one. */
  /* Clubbed header row deliberately gets NO special background — it must
     read identically to a plain single-colour row (P70 QA: tinting it
     the same as the expanded sub-rows made the whole block look like one
     indistinct blob). The chevron alone signals "this expands." Only the
     expanded sub-rows get their own shade, one step DARKER than the row
     default — var(--bg), the page background, is already consistently
     darker than var(--surface) in both themes (no new token). */
  tr.colour-sub-row { background-color: var(--bg); }
  /* Needs the .styles-table tbody prefix to beat the generic hover rule
     above on specificity (equal class-tier, generic wins the type-tier
     tiebreak otherwise — see components.css's matching .mf-style-text
     fix for the same bug, caught P70). One step brighter than the
     sub-row's own var(--bg) rest state, same size step as every other
     row's surface→surface-2 hover jump. */
  .styles-table tbody tr.colour-sub-row:hover { background-color: var(--surface); }
  tr.colour-sub-row td { color: var(--ink-dim); }
  /* .styles-table td's own border-bottom (above) is the main border —
     unchanged, same weight/colour used between every top-level row in the
     table. Inside an expanded clubbed block specifically, go a shade
     LIGHTER (var(--border-soft), already fainter than var(--border) in
     both themes) so the group reads as colours nested inside one block:
     header-to-first-sub-row (blanket override, a group header's next
     sibling is always a sub-row) and sub-row-to-sub-row (:has() needed —
     a sub-row's OWN class can't tell whether the row after it is another
     sub-row or the next different style, only look-ahead can). The LAST
     sub-row of a group is deliberately left unoverridden, so its
     border-bottom falls through to the main border — that's what marks
     the clubbed block's own end, with no extra rule needed. */
  tr.colour-group-row td { border-bottom: 1px solid var(--border-soft); }
  tr.colour-sub-row:has(+ tr.colour-sub-row) td { border-bottom: 1px solid var(--border-soft); }
  .cell-date { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; }
  .cell-date .agg-prefix {
    font-size: 10px; color: var(--ink-faint); text-transform: uppercase;
    letter-spacing: .03em; display: block; margin-bottom: 2px;
  }
  .cell-date .agg-prefix.empty { visibility: hidden; }
  .styles-table td.open-activities-cell { width: 130px; white-space: nowrap; }
  /* .styles-table th (class+element, specificity 0,1,1) beats a bare
     .th-oa (0,1,0) regardless of source order — the header's own
     text-align:left was silently winning over this, so "Open Activities"
     never actually right-aligned to match .open-activities-cell below it
     (P69). Scoped to match/beat the base rule's specificity. */
  .styles-table th.th-oa { text-align: right; }
  .stage-dots { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
  .sdot { width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--border); background: transparent; }
  .sdot[data-s="done"] { background: var(--good); border-color: var(--good); }
  .sdot[data-s="overdue"] { background: var(--bad); border-color: var(--bad); }
  .sdot[data-s="at-risk"] { background: var(--warn-wash); border-color: var(--warn); }
  .sdot[data-s="pending"] { background: transparent; border-color: var(--border); }
  .sdot[data-s="rejected"] {
    background: var(--bad); border-color: var(--bad);
    box-shadow: 0 0 0 2px var(--bad-wash);
  }
  .sdot[data-s="na"] { background: transparent; border-color: var(--border); border-style: dashed; opacity: 0.35; }
  .sdot[data-s="not-started"] { background: var(--surface-2); border-color: var(--border); }
  .empty-row td, .loading-row td { color: var(--ink-dim); font-size: 13px; padding: 24px 14px; text-align: center; }
  @media (max-width: 720px) {
    .kpi-strip { grid-template-columns: repeat(3, 1fr); }
  }
  .rail { position: fixed; left: 0; top: 0; bottom: 0; width: 56px;
           background: var(--surface-2); border-right: 1px solid var(--border);
           display: flex; flex-direction: column; align-items: center;
           padding-top: 12px; gap: 4px; z-index: 40; }
  .rail-item { width: 40px; height: 40px; display: flex; align-items: center;
                justify-content: center; border: none; background: none;
                border-radius: 8px; cursor: pointer; font-size: 17px;
                color: var(--ink-dim); }
  .rail-item:hover { background: var(--surface); color: var(--ink); }
  .rail-item.active { background: var(--accent-wash); color: var(--accent); }
  body { padding-left: 56px; }
  @media (max-width: 720px) { .rail { flex-direction: row; width: 100%;
    height: 48px; bottom: auto; padding: 0; justify-content: space-around; }
    body { padding-left: 0; padding-top: 48px; } }
  .dsection h3 { display: flex; align-items: center; gap: 6px; }
  .dsection h3.collapsible-toggle { cursor: pointer; user-select: none; }
  .dsection h3.collapsible-toggle .toggle-icon {
    display: inline-block; transition: transform 0.15s ease; font-size: 9px;
  }
  .dsection h3.collapsible-toggle.open .toggle-icon { transform: rotate(90deg); }
  .dsection h3.collapsible-toggle .toggle-count { text-transform: none; letter-spacing: normal; }
  .dlog-body.hidden { display: none; }
  .dsection h3.dirty::after {
    content: ""; width: 6px; height: 6px; border-radius: 50%;
    background: var(--accent); flex-shrink: 0;
  }
  .dcard.dirty { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
  .dcard.dirty .dcard-head .name::after {
    content: ""; display: inline-block; vertical-align: middle; margin-left: 6px;
    width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  }
  .dsection { padding: 16px 0; border-bottom: 1px solid var(--border); }
  .dsection:last-child { border-bottom: none; }
  /* Persistent activity-log container for renderActivityLogSection on the
     new #styleDetailView (/v2) shell — kept outside #dvDetailArea so Task
     7's per-click renderSpineAndDetail() can clear and repopulate
     #dvDetailArea alone without wiping it. Style Details itself moved into
     the spine as a real entry (post-interactive-review revision) — no
     separate persistent container for it anymore. */
  .dv-activity-log { padding: 16px 24px 20px; border-top: 1px solid var(--border); }
  /* #dvHeader (Task 4's /v2 shell) had no layout rule at all — title block
     and .detail-actions (which relies on margin-left:auto to push right)
     just stacked as unstyled block content instead of the mockup's header
     row, part of the P53 manual-verification header-buttons gap. */
  .dv-header {
    display: flex; flex-direction: column;
    padding: 18px 26px 16px; border-bottom: 1px solid var(--border);
  }
  /* Club banner (Parthiv, P54) moved to sit below this row, inside the
     header, matching the mockup's .app-header > .id-row + .club-banner
     nesting — it previously rendered as a page-level sibling before the
     whole header. */
  .dv-id-row { display: flex; align-items: flex-start; gap: 16px; }
  /* #styleDetailView had no bounding-height/flex CSS at all, so the page
     just grew with content — .detail-footer's flex-shrink:0 (42.02-
     components.css) and .dv-spine/.dv-detail-area's overflow-y:auto below
     were both dead for lack of a flex-column ancestor with a fixed height
     to size against (P61 fix, Open Backlog #1a). Pattern matches
     #loginScreen/#loginScreen.hidden above: unqualified ID selector would
     otherwise beat the generic .hidden class on specificity and defeat
     hiding, so the override needs its own #id.hidden rule too. */
  #styleDetailView {
    display: flex; flex-direction: column; height: 100vh; overflow: hidden;
  }
  #styleDetailView.hidden { display: none; }
  #styleDetailView > .dv-owner-banner,
  #styleDetailView > .dv-header,
  #styleDetailView > .dv-activity-log { flex-shrink: 0; }
  /* Task 7 (DP129): spine + single-group-focus detail area, replacing the
     multi-open accordion on /v2. */
  .dv-body { display: flex; flex: 1; min-height: 0; }
  .dv-spine {
    width: 280px; flex-shrink: 0; border-right: 1px solid var(--border);
    padding: 14px 0; overflow-y: auto;
  }
  .dv-spine-item {
    display: block; width: 100%; text-align: left; padding: 10px 20px;
    border: none; background: none; border-left: 3px solid transparent;
    color: var(--ink); cursor: pointer; font-family: var(--sans); font-size: 13px;
  }
  .dv-spine-item.active { background: var(--surface-2); border-left-color: var(--accent); }
  /* flex-wrap (not space-between on a single line) so a badge/pill that
     doesn't fit drops to its own line below the title instead of either
     clipping past the spine's edge or squeezing the title into a narrow,
     multi-line column — both were real regressions found in manual
     verification (no test covers /v2's rendered layout). The title itself
     stays nowrap so short 2-3 word group names never wrap mid-word. */
  .dv-spine-item-title {
    display: flex; flex-wrap: wrap; align-items: center; gap: 3px 8px;
  }
  .dv-spine-item-label { white-space: nowrap; font-weight: 600; }
  .dv-spine-item-sub { font-size: 11px; color: var(--ink-dim); margin-top: 2px; }
  .dv-detail-area { flex: 1; padding: 20px 24px; overflow-y: auto; }
  .dv-group-body {
    display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  }
  .dv-group-body > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  /* PO tabs (addendum §3.6, Task 9) must span the full 2-col grid width —
     without this the tabs land in a single grid cell next to the PPS/QC
     card instead of a horizontal row above it (P53 manual-verification
     bug). A tabbed group's own content isn't meant for the 2-col
     side-by-side layout fabric_trim uses that grid for, so once the tabs
     take their own full-width row, every other child of that body is
     forced full-width/stacked too — the generic :last-child:nth-child(odd)
     rule above can't be relied on here since the tabs row shifts parity. */
  .dv-group-body:has(> .dv-po-tabs) > * { grid-column: 1 / -1; }
  /* Style Details (noStatus spine entry) reuses renderHeaderSection/
     renderDeliverySection's own full-width .dsection styling — it must not
     get the 2-column stage-card grid meant for small cards. */
  .dv-plain-body .dsection:first-child { padding-top: 0; }
  .dv-locked-note {
    grid-column: 1 / -1; background: var(--surface-2); border-radius: 8px;
    padding: 10px 14px; color: var(--ink-dim); font-size: 12.5px; margin-bottom: 12px;
  }
  .dv-empty-group {
    grid-column: 1 / -1; color: var(--ink-dim); font-size: 12.5px;
    padding: 20px; text-align: center;
  }
  /* Shipping Checklist's "Lab test — Overall" heading — same full-width
     fix as .dv-locked-note/.dv-empty-group above, for the same reason
     (P54 screenshot: a bare heading sharing a grid row with the taller
     Fabric-report card left a large blank gap under it). */
  .dv-labtest-overall-head { grid-column: 1 / -1; }
  .dsection h3 {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--ink-dim); margin: 0 0 10px; font-weight: 700;
  }
  .dgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; font-size: 13px; }
  .dgrid .full { grid-column: 1 / -1; }
  .dfield-lbl { color: var(--ink-dim); font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; }
  .dfield-val { font-size: 13.5px; margin-top: 2px; }
  .dcard {
    border: 1px solid var(--border); border-radius: 7px; padding: 12px 14px; margin-bottom: 10px;
    background: var(--surface-2);
  }
  .dcard-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; gap: 8px; }
  .dcard-head .name { font-weight: 600; font-size: 13.5px; }
  .dcard-head .status-wrap { display: flex; align-items: center; gap: 6px; }
  .dcard-head-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
  .gate-locked { opacity: 0.6; }
  .triage-row { display: flex; align-items: center; gap: 8px; padding: 7px 0;
                border-bottom: 1px solid var(--border); }
  .triage-label { flex: 1; font-size: 13px; }
  .gate-lock {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; border-radius: 50%; font-size: 10px; flex-shrink: 0;
    color: var(--warn); background: var(--warn-wash); cursor: help;
  }
  .iteration-list { margin-bottom: 4px; }
  .iteration-collapsed {
    font-size: 12px; color: var(--ink-dim); padding: 6px 10px; margin-bottom: 6px;
    border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
  }
  .iteration-collapsed:hover { background: var(--surface-2); }
  .iteration-detail { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border); }
  .iteration-detail.hidden { display: none; }
  .iteration-summary-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .iteration-edit-pencil {
    display: inline-flex; align-items: center; justify-content: center;
    width: 12px; height: 12px; flex-shrink: 0; color: var(--ink-dim); cursor: pointer;
  }
  .iteration-edit-pencil:hover { filter: brightness(1.15); }
  .iteration-edit-cancel {
    display: inline-flex; align-items: center; justify-content: center;
    width: 13px; height: 13px; flex-shrink: 0; color: var(--ink-dim); cursor: pointer;
  }
  .iteration-edit-cancel:hover { color: var(--bad); }
  .section-revert-icon {
    display: none; align-items: center; justify-content: center;
    width: 13px; height: 13px; flex-shrink: 0; color: var(--ink-dim); cursor: pointer;
  }
  .section-revert-icon:hover { color: var(--bad); }
  .dcard.dirty .section-revert-icon { display: inline-flex; }
  .dgroup { border-bottom: 1px solid var(--border); }
  .dgroup:last-child { border-bottom: none; }
  .dgroup-head {
    display: flex; align-items: center; gap: 8px; padding: 14px 0; min-height: 44px;
    cursor: pointer; user-select: none;
  }
  .dgroup-chevron {
    display: inline-flex; color: var(--ink-dim); font-size: 10px; flex-shrink: 0;
    transition: transform 0.2s ease-out;
  }
  .dgroup-head[aria-expanded="true"] .dgroup-chevron { transform: rotate(90deg); }
  .dgroup-title {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--ink-dim); font-weight: 700; flex: 1;
  }
  .dgroup-body { padding-bottom: 4px; }
  .dgroup-body.hidden { display: none; }
  @media (prefers-reduced-motion: reduce) {
    .dgroup-chevron { transition: none; }
  }
  .fabric-edit-row {
    border: 1px solid var(--border); border-radius: 6px; padding: 8px; margin-bottom: 8px;
  }
  .fabric-edit-row .fabric-card-row {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 6px;
  }
  .fabric-edit-row .fabric-card-row.single { grid-template-columns: 1fr auto; align-items: center; }
  .fabric-edit-row label { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--ink-dim); }
  .fabric-edit-row label.hidden { display: none; }
  .fabric-edit-row input:not([type="checkbox"]), .fabric-edit-row select {
    font-size: 13px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 5px;
    background: var(--surface); color: var(--ink); font-family: inherit; width: 100%; box-sizing: border-box; height: 32px;
  }
  .fabric-edit-row input[type="checkbox"] { width: auto; height: auto; flex-shrink: 0; }
  .fabric-edit-row select {
    appearance: none; -webkit-appearance: none; -moz-appearance: none; padding-right: 26px;
    background-image:
      linear-gradient(45deg, transparent 50%, var(--ink-dim) 50%),
      linear-gradient(135deg, var(--ink-dim) 50%, transparent 50%);
    background-position: right 15px center, right 10px center;
    background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
  }
  .fabric-edit-row .fabric-checkbox { flex-direction: row; align-items: center; gap: 6px; }
  .fabric-total { font-size: 12px; color: var(--ink-dim); margin: 4px 0 8px; }
  .dext-row, .dlog-row {
    font-size: 12.5px; padding: 7px 0; border-bottom: 1px dashed var(--border);
  }
  .dext-row:last-child, .dlog-row:last-child { border-bottom: none; }
  .lmeta { color: var(--ink-dim); font-size: 11px; }
  .internal-code-footer { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); }
  .dlog-row .lmeta, .dext-row .lmeta { margin-top: 2px; }
  .dempty { color: var(--ink-dim); font-size: 12.5px; padding: 4px 0; }
  .create-modal .requires-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; margin: 4px 0 14px; }
  .create-modal .requires-grid label {
    display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-dim); flex-direction: row;
  }
  .requires-heading {
    font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em;
    color: var(--ink-dim); margin: 12px 0 6px;
  }
  .requires-group-heading {
    font-size: 11.5px; font-weight: 600; color: var(--ink); margin: 10px 0 4px;
  }
  .requires-group-heading:first-child { margin-top: 2px; }
  .wizard-steps { display: flex; gap: 4px; margin: 4px 0 16px; flex-wrap: wrap; }
  .wizard-step-label {
    padding: 6px 10px; border-radius: 5px; font-size: 11.5px; font-weight: 500;
    color: var(--ink-dim); background: var(--surface-2);
  }
  .wizard-step-label.active { color: var(--surface); background: var(--accent); }
  .wizard-step-label.done { color: var(--ink); }
  .required-note { font-size: 11.5px; color: var(--ink-dim); margin: 0 0 12px; }
  .req-star { font-weight: 700; color: var(--bad); }
  .analytics-section { margin: 22px 0; }
  .analytics-section:first-of-type { margin-top: 4px; }
  .analytics-section h4 {
    margin: 0 0 10px; font-size: 12.5px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.05em; color: var(--ink-dim);
  }
  .stat-tile-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 4px 0 22px; }
  .stat-tile {
    padding: 12px 14px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
  }
  .stat-tile .num { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 21px; font-weight: 600; line-height: 1; }
  .stat-tile.overdue .num { color: var(--bad); }
  .stat-tile.at-risk .num { color: var(--warn); }
  .stat-tile.on-track .num { color: var(--good); }
  .stat-tile .lbl { margin-top: 5px; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-dim); }
  .chart-label { font-size: 10.5px; fill: var(--ink-dim); }
  .chart-value { font-size: 10.5px; fill: var(--ink); font-family: var(--mono); font-variant-numeric: tabular-nums; }
  .chart-legend { display: flex; flex-wrap: wrap; gap: 12px; margin: 8px 0 4px; }
  .chart-legend-item { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ink-dim); }
  .chart-legend-swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; }
  .analytics-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
  .analytics-table th {
    text-align: left; padding: 6px 10px; color: var(--ink-dim); font-weight: 600;
    border-bottom: 1px solid var(--border); cursor: pointer; user-select: none; white-space: nowrap;
  }
  .analytics-table th:hover { color: var(--ink); }
  .analytics-table th .sort-arrow { margin-left: 4px; opacity: 0.6; }
  .analytics-table td { padding: 6px 10px; border-bottom: 1px solid var(--border); }
  .analytics-empty { font-size: 12.5px; color: var(--ink-dim); padding: 8px 0; }
  .verdict-band { margin: 4px 0 18px; padding: 14px 16px; border-radius: 8px;
                  background: var(--surface-2); border: 1px solid var(--border); }
  .verdict-line { margin: 0 0 8px; font-size: 14px; line-height: 1.5; color: var(--ink); }
  .verdict-line:last-child { margin-bottom: 0; }
  .verdict-empty { margin: 0; font-size: 13px; color: var(--ink-dim); font-style: italic; }
  .analytics-table td .na { color: var(--ink-dim); font-variant-numeric: tabular-nums; }
  .analytics-table td .delta { margin-left: 6px; font-size: 11px;
                               font-variant-numeric: tabular-nums; }
  .delta-good::before {
    content: ""; display: inline-block; width: 6px; height: 6px;
    border-radius: 50%; background: var(--good); margin-right: 5px;
  }
  .delta-bad::before {
    content: ""; display: inline-block; width: 6px; height: 6px;
    border-radius: 50%; background: var(--bad); margin-right: 5px;
  }
  .analytics-table td .row-name { font-weight: 600; }
  .coverage-note { margin: 14px 0 0; font-size: 11.5px; color: var(--ink-dim); }
  .breakdown { margin-top: 16px; }
  .breakdown summary { cursor: pointer; font-size: 12.5px; font-weight: 600;
                       color: var(--ink-dim); padding: 6px 0; }
  .breakdown summary:hover { color: var(--ink); }
  .breakdown .analytics-table { margin-top: 6px; }
  .analytics-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border);
                    margin-bottom: 16px; }
  .atab { padding: 9px 16px; font-size: 13px; font-weight: 500; border: none;
          background: none; color: var(--ink-dim); cursor: pointer;
          border-bottom: 2px solid transparent; }
  .atab:hover { color: var(--ink); }
  .atab.active { color: var(--accent); border-bottom-color: var(--accent); }
  .lbl-row { display: block; }
  .requires-heading .req-star { margin-left: 4px; }
  .requires-grid label { line-height: 1.2; }
  .requires-grid input[type="checkbox"] { width: 14px; height: 14px; flex-shrink: 0; margin: 0; }
  .fabric-box {
    border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px 12px;
    background: var(--surface-2); margin-bottom: 4px;
  }
  .fabric-caption { font-size: 12px; color: var(--ink-dim); margin-bottom: 8px; }
  .fabric-total { font-size: 12px; font-weight: 600; margin: 6px 0; }
  .fabric-card {
    border: 1px solid var(--border); border-radius: 6px; padding: 8px; margin-bottom: 8px;
    background: var(--surface);
  }
  .fabric-card.collapsed { padding: 0; }
  .fabric-card .fabric-card-body { display: block; }
  .fabric-card.collapsed .fabric-card-body { display: none; }
  .fabric-summary-line { display: none; padding: 8px 10px; font-size: 12.5px; cursor: pointer; }
  .fabric-card.collapsed .fabric-summary-line { display: block; }
  .fabric-summary-line:hover { background: var(--surface-2); border-radius: 6px; }
  .fabric-card-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 6px; }
  .fabric-card-row.single { grid-template-columns: 1fr auto; align-items: center; margin-bottom: 0; }
  .fabric-card label {
    display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--ink-dim);
  }
  .fabric-card label.fabric-checkbox { flex-direction: row; align-items: center; gap: 6px; }
  .fabric-card input, .fabric-card select {
    font-size: 13px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 5px;
    background: var(--surface); color: var(--ink);
  }
  .dform-grid .requires-grid { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 6px; }
  .dform-grid .requires-grid label {
    display: flex; align-items: center; gap: 6px; flex-direction: row; font-size: 12px; color: var(--ink-dim);
  }
