/* ═══════════════════════════════════════════════════════════════════
   BASEFORCE.CSS · App-specific extensions only
   ───────────────────────────────────────────────────────────────────
   This file contains ONLY app-specific additions that have no
   equivalent in the base design system (./design-system/).

   Architectural rule — this file MUST NOT:
     · introduce new pigments (colors, backgrounds, borders, text)
     · introduce new tokens (spacing, radii, motion, typography)
     · override anything defined in tokens.css, theme-slate.css,
       or components.css

   Baseforce carries two semantic taxonomies — tier IDs (Q1_RETAIN,
   Q2_MONITOR_H, Q3_MONITOR_L, Q4_PIP, Q4T_TERMINATE) and POS band
   labels (Green, Yellow, Orange, Red, Zeroed). Those semantics live
   in the data. This file maps them onto existing design-system badge
   variants so rendering code can stamp out, e.g.,
   `.badge .badge--tier-Q1_RETAIN` without the design system needing
   to know anything Baseforce-specific.

   Anything that can already be expressed with base tokens and
   components belongs in the HTML, not here.
   Version 1.0 · April 2026
   ═══════════════════════════════════════════════════════════════════ */


/* ─── Badge semantic roles · locked I1 ────────────────────────────── */
/*
   The design system ships three solid-fill badge variants used as
   general-purpose markers (outside the approved/progress/rejected/draft
   status family). In Baseforce these three carry three distinct
   semantic roles — do not overload or swap them between roles:

     .badge--ink       FORMAL RECORD-WEIGHT.
                       Confidentiality markers in page headers and
                       canonical typed records (Decision register
                       entries). Ink-filled dark pill. One usage per
                       page header is the typical load; record-type
                       usage in rendered lists is the exception.

     .badge--accent    CANONICAL AUTHORITY.
                       Locked-state markers ("Framework locked — S18"),
                       primary structural metadata (dimension weights),
                       and other "this is the canonical value" pills.
                       Slate-filled accent pill.

     .badge--neutral   TYPED CONTENT / QUIET CLASSIFICATION.
                       Parking Lot items, supporting tags on cards,
                       "Hold" or unclassified states. Warm-neutral pill.

   The tier and band badges below (.badge--tier-* and .badge--band-*)
   extend the approved/progress/rejected/draft/neutral variants for
   Baseforce's data-semantic taxonomies — see D5 lock in the port plan.
 */


/* ─── Tier badges · alias to base badge variants (D5) ─────────────── */

.badge--tier-Q1_RETAIN {
  background: var(--badge-approved-bg);
  border-color: var(--badge-approved-bd);
  color: var(--badge-approved-tx);
}

.badge--tier-Q2_MONITOR_H {
  background: var(--badge-neutral-bg);
  color: var(--badge-neutral-tx);
  border: none;
}

.badge--tier-Q3_MONITOR_L {
  background: var(--badge-draft-bg);
  border-color: var(--badge-draft-bd);
  color: var(--badge-draft-tx);
}

.badge--tier-Q4_PIP,
.badge--tier-Q4_PIP_FLAG {
  background: var(--badge-progress-bg);
  border-color: var(--badge-progress-bd);
  color: var(--badge-progress-tx);
}

.badge--tier-Q4T_TERMINATE {
  background: var(--badge-rejected-bg);
  border-color: var(--badge-rejected-bd);
  color: var(--badge-rejected-tx);
}


/* ─── POS band badges · alias to base badge variants ──────────────── */

.badge--band-green  { background: var(--badge-approved-bg); border-color: var(--badge-approved-bd); color: var(--badge-approved-tx); }
.badge--band-yellow { background: var(--badge-progress-bg); border-color: var(--badge-progress-bd); color: var(--badge-progress-tx); }
.badge--band-orange { background: var(--badge-draft-bg);    border-color: var(--badge-draft-bd);    color: var(--badge-draft-tx); }
.badge--band-red    { background: var(--badge-rejected-bg); border-color: var(--badge-rejected-bd); color: var(--badge-rejected-tx); }
.badge--band-zeroed { background: var(--badge-neutral-bg);  color: var(--badge-neutral-tx); border: none; }


/* ─── Chip · toggleable filter chip ───────────────────────────────── */
/* Not covered by components.css. .segmented is a grouped control;
   chips are independent toggles used across filter bars. Uses only
   base tokens. */

.chip-group {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.chip {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-sans);
  font-size: var(--size-label);
  font-weight: var(--weight-regular);
  letter-spacing: 0.02em;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  border: 0.5px solid var(--border-rest);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--motion-micro) var(--ease),
              color var(--motion-micro) var(--ease),
              background-color var(--motion-micro) var(--ease);
}

.chip:hover {
  border-color: var(--border-hover);
  color: var(--text-primary);
}

.chip[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-dark);
  font-weight: var(--weight-medium);
}

.chip:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--surface-page), 0 0 0 3.5px var(--accent);
}


/* ─── Sortable table header · th-sortable ─────────────────────────── */
/*
   Markup contract — required on every sortable <th>:

     <th class="th-sortable" aria-sort="none" tabindex="0">
       Column label
       <svg class="th-sort-icon" viewBox="0 0 8 10" aria-hidden="true">
         <path class="th-sort-asc"  d="M0 4L4 0L8 4"
               stroke="currentColor" stroke-width="1.1" fill="none"
               stroke-linecap="round" stroke-linejoin="round"/>
         <path class="th-sort-desc" d="M0 6L4 10L8 6"
               stroke="currentColor" stroke-width="1.1" fill="none"
               stroke-linecap="round" stroke-linejoin="round"/>
       </svg>
     </th>

   Required attributes on the <th>:
     · class="th-sortable"   applies the interactive styling
     · aria-sort="none"      initial state; mutated by page JS
     · tabindex="0"          makes the header keyboard-focusable

   aria-sort state cycle (page JS drives):
     "none" → "ascending" → "descending" → "ascending" → "descending" → …
     First click on a column sorts ascending; subsequent clicks on the
     same column toggle ascending/descending. "none" is the initial
     state only — it is not cycled back into by user interaction.

   Only-one-column-at-a-time invariant:
     When a new column becomes the sort key, every other .th-sortable
     in the same table must have aria-sort reset to "none" before the
     new column's attribute is set. Screen readers rely on exactly one
     column reporting a non-none sort state at any given time.

   Separation of concerns:
     This file styles the visual states only. Click handlers, keyboard
     handlers (Enter / Space), aria-sort mutation, and actual row
     sorting are page-responsibility — wired in the dedicated JS
     session after the port completes. The CSS below reads aria-sort
     as the single source of truth: flip the attribute in JS and the
     visual updates automatically.
 */

.th-sortable { cursor: pointer; user-select: none; transition: color var(--motion-micro) var(--ease); }
.th-sortable:hover { color: var(--text-primary); }
.th-sortable:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--surface-page), 0 0 0 3.5px var(--accent); }

.th-sort-icon { width: 8px; height: 10px; margin-left: 6px; vertical-align: -1px; color: currentColor; }
.th-sort-icon .th-sort-asc,
.th-sort-icon .th-sort-desc { opacity: 0.3; transition: opacity var(--motion-micro) var(--ease); }

.th-sortable[aria-sort="ascending"]  .th-sort-asc  { opacity: 1; }
.th-sortable[aria-sort="descending"] .th-sort-desc { opacity: 1; }


/* ─── Expand / disclosure mechanism · aria-expanded driven ────────── */
/*
   Generic disclosure pattern. Single source of truth: the aria-expanded
   attribute on the clickable control. Flip that attribute in page JS
   and the caret + panel update automatically.

   Two structural patterns are supported — pages pick whichever fits:

   1. Sibling pattern (card / div layouts):

        <div class="…" aria-expanded="false" onclick="…">
          …header content…
          <svg class="expand-caret" viewBox="0 0 14 14" fill="none">
            <path d="M3.5 5.5 L7 9 L10.5 5.5" stroke="currentColor"
                  stroke-width="1.1" stroke-linecap="round"
                  stroke-linejoin="round"/>
          </svg>
        </div>
        <div class="expand-panel">
          …panel content, revealed when header is expanded…
        </div>

   2. Cross-row pattern (table layouts — required because table-row
      elements can't animate max-height; panel must live inside the
      adjacent <tr>'s <td>):

        <tr aria-expanded="false" onclick="…">…</tr>
        <tr>
          <td colspan="N">
            <div class="expand-panel">…</div>
          </td>
        </tr>

   The caret rotates 180° when its ancestor's aria-expanded is "true".
   The panel's max-height opens when the immediately preceding sibling
   (or preceding-<tr>) has aria-expanded="true".

   Page JS handles: click/keyboard toggle, single- vs multi-open policy,
   and the actual aria-expanded mutation. This file styles only. */

.expand-caret {
  width: 14px; height: 14px;
  color: var(--text-tertiary);
  transition: transform var(--motion-short) var(--ease),
              color var(--motion-micro) var(--ease);
}
[aria-expanded="true"] .expand-caret {
  transform: rotate(180deg);
  color: var(--text-primary);
}

.expand-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--motion-medium) var(--ease);
}
[aria-expanded="true"] + .expand-panel,
[aria-expanded="true"] + tr .expand-panel {
  max-height: 2000px;
}


/* ─── Data table · shared flat-border tabular presentation ────────── */
/*
   Flat tabular styling for row-based data. Composes with .th-sortable
   (above) for sortable headers, and with the expand mechanism (above)
   for row-level disclosure. Used wherever Baseforce presents data in a
   real <table> — individual rosters, deal lists, item registers.

   Markup contract — minimal table:

     <table class="data-table">
       <thead>
         <tr>
           <th>Name</th>
           <th class="r">Amount</th>
           …
         </tr>
       </thead>
       <tbody>
         <tr>…row cells…</tr>
       </tbody>
     </table>

   With row disclosure (two-row pattern — the <tr> immediately after a
   tr[aria-expanded] contains the panel inside a full-width <td>):

     <tbody>
       <tr aria-expanded="false" onclick="…">
         …row cells… <svg class="expand-caret">…</svg>
       </tr>
       <tr>
         <td colspan="N">
           <div class="expand-panel">
             …panel content…
           </div>
         </td>
       </tr>
     </tbody>

   With sortable headers:

     <th class="th-sortable" aria-sort="none" tabindex="0">
       Column label
       <svg class="th-sort-icon" viewBox="0 0 8 10" …>…</svg>
     </th>

   Three-way composition summary:
     · .data-table          borders, typography, row-hover, alignment
     · .th-sortable         interactive sort header (→ th-sortable block)
     · aria-expanded=true   row highlight + panel reveal (→ expand block)

   Column alignment:
     · class="r" on <th> and <td>   right-align + tabular-nums. Apply to
                                    every column containing numeric data.

   Page vs CSS responsibility:
     · This file styles:
         borders, typography of <th>, td padding, row hover/expanded
         background, the panel-row <td> reset (padding:0 + no border)
         when the row contains .expand-panel.
     · Page owns:
         column widths (inline style on <th>), column-specific rendering
         (tier badges, inline colored values, score bars), click and
         keyboard handlers, aria-expanded / aria-sort mutation, sort
         logic, filter / search.

   The :has(.expand-panel) selector drives the panel-row cell reset
   without requiring a dedicated class on the <tr> — any row containing
   the panel gets the flush-cell treatment automatically. Browser
   support for :has() is universal among evergreen browsers.
 */

.data-table { width: 100%; border-collapse: collapse; font-size: var(--size-small); }
.data-table th, .data-table td {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  vertical-align: middle;
  border-bottom: 0.5px solid var(--border-rest);
}
.data-table th {
  font-size: var(--size-eyebrow);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-style: italic;
  font-weight: var(--weight-regular);
  white-space: nowrap;
}
.data-table th.r, .data-table td.r { text-align: right; font-variant-numeric: tabular-nums; }

.data-table tr[aria-expanded] { cursor: pointer; transition: background-color var(--motion-micro) var(--ease); }
.data-table tr[aria-expanded]:hover td { background: var(--surface-flat); }
.data-table tr[aria-expanded="true"] td { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.data-table tr:has(.expand-panel) td { padding: 0; border: none; background: var(--surface-flat); }

/* ─── .data-table-scroll · horizontal scroll wrapper for wide tables ─── */
/*
   DS-D1792. System primitive. .content carries overflow-x: hidden
   (components.css), so an unwrapped wide .data-table is CLIPPED, not
   scrollable — SIGS-B1 ships 9 columns and is the proving case. Wrap a
   wide .data-table in <div class="data-table-scroll"> to restore
   horizontal scroll. Harmless on narrow tables — safe as the default
   wrapper for every .data-table.
 */
.data-table-scroll {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

@media print {
  .data-table-scroll { overflow: visible; }
  .data-table { overflow-wrap: anywhere; }
}


/* ─── Page sidenav · secondary navigation column within .content ─── */
/*
   Sticky vertical list of links or SPA-toggle triggers, placed inside
   .content beside .page or .main. Used for within-page section jumps
   — e.g., dataops's 11 source systems via in-page anchors, overview's
   SPA page-switch between macro / division / quadrant views. Distinct
   from the primary site nav (nav.js), which is always the left rail.

   Markup contract:

     <aside class="page-sidenav">
       <div class="page-sidenav-group">[optional group label]</div>
       <a class="page-sidenav-item" href="#anchor" onclick="…">
         [optional dot] Link label [optional trailing badge]
       </a>
       <a class="page-sidenav-item active" …>…</a>
       …
     </aside>

   Active state: add .active to the current item. Page JS manages
   active-state swapping on click.

   Width is page-controlled (set a column width on the parent grid
   or inline on the element) — 220-260px is the common range.

   Page vs CSS responsibility:
     · This file styles: sticky container, item base / hover /
       active state, group-label typography.
     · Page owns: content composition (dots, badges, icons within
       items), click handlers, active-state swap logic, in-page
       scroll offset for anchor targets.

   Dual-state pattern (.active class vs aria-current):
     · .active class         → visual state for SPA within-page
                               toggles (e.g., overview.html's go(id)
                               switching between cover / macro /
                               population panes). Does NOT claim
                               URL-level navigation.
     · [aria-current="true"] → same visual state, but exposed to
                               screen readers. Page JS should toggle
                               both together when SPA state changes.
     · [aria-current="page"] → RESERVED for real URL-level nav — the
                               primary site nav (nav.js) owns this.
                               Do not use "page" on page-sidenav
                               items; it would double-assert "current
                               page" when only one navigation context
                               is truly current.
 */

.page-sidenav {
  background: var(--surface-elevated);
  border-right: 0.5px solid var(--border-rest);
  overflow-y: auto;
  position: sticky;
  top: var(--topbar-height);
  align-self: start;
  max-height: calc(100vh - var(--topbar-height));
  padding: var(--space-3) 0;
  flex-shrink: 0;
}

.page-sidenav-group {
  font-size: var(--size-eyebrow);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-style: italic;
  padding: var(--space-3) var(--space-4) var(--space-1);
}

.page-sidenav-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--size-small);
  border-left: 2px solid transparent;
  transition: color var(--motion-micro) var(--ease),
              border-color var(--motion-micro) var(--ease),
              background-color var(--motion-micro) var(--ease);
}

.page-sidenav-item:hover {
  color: var(--text-primary);
  background: var(--surface-flat);
}

.page-sidenav-item.active,
.page-sidenav-item[aria-current="true"] {
  color: var(--accent);
  border-left-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}


/* ─── Document artifact primitives · .doc-* ────────────────────────── */
/*
   Shared layout scaffold for Wave C standalone artifacts — narrative
   documents, assessments, long-form reports. These primitives are
   deliberately distinct from the app-shell + nav.js + topbar system:

     · App pages load nav.js, render .nav sidebar + .topbar, use .page
       inside .content. Full site chrome.
     · Artifacts load NONE of that. They inherit grain + tokens +
       Satoshi + day-mode default from the design system, and NOTHING
       else from the shell. No nav, no topbar, no theme toggle, no
       site navigation at all.

   The distinction is intentional: an app page is where you work, a
   document is where you read. Documents don't need site chrome
   floating above them — their own header IS the header.

   Markup contract:

     <body>
       <div class="app-shell">                   <!-- grain canvas -->
         <main class="doc-container motion-settle">
           <header class="doc-header">
             <div class="doc-logo">[Wordmark]</div>
             <div class="doc-meta">
               <span>[Meta item]</span>
               <div class="doc-meta-divider"></div>
               <span>[Meta item]</span>
             </div>
           </header>

           <article class="doc-wrap">
             …page-specific content…
           </article>

           <footer class="doc-footer">
             <div>[Credit]</div>
             <div>[Classification]</div>
           </footer>
         </main>
       </div>
     </body>

   Customization points (page-scoped overrides are expected):

     · .doc-wrap max-width — default 780px suits prose; assessment
       docs typically want 900px; reference/spec docs 1100px.
       Pick per the editorial register.
     · .doc-meta flex-direction — default horizontal (row) with
       .doc-meta-divider between items; vertical stacks (flex-column,
       align-items: flex-end, no dividers) via scoped override when
       the meta block needs to stack.
     · .doc-logo — styles the wordmark text. Pages wrap with a
       subtitle / system-badge in scoped CSS if the header needs a
       two-line brand lockup.

   Reserved for standalone artifacts. App pages use the site shell.
 */

.doc-container {
  flex: 1;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.doc-header {
  padding: var(--space-5) var(--space-6);
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 0.5px solid var(--border-rest);
  background: var(--surface-elevated);
  flex-wrap: wrap;
  gap: var(--space-3);
}

.doc-logo {
  font-size: 20px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--track-title);
  color: var(--text-primary);
}

.doc-meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--size-eyebrow);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-style: italic;
  flex-wrap: wrap;
}

.doc-meta-divider {
  width: 1px;
  height: 12px;
  background: var(--border-rest);
}

.doc-wrap {
  max-width: 780px;
  margin: 0 auto;
  width: 100%;
  padding: var(--space-10) var(--space-6) var(--space-8);
  flex: 1;
}

.doc-footer {
  padding: var(--space-4) var(--space-6);
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: var(--size-label);
  color: var(--text-tertiary);
  font-style: italic;
  border-top: 0.5px solid var(--border-rest);
}


/* ─── Filter bar · sticky row under topbar ────────────────────────── */
/* Container for chip groups + action buttons + search inputs across
   filter-driven pages (planning, issues, accounts, pipeline). Negative
   horizontal margin extends to the edges of the .content area so the
   sticky bar reads full-width under the topbar. */

.filter-bar {
  position: sticky;
  top: var(--topbar-height);
  z-index: 40;
  background: var(--surface-elevated);
  border-bottom: 0.5px solid var(--border-rest);
  padding: var(--space-3) var(--space-6);
  margin: 0 calc(var(--space-6) * -1) var(--space-5);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.filter-sep {
  width: 1px;
  height: 20px;
  background: var(--border-rest);
  flex-shrink: 0;
}


/* ─── Filter select · compact <select> for filter bars ────────────── */
/*
   Compact <select> element for filter-bar use. Strips native appearance
   and paints a custom chevron so the control reads as part of the
   design system regardless of browser.

   Markup contract:

     <select class="filter-select" onchange="…">
       <option value="asc">A → Z</option>
       <option value="desc">Z → A</option>
     </select>

   Appearance-reset rationale:
     Native <select> elements leak browser-default chevron styling and
     OS-level dropdown chrome. appearance: none strips the native
     widget; a background-image SVG chevron restores the dropdown hint
     styled to match the design system.

   Day-mode chevron color is embedded as a fixed hex (#8A8578 =
   --text-tertiary day value). CSS custom properties cannot be
   interpolated into data-URI background-image strings, so the day
   hex is hardcoded in the SVG data URI.

   Known debt: night mode renders the same chevron hex, which reads
   slightly dim against the night surface. Not urgent — the chevron
   is a hint, not a load-bearing affordance. A follow-up can address
   via a dual-background approach ([data-theme="night"] selector
   swapping to a night-hex chevron) if the visual dim becomes a
   real concern.

   Use on pages with compact sort/filter controls (seller.html,
   opportunities-browse.html). For full-form selects use .select from
   components.css.
 */

.filter-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-color: var(--surface-raised);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14' fill='none'><path d='M3.5 5.5L7 9l3.5-3.5' stroke='%238A8578' stroke-width='1.1' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 10px;
  border: 0.5px solid var(--border-rest);
  border-radius: var(--radius-md);
  padding: 4px 28px 4px 10px;
  font-family: var(--font-sans);
  font-size: var(--size-label);
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color var(--motion-short) var(--ease),
              box-shadow var(--motion-short) var(--ease);
}

.filter-select:hover { border-color: var(--border-hover); }

.filter-select:focus {
  outline: none;
  border: 1px solid var(--text-primary);
  box-shadow: 0 0 0 2px var(--surface-page), 0 0 0 3.5px var(--accent);
}


/* ─── Progress bar primitive · .prog-track / .prog-fill ───────────── */
/* Thin horizontal bar used for division breakdowns, axis scores, pod
   aggregates, and other non-tabular share/percent displays. Fill color
   and width are set inline per instance — the primitive only defines
   shape and transition. Default height 3px; pages can override. */

.prog-track {
  height: 3px;
  background: var(--surface-flat);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.prog-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  transition: width var(--motion-medium) var(--ease);
}


/* ─── Narrative · eyebrow label + prose body ──────────────────────── */
/*
   Paired primitives for analytical narrative text blocks — the
   "report read" pattern where an eyebrow label introduces a prose
   paragraph. Used across pages that render long-form assessment:

     · accounts.html  — AI-generated account assessment (inside a
                        boxed .narrative-box wrapper)
     · overview.html  — per-seller analytical narrative (inline with
                        a top-border separator)
     · seller.html    — seller profile narrative (below detail expand)

   Markup contract:

     <div class="[optional page-scoped container]">
       <div class="narrative-label">Label text</div>
       <div class="narrative-text">
         <p>First paragraph.</p>
         <p>Second paragraph.</p>
       </div>
     </div>

   Typography rationale:
     · .narrative-label  eyebrow-scale, italic, tertiary text. Quiet,
                         non-competing with the prose it introduces.
                         Uses the same micro-label pattern as
                         .section-title and .metric-label for
                         consistency across reading registers.
     · .narrative-text   inherits body font-size (size-body) with
                         reading-comfortable line-height (1.6). Strong
                         emphasis (on dollar amounts, percentages,
                         names) resolves to ink via color, NOT via
                         bold weight — the design system caps weight
                         at medium (500).

   Reserved for prose:
     · DO use for paragraph-level narrative content (analysis,
       assessment, story-driven explanation).
     · DO NOT use for short metadata or labeled data — use .caption
       or .meta for that. For labeled data cells use .metric-*.
     · Container (boxed card, inline with separator, full-width prose)
       is page-scoped. These primitives style only the content inside.

   Font-size override: pages rendering compact narratives (e.g.,
   overview.html's per-seller card note) can set .narrative-text
   to size-small via scoped CSS or inline. Default inherits size-body.
 */

.narrative-label {
  font-size: var(--size-eyebrow);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-style: italic;
  margin-bottom: var(--space-2);
}

.narrative-text {
  color: var(--text-secondary);
  line-height: 1.6;
}
.narrative-text p { margin-bottom: var(--space-3); }
.narrative-text p:last-child { margin-bottom: 0; }
.narrative-text strong { color: var(--text-primary); font-weight: var(--weight-medium); }


/* ─── Grid · 6-col extension (home KPI strip) ─────────────────────── */
/* components.css defines grid--2 through grid--5. Baseforce home has
   a 6-metric KPI strip. One-line extension of the existing series. */

.grid--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }


/* ─── Nav group label · placement of .eyebrow inside .nav-items ───── */
/* Reuses components.css .eyebrow; only sets nav-local padding and
   inherits the collapse-fade from sibling .nav-item-label behavior. */

.nav-items > .eyebrow {
  display: block;
  padding: var(--space-3) var(--space-4) var(--space-1);
  transition: opacity var(--motion-short) var(--ease);
}

.nav[data-collapsed="true"] .nav-items > .eyebrow {
  opacity: 0;
  pointer-events: none;
}


/* ─── Brand wordmark as link · home-return affordance ──────────────── */
/* Both the sidebar brand lockup (.nav-wordmark, emitted by nav.js) and
   the standalone document logo (.doc-logo, used by Wave C artifacts and
   the org studio header) are rendered as anchors pointing to /home.html
   so the reader can always return to the platform home. The base design-
   system rules for those classes target divs and do not anticipate anchor
   styling — this additive block (properties are new, not overriding)
   strips the default link underline, re-asserts the primary text color,
   and adds a subtle accent-on-hover consistent with other interactive
   brand elements. The [href] attribute selector scopes the rule to
   anchor usages only, so any remaining div usage stays unchanged. */

.nav-wordmark[href],
.doc-logo[href] {
  text-decoration: none;
  color: var(--text-primary);
  transition: color var(--motion-micro) var(--ease);
}

.nav-wordmark[href]:hover,
.doc-logo[href]:hover {
  color: var(--accent);
}


/* ─── Tier system · diagnostic state grammar + legacy D2/D3 frames ──
   The standalone-brand stack (brand.html + /customer-browse.js) was
   retired 2026-06-02 along with the orphaned .d1-* / .expand-host /
   .d2-skel / .d2-error rules. What remains in this section:

     • [data-state="…"] grammar — actively used across the app.
     • tbody tr[data-brand-id|data-agency-id] row affordance — used
       by the customers-browse cascade-toggle.
     • .tier-card / .d2-* / .d3-modal frames — not re-audited in the
       2026-06-02 sweep; may contain further orphans.

   Diagnostic state grammar (DS-D26 + DS-D43): apply [data-state="…"]
   to any element to expose --state and --state-soft custom properties
   for color and tinted-background use respectively. The base ramp uses
   page-local hex values rather than design-system tokens because the
   six lens states are app-specific data semantics that aren't part of
   the foundational ramp.
   ──────────────────────────────────────────────────────────────────── */

[data-state]            { --state: var(--text-tertiary); --state-soft: rgba(0,0,0,0.04); }
[data-state="ELEVATED"] { --state: #623096; --state-soft: rgba(98,48,150,0.10); }
[data-state="HEALTHY"]  { --state: #256E48; --state-soft: rgba(37,110,72,0.10); }
[data-state="WATCH"]    { --state: #C4862C; --state-soft: rgba(196,134,44,0.12); }
[data-state="CONCERN"]  { --state: #B0421A; --state-soft: rgba(176,66,26,0.12); }
[data-state="CRITICAL"] { --state: #9E2018; --state-soft: rgba(158,32,24,0.14); }
[data-state="PENDING"]  { --state: var(--text-tertiary); --state-soft: rgba(255,255,255,0.04); }

[data-theme="night"] [data-state="ELEVATED"] { --state: #B07AE0; --state-soft: rgba(176,122,224,0.14); }
[data-theme="night"] [data-state="HEALTHY"]  { --state: #4FBE85; --state-soft: rgba(79,190,133,0.14); }
[data-theme="night"] [data-state="WATCH"]    { --state: #F5BE5C; --state-soft: rgba(245,190,92,0.14); }
[data-theme="night"] [data-state="CONCERN"]  { --state: #F07A48; --state-soft: rgba(240,122,72,0.14); }
[data-theme="night"] [data-state="CRITICAL"] { --state: #E8554A; --state-soft: rgba(232,85,74,0.14); }

/* Brand- / agency-row affordance */
tbody tr[data-brand-id], tbody tr[data-agency-id] { cursor: pointer; }
tbody tr[data-brand-id][aria-expanded="true"],
tbody tr[data-agency-id][aria-expanded="true"] {
  background: var(--surface-flat);
  border-bottom: none;
}
tbody tr[data-brand-id][aria-expanded="true"] td.col-name,
tbody tr[data-agency-id][aria-expanded="true"] td.col-name {
  color: var(--text-primary);
}

.tier-card { padding: 24px 32px; }
@media (prefers-reduced-motion: no-preference) {
  .tier-card { animation: tier-fade 280ms cubic-bezier(0.2, 0, 0, 1); }
}
@keyframes tier-fade {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── D2 card frame ── */
.d2-card {
  display: flex; flex-direction: column;
  gap: 20px;
  max-width: 1200px;
}

/* D2 hero */
.d2-hero {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 24px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border-rest);
}
.d2-hero-left { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.d2-brand-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.d2-brand-name {
  font-size: 26px; font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin: 0;
}
.d2-agency-line { font-size: 13px; color: var(--text-secondary); }
.d2-hero-right {
  display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  flex-shrink: 0;
}
.d2-composite {
  font-size: 44px; font-weight: 500;
  letter-spacing: -0.03em;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.d2-composite-label {
  font-size: 9.5px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary);
}

/* D2 metric tiles */
.d2-metrics {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}
.d2-metric {
  padding: 14px 16px;
  background: var(--surface-elevated);
  border: 1px solid var(--border-rest);
  border-radius: 8px;
  display: flex; flex-direction: column; gap: 4px;
}
.d2-metric-label {
  font-size: 9.5px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary);
}
.d2-metric-value {
  font-size: 22px; font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.d2-metric--vivid .d2-metric-value { color: var(--vivid); }

/* D2 section title */
.d2-section-title {
  font-size: 9.5px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 10px;
}

/* D2 trajectory chart */
.d2-traj {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 4px;
  align-items: end;
  height: 96px;
}
.d2-traj-col {
  position: relative;
  display: flex; flex-direction: column;
  align-items: stretch; justify-content: flex-end;
  height: 100%;
  padding-bottom: 16px;
}
.d2-traj-bar {
  width: 100%;
  background: color-mix(in srgb, var(--text-secondary) 35%, transparent);
  border-radius: 2px 2px 0 0;
  min-height: 2px;
  transition: background-color 120ms;
}
.d2-traj-col--current .d2-traj-bar { background: var(--vivid); }
.d2-traj-col:hover .d2-traj-bar { background: var(--text-primary); }
.d2-traj-col--current:hover .d2-traj-bar { background: var(--vivid); opacity: 0.85; }
.d2-traj-label {
  position: absolute; bottom: 0; left: 0; right: 0;
  font-size: 9px; text-align: center;
  color: var(--text-tertiary);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.d2-traj-tip {
  position: absolute;
  bottom: calc(100% - 12px);
  left: 50%; transform: translateX(-50%);
  background: var(--surface-elevated);
  border: 1px solid var(--border-rest);
  border-radius: 4px;
  padding: 4px 8px;
  font-size: 11px; white-space: nowrap;
  color: var(--text-primary);
  pointer-events: none;
  opacity: 0; transition: opacity 100ms;
  z-index: 5;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.d2-traj-col:hover .d2-traj-tip { opacity: 1; }

/* D2 lens strip */
.d2-lenses {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
}
.d2-lens {
  padding: 12px;
  background: var(--state-soft);
  border-left: 3px solid var(--state);
  border-radius: 0 6px 6px 0;
  display: flex; flex-direction: column; gap: 5px;
}
.d2-lens-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.d2-lens-name {
  font-size: 10px; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-secondary);
}
.d2-lens-score {
  font-size: 18px; font-weight: 500;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.d2-lens-state {
  font-size: 9px; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--state);
}
.d2-lens-driver { font-size: 11px; color: var(--text-secondary); line-height: 1.4; }

/* D2 waterfall (brand-only, MA/DUAL) */
.d2-waterfall { display: flex; flex-direction: column; gap: 6px; }
.d2-wf-row {
  display: grid;
  grid-template-columns: 160px 1fr 110px;
  align-items: center; gap: 12px;
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.d2-wf-label { color: var(--text-secondary); }
.d2-wf-bar-track {
  height: 12px;
  background: color-mix(in srgb, var(--text-tertiary) 8%, transparent);
  border-radius: 2px; overflow: hidden;
}
.d2-wf-bar {
  height: 100%;
  background: var(--text-secondary);
  border-radius: 2px;
  transition: width 300ms cubic-bezier(0.2, 0, 0, 1);
}
.d2-wf-row--cm .d2-wf-bar { background: var(--vivid); }
.d2-wf-value { color: var(--text-primary); text-align: right; font-weight: 500; }
.d2-wf-margin {
  margin-top: 6px; padding-top: 8px;
  border-top: 1px solid var(--border-rest);
  display: flex; justify-content: space-between; font-size: 12px;
}
.d2-wf-margin-label { color: var(--text-secondary); }
.d2-wf-margin-value {
  color: var(--vivid); font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.d2-altpanel {
  padding: 16px 18px;
  background: var(--surface-elevated);
  border: 1px solid var(--border-rest);
  border-radius: 6px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* D2 agency portfolio table */
.d2-portfolio-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.d2-portfolio-table th {
  font-size: 10px; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-tertiary);
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-rest);
}
.d2-portfolio-table th.col-num    { text-align: right; }
.d2-portfolio-table th.col-center { text-align: center; }
.d2-portfolio-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-rest);
  color: var(--text-primary);
}
.d2-portfolio-table td.col-num    { text-align: right; white-space: nowrap; }
.d2-portfolio-table td.col-center { text-align: center; }
.d2-portfolio-name-cell { display: flex; align-items: center; gap: 6px; }
.d2-portfolio-name {
  color: var(--text-primary); text-decoration: none;
  font-weight: 500;
}
.d2-portfolio-name:hover { text-decoration: underline; }
.d2-portfolio-more {
  margin-top: 8px; font-size: 12px;
}
.d2-portfolio-more > summary {
  cursor: pointer;
  padding: 6px 0;
  color: var(--text-secondary);
  list-style: none;
}
.d2-portfolio-more > summary::-webkit-details-marker { display: none; }
.d2-portfolio-more[open] > summary { color: var(--text-primary); }
.d2-portfolio-more > summary::before { content: '▸ '; transition: transform 120ms; display: inline-block; }
.d2-portfolio-more[open] > summary::before { content: '▾ '; }

/* D2 footer + CTA */
.d2-footer {
  display: flex; justify-content: space-between; align-items: center;
  padding-top: 14px;
  border-top: 1px solid var(--border-rest);
  font-size: 11px;
  color: var(--text-tertiary);
}
/* Vivid-fill CTA — design-system Tier-3 punctuation (DS-D27).
   Visually announces "deeper data behind this click" against the
   muted surface palette of the D2 card. Auto-flips with theme. */
.d2-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  background: var(--vivid);
  color: var(--text-on-dark);
  border: none;
  border-radius: 6px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0,0,0,0.10);
  transition: transform var(--motion-short, 120ms) var(--ease, ease),
              box-shadow var(--motion-short, 120ms) var(--ease, ease),
              filter var(--motion-short, 120ms) var(--ease, ease);
}
.d2-cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(0,0,0,0.16);
  filter: brightness(1.06);
}
.d2-cta:active {
  transform: translateY(0);
  box-shadow: 0 1px 2px rgba(0,0,0,0.10);
}
.d2-cta:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--surface-page), 0 0 0 4px var(--vivid);
}

/* ── D3 modal ── */
.d3-modal {
  position: fixed; inset: 0; z-index: 2000;
  display: none;
  align-items: center;
  justify-content: center;
}
.d3-modal[aria-hidden="false"] { display: flex; }
.d3-backdrop {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.5);
}
.d3-frame {
  position: relative;
  width: 88vw; height: 90vh;
  background: var(--surface-page);
  border-radius: 10px;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.25);
  overflow: hidden;
}
@media (prefers-reduced-motion: no-preference) {
  .d3-modal[aria-hidden="false"] .d3-frame {
    animation: d3-rise 200ms cubic-bezier(0.2, 0, 0, 1);
  }
}
@keyframes d3-rise {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.d3-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 14px 24px;
  border-bottom: 1px solid var(--border-rest);
  background: var(--surface-elevated);
  flex-shrink: 0;
}
.d3-title {
  font-size: 17px; font-weight: 500;
  margin: 0; color: var(--text-primary);
}
.d3-header-actions { display: flex; gap: 18px; align-items: center; }
.d3-newtab {
  font-size: 12px;
  color: var(--text-secondary);
  text-decoration: none;
}
.d3-newtab:hover { color: var(--text-primary); }
.d3-close {
  background: none; border: none;
  font-size: 22px; line-height: 1;
  color: var(--text-tertiary);
  cursor: pointer;
  padding: 0 4px;
  font-family: inherit;
}
.d3-close:hover { color: var(--text-primary); }
.d3-body {
  padding: 24px 32px 32px;
  overflow-y: auto;
  flex: 1;
}
.d3-section { margin-top: 24px; }
.d3-placeholder {
  padding: 28px;
  background: var(--surface-elevated);
  border: 1px dashed var(--border-rest);
  border-radius: 6px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 13px;
}

/* ── D3 content sections (signal decomp, narrative, peers, activity) ── */

.d3-loading {
  padding: 8px 0;
}

/* Signal decomposition (brand) */
.d3-signal-group { margin-bottom: 14px; }
.d3-signal-summary {
  cursor: pointer;
  padding: 8px 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  list-style: none;
  border-bottom: 1px solid var(--border-rest);
}
.d3-signal-summary::-webkit-details-marker { display: none; }
.d3-signal-summary::before {
  content: '▸ ';
  display: inline-block;
  color: var(--text-tertiary);
  margin-right: 2px;
}
.d3-signal-group[open] .d3-signal-summary::before { content: '▾ '; }
.d3-signal-lens-id {
  color: var(--text-tertiary);
  letter-spacing: 0.04em;
  font-weight: 500;
}
.d3-signal-lens-pct {
  color: var(--text-tertiary);
  font-weight: 400;
  font-size: 11px;
}
.d3-signal-rows {
  padding: 8px 0 14px;
  display: flex; flex-direction: column;
  gap: 4px;
}
.d3-signal-row {
  display: grid;
  grid-template-columns: minmax(140px, 200px) 1fr 50px 110px;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  padding: 4px 0;
}
.d3-signal-name { color: var(--text-secondary); }
.d3-signal-bar-track {
  height: 6px;
  background: color-mix(in srgb, var(--text-tertiary) 8%, transparent);
  border-radius: 2px;
  overflow: hidden;
}
.d3-signal-bar {
  height: 100%;
  background: var(--text-secondary);
  border-radius: 2px;
}
.d3-signal-weight {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
  font-size: 11px;
}
.d3-signal-value {
  text-align: right;
  color: var(--text-primary);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* Agency simplified signal decomp */
.d3-agency-signal-note {
  font-size: 12px;
  color: var(--text-tertiary);
  font-style: italic;
  margin-bottom: 12px;
}
.d3-agency-lenses {
  display: flex; flex-direction: column;
  gap: 6px;
}
.d3-agency-lens {
  display: grid;
  grid-template-columns: 1fr 60px 110px;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--state-soft);
  border-left: 3px solid var(--state);
  border-radius: 0 4px 4px 0;
  font-size: 12px;
}
.d3-agency-lens-name {
  color: var(--text-secondary);
  font-weight: 500;
}
.d3-agency-lens-score {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 18px;
  font-weight: 500;
  color: var(--text-primary);
}
.d3-agency-lens-state {
  text-align: right;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--state);
}

/* Narrative */
.d3-narrative-p {
  margin: 0 0 12px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-secondary);
}
.d3-narrative-p:last-child { margin-bottom: 0; }

/* Peer comparison */
.d3-peer-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.d3-peer-table th {
  font-size: 10px; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-tertiary);
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-rest);
}
.d3-peer-table th.col-num { text-align: right; }
.d3-peer-table td {
  padding: 10px;
  border-bottom: 1px solid var(--border-rest);
  color: var(--text-primary);
}
.d3-peer-table td.col-num { text-align: right; white-space: nowrap; }
.d3-peer-row--current td { background: color-mix(in srgb, var(--vivid) 6%, transparent); }
.d3-peer-row--current td:first-child {
  border-left: 3px solid var(--vivid);
  padding-left: 8px;
  font-weight: 500;
}
.d3-peer-meta {
  font-size: 10px;
  color: var(--text-tertiary);
  font-weight: 400;
  margin-left: 4px;
}
.d3-peer-name {
  color: var(--text-primary);
  text-decoration: none;
  font-weight: 500;
}
.d3-peer-name:hover { text-decoration: underline; }
.d3-peer-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--state);
  vertical-align: middle;
}

/* Activity summary */
.d3-activity {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}
.d3-activity-col {
  display: flex; flex-direction: column;
  gap: 0;
}
.d3-activity-col-title {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 6px;
}
.d3-activity-row {
  display: grid;
  grid-template-columns: minmax(120px, 140px) 1fr;
  gap: 12px;
  font-size: 12px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--border-rest);
}
.d3-activity-row:last-child { border-bottom: none; }
.d3-activity-label { color: var(--text-tertiary); }
.d3-activity-value {
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  word-break: break-word;
}

/* Tier-system responsive (mirrors page-level rules) */
@media (max-width: 768px) {
  .tier-card { padding: 16px; }
  .d2-metrics { grid-template-columns: repeat(2, 1fr); }
  .d2-lenses { grid-template-columns: repeat(2, 1fr); }
  .d2-hero { flex-direction: column; align-items: flex-start; }
  .d2-hero-right { align-items: flex-start; }
  .d2-wf-row { grid-template-columns: 110px 1fr 80px; gap: 8px; }
  .d3-frame { width: 100vw; height: 100vh; border-radius: 0; }
  .d3-body { padding: 16px; }

  /* D3 mobile */
  .d3-signal-row {
    grid-template-columns: 1fr 80px;
    grid-template-areas: "name value" "bar bar";
    gap: 4px 8px;
  }
  .d3-signal-name { grid-area: name; }
  .d3-signal-value { grid-area: value; text-align: right; }
  .d3-signal-bar-track { grid-area: bar; }
  .d3-signal-weight { display: none; }
  .d3-activity { grid-template-columns: 1fr; }
  .d3-peer-table { font-size: 11px; }
  .d3-peer-table th, .d3-peer-table td { padding: 6px 4px; }
}


/* ═══════════════════════════════════════════════════════════════════
   BASEFORCE REFRESH · additive app-layer polish · 2026-07 audit
   ───────────────────────────────────────────────────────────────────
   Append-only. No existing rule is modified. Every primitive below has
   a stated, functional purpose — nothing decorative for its own sake.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Signed-value semantics ─────────────────────────────────────────
   Formalizes the .neg convention (used ad-hoc in seller/ledger pages)
   into shared utilities and adds the positive/zero cases data-viz.md
   already prescribes. Purpose: a number's sign is a fact; the eye
   should catch it as color before it parses the digits. */
.val-neg  { color: var(--status-error); }
.val-pos  { color: var(--status-live); }
.val-zero { color: var(--text-tertiary); }
.tnum     { font-variant-numeric: tabular-nums; }

/* ── Rank micro-bar ─────────────────────────────────────────────────
   Turns an ordinal "12 / 57" into something scannable: a hairline whose
   fill = percentile within the cohort. Purpose: on a leaderboard,
   comparative standing IS the point — a column of rank fractions hides
   it; a fill you scan down a column reveals it at a glance. Set
   --rank-pct (0–100) inline: (cohort − rank + 1) / cohort × 100.
   Restrained on purpose: 2px, accent fill, quiet track — texture until
   you look for it. */
.rank-cell { display: inline-flex; align-items: center; gap: var(--space-2); justify-content: flex-end; }
.rank-num  { font-variant-numeric: tabular-nums; }
.rank-bar {
  position: relative;
  width: 34px; height: 2px;
  border-radius: var(--radius-pill);
  background: var(--surface-divider);
  overflow: hidden;
  flex-shrink: 0;
}
.rank-bar::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--rank-pct, 0) * 1%);
  background: var(--accent);
  border-radius: var(--radius-pill);
  transition: width var(--motion-medium) var(--ease);
}
/* Top-of-cohort earns the vivid punctuation — a sanctioned Tier-3 hit. */
.rank-bar[data-lead="true"]::before { background: var(--vivid, var(--accent)); }

/* ── Signature rule as connective tissue ────────────────────────────
   The bar·dot·bar mark (.signature-rule) exists but shows once, on home.
   Promoted to quiet brand punctuation under page headers and as the
   empty/loading marker. Purpose: wayfinding + identity carried by a mark
   you feel more than notice. Reuses the existing primitive; no new
   pigment, no new token. */
.page-header .signature-rule { margin-bottom: var(--space-3); display: inline-flex; }
.bf-loading-mark { display: inline-flex; align-items: center; gap: var(--space-1); }
.bf-loading-mark .bar-sm { width: 14px; height: 0.5px; background: var(--accent); opacity: 0.5; }
.bf-loading-mark .bar-lg { width: 28px; height: 0.5px; background: var(--accent); opacity: 0.5; }
.bf-loading-mark .dot { width: 3px; height: 3px; border-radius: 50%; background: var(--accent); animation: breath var(--motion-breath) var(--ease) infinite; }


/* ═══════════════════════════════════════════════════════════════════
   SECTION ACCESS STATES + CLASSIFIED · DS-D1810 pattern lock v4
   (DESIGN - S909/PATHC-T0B · CEO-approved amendment 2026-08-01 · v3 name: Classified · v4 identity: EEID)
   ───────────────────────────────────────────────────────────────────
   Append to baseforce.css. SUPERSEDES the v3 block (bf_handoff 165).
   No new pigments, no new tokens, overrides nothing in the base system.

   Grammar — per-section visibility flag, three viewer states:

     GRANTED — normal composition.

     HIDDEN  — rendered as ABSENCE. The nav item is not emitted: no
               placeholder, no gap, no count hint. If a viewer holds
               ZERO granted sections in the Classified group, the group
               divider AND label are not emitted either — the group
               itself is absence. Markup rule, deliberately no
               [data-access="hidden"] CSS state.

     LOCKED  — retained in the system grammar for non-compartmentalized
               gated contexts (.nav-item[data-access="locked"] below).
               THE CLASSIFIED GROUP NEVER COMPOSES IT: it renders
               GRANTED + HIDDEN only — a granted officer never sees
               locked siblings, because the exception structure is
               itself private.

   The restricted page (.locked-panel) is a ROUTE-GUARD FALLBACK only
   (deep link, stale nav, mid-revocation). Denial carries zero bits:
   generic title, one canonical private route for any unauthorized
   Classified path, uniform API denial shape, no section name in any line,
   no client-persisted nav state (absence re-derived per load).
   ═══════════════════════════════════════════════════════════════════ */

/* ─── Nav group label (Classified) ────────────────────────────────
   Echoes the page-eyebrow treatment. Emitted only when the viewer
   holds ≥1 granted section in the group. */
.nav-group-label {
  padding: var(--space-3) var(--space-4) var(--space-1);
  font-size: var(--size-eyebrow);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-style: italic;
  white-space: nowrap;
  transition: opacity var(--motion-short) var(--ease);
}
.nav[data-collapsed="true"] .nav-group-label { opacity: 0; pointer-events: none; }

/* ─── Nav item · locked (system grammar; never composed by Classified) */
.nav-item[data-access="locked"] {
  color: var(--text-tertiary);
}
.nav-item[data-access="locked"]:hover {
  color: var(--text-tertiary);
  border-left-color: var(--border-rest);
}
.nav-item[data-access="locked"] .nav-lock {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  margin-left: auto;
  opacity: 0.8;
}
.nav[data-collapsed="true"] .nav-item[data-access="locked"] .nav-lock {
  opacity: 0;
}

/* ─── Restricted page (route-guard fallback) ──────────────────────
   Screen script:
     RESTRICTED (badge--ink) · "Secure authorization required."
     then the ledger readout, lines committed one-by-one behind the
     REAL access check (~700ms/line, ~2.2s total, minimum hold so it
     never flashes — async-state logic timing, NOT motion tokens):
       Identifying user…
       User EEID <eeid> identified.
       Checking access privileges…
       EEID <eeid> · restricted authorization not found.
       Access denied.            ← terminal, --status-error
   Identity renders as EEID ONLY — never email, never name. EEID is
   the canonical identity key (person.worker_record); the sterile
   system-key register is deliberate (CEO-ruled).
   No request path. No contact line. Prose is terminal by design.
   Repeat visits render the full ledger instantly (per-session cache).
   prefers-reduced-motion renders the full static ledger instantly —
   no sequence, ever. */
.locked-panel {
  max-width: 400px;
  margin: var(--space-16) auto 0;
  text-align: center;
}
.locked-panel .locked-mark {
  width: 24px;
  height: 24px;
  color: var(--text-tertiary);
  margin: 0 auto var(--space-4);
  display: block;
}
.locked-panel .locked-sub {
  font-size: var(--size-body);
  line-height: var(--line-body);
  color: var(--text-secondary);
  margin-top: var(--space-3);
}
.locked-ledger {
  margin-top: var(--space-6);
  display: inline-block;
  text-align: left;
}
.locked-ledger-line {
  font-size: var(--size-small);
  line-height: var(--line-body);
  color: var(--text-tertiary);
  letter-spacing: var(--track-body);
  opacity: 0;
}
.locked-ledger-line.is-committed {
  opacity: 1;
  transition: opacity var(--motion-short) var(--ease);
}
.locked-ledger-line--identity { color: var(--text-secondary); }
.locked-ledger-line--denied {
  color: var(--status-error);
  font-weight: var(--weight-medium);
}
@media (prefers-reduced-motion: reduce) {
  .locked-ledger-line { opacity: 1; transition: none; }
}