/*
 * portfolio-dash-d5.css
 *
 * D5 visual redesign of the /portfolio dashboard: dark, cinematic,
 * monochrome-restrained, map-as-stage command briefing. This file is loaded
 * ONLY by atlas/web/static/portfolio.html, after console.css and
 * product-tokens.css, and touches NOTHING in design-tokens.css or
 * console.css directly.
 *
 * UI reconciliation (2026-08-12): tokens and fonts now come from
 * product-tokens.css, the single product token source; this file holds
 * page layout only. The only token declarations remaining below are the
 * page-specific radius override (body.portfolio-dash's 2px instrument
 * scale, a page layout decision, not a shared token) in its own small
 * block further down.
 *
 * SCOPING. design-tokens.css defines its palette as a bare `:root { ... }`
 * block (no page-scoping selector of its own), and portfolio-dashboard.js
 * reads every paint colour via getComputedStyle(document.documentElement)
 * (see that file's cssVar()), i.e. off <html>, not off <body>. A token
 * override scoped to `body.portfolio-dash { --surface-0: ... }` would
 * therefore be invisible to the map's own paint properties: custom
 * properties do not flow from a descendant back up to an ancestor's
 * computed style. The only selector that can reach <html> is one that
 * matches something at or above <html> itself, and this file is never
 * loaded by any other route (grep confirms body.portfolio-dash exists
 * nowhere but portfolio.html, and this file is linked from nowhere but
 * portfolio.html), so a bare `:root` override here is safe: it can never
 * be evaluated against /room, /coverage, /gallery, /runs, or /methodology,
 * because those pages' HTML never requests this stylesheet. The scoping
 * boundary is "which page loads this file", not "which selector it uses
 * inside that file". See the D5 build report for the byte-identical-text
 * and unchanged-other-pages proof this claim rests on.
 *
 * D9 command-view palette (now owned by product-tokens.css). Amber
 * #E8A33D is removed entirely from this page: --accent-600 is the UI accent
 * (eyebrows, links, active toggle state, focus rings, quiet ghost-outline
 * buttons), used sparingly and never as an outcome colour. That accent is
 * now BRASS, #856410 on the light default and #D9A441 on the dark arm; the
 * ice steel cyan #7FAFC6 named here was retired by the light-first reskin.
 * This page reads the token, so it followed without an edit. The "susceptibility indicated" outcome has its own
 * dedicated token, --outcome-indicated, deep desaturated signal red
 * #C4523E, used ONLY for that one outcome (map cells, halos, chips,
 * legend, colour dots, the outcome split figure). Everywhere else in the
 * console (nav, other pages) --accent-600 remains the existing blue,
 * because product-tokens.css's dark skin block is what all migrated pages
 * now share.
 */

body.portfolio-dash {
  --radius-sm: 2px;
  --radius-md: 2px;
  --radius-lg: 2px;
  --radius-full: 2px;
  font-family: var(--font-body);
  font-variant-numeric: tabular-nums;
}

body.portfolio-dash,
body.portfolio-dash input,
body.portfolio-dash button {
  font-variant-numeric: tabular-nums;
}

/* Small-caps-style panel/eyebrow labels: uppercase + wide tracking reads as
   the "small caps" language of the spec without relying on font-variant
   (IBM Plex Sans ships no true small-caps glyphs). Every selector in this
   list was checked against an untouched checkout of the prod-freeze
   commit (via getComputedStyle) and is ALREADY uppercase there today
   (.rail-heading/.insp-heading/.db-metric-label/.bs-title/.diff-strip-
   label/.insp-field-label/thead th via console.css's own rules,
   .cutoff-disclosure-field-label via portfolio.html's own inline style,
   never actually rendered by either fixture but authored uppercase in
   both), so this rule only restyles size/weight/colour, never casing.

   .lt-label and .const-legend-label are DELIBERATELY excluded: verified
   against the same untouched checkout, both compute text-transform: none
   originally (.lt-label inherits uppercase from console.css's bare
   `label {}` selector by mistake in an earlier draft of this file's OWN
   override, since caught and reverted; .const-legend-label was never
   uppercase to begin with). Restyled separately below without touching
   case.

   #state-toggle-caption is DELIBERATELY INCLUDED as of the approved wording
   change (iteration 2, task 3): the caption now always holds the fixed
   literal "Evidence as of" (set once in renderStateToggleDates, portfolio-
   dashboard.js), never dossier-derived text, so uppercasing it here is a
   presentation-only small-caps treatment of a string this file's own author
   controls, not a re-casing of data. See its own rule further below. */
body.portfolio-dash .rail-heading,
body.portfolio-dash .insp-heading,
body.portfolio-dash .db-metric-label,
body.portfolio-dash .bottom-strip-head .bs-title,
body.portfolio-dash .diff-strip-label,
body.portfolio-dash .insp-field-label,
body.portfolio-dash .cutoff-disclosure-field-label,
body.portfolio-dash table.table-dense thead th {
  font-size: var(--text-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-2);
  font-weight: var(--font-regular);
}
/* Restyled (size/weight/colour) but NOT re-cased: text-transform is left
   at whatever console.css's global `label` rule already computes
   (uppercase), so rendered text is unchanged from original prod. */
body.portfolio-dash .lt-label {
  color: var(--on-surface-0);
  font-size: 12px;
  letter-spacing: 0.04em;
  font-weight: var(--font-regular);
}

/* Integrity chip text: same small-caps treatment, but the colour stays
   whatever .integrity-chip/.integrity-chip.verified/.failed already sets
   (primary white when verified, danger when failed), so this rule only
   ever touches size/tracking/case, never colour. */
body.portfolio-dash #db-integrity-text {
  font-size: var(--text-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: var(--font-medium);
}

/* Every digit reads in the mono face, tabular. Applies broadly rather than
   enumerating every number-bearing element, since the dashboard's whole
   premise is instrumentation density. */
body.portfolio-dash .db-metric-value,
body.portfolio-dash .db-outcome-split,
body.portfolio-dash .book-summary-grid dd,
body.portfolio-dash .insp-field-value.mono,
body.portfolio-dash .cutoff-disclosure-date,
body.portfolio-dash .diff-chip .dc-delta,
body.portfolio-dash .evidence-mini-item .em-id,
body.portfolio-dash .evidence-point-item .ep-id,
body.portfolio-dash #db-book-name .db-book-id,
body.portfolio-dash table.table-dense .num,
body.portfolio-dash #bottom-table-body td:first-child,
body.portfolio-dash #bottom-table-body td:nth-child(2),
body.portfolio-dash #bottom-table-body td:nth-child(3) {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

body.portfolio-dash :focus-visible {
  outline: 2px solid var(--accent-600);
  outline-offset: 2px;
}

/* ================================================================
   LAYOUT RECOMPOSITION: the map becomes the base layer of the whole
   viewport; every other region (top bar, verdict banner, diff strip,
   rail, inspector, bottom strip) becomes an absolutely positioned glass
   panel floating over it, instead of a flex sibling that letterboxes the
   map into a rectangle between opaque bars. Same elements, same DOM
   order, same event handlers; only position/sizing changes.
   ================================================================ */

/* #dash-shell stays flex/column (unchanged from portfolio.html's own
   <style>): the top bar, verdict banner, and diff strip remain normal
   flex children so they stack and push #dash-body down by their own
   real height, whatever that is with the diff strip shown or hidden.
   Only #dash-body's OWN row (map/rail/inspector) becomes a positioning
   context: #dash-body keeps its existing flex:1 1 auto (fills whatever
   height remains below the stack) but becomes position:relative so its
   three children can layer via position:absolute WITHIN that remaining
   box, rather than my having to hardcode the stack's height anywhere. */
/* D9 command view: MAIN SPLIT, a real flex row, not a floating-panel
   overlay. Map fills the left ~60%, the right rail (#command-rail /
   #dash-inspector, one visible at a time, see setInspectorPanelVisible in
   portfolio-dashboard.js) is a normal ~40%-wide sibling column, not
   position:absolute over the map. #dash-rail (the OLD left rail: Layers
   checkboxes, full legend, book summary) is RELOCATED out of #dash-body
   entirely, into #below-fold (see portfolio.html's DOM and that section's
   own rules further down): it is no longer part of this row at all, so it
   carries no rule here. #dash-bottom is likewise relocated to #below-fold,
   so it carries no rule here either; see mapBottomPanelOcclusionPx's own
   D9 note in portfolio-dashboard.js for why that specific move needed a
   matching JS change (the panel no longer occludes the map, so the
   landing-camera padding must stop reserving room for it). */
body.portfolio-dash #dash-body {
  display: flex;
  min-height: 0;
}

body.portfolio-dash #dash-map-col {
  flex: 1 1 60%;
  position: relative;
  min-width: 0;
}

/* Cinematic vignette: a very quiet radial darkening toward the frame edges
   so the map reads as a stage rather than a flat raster. Decorative only:
   pointer-events: none so it never intercepts a map click/drag. */
body.portfolio-dash #dash-map-col::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 5;
  background: radial-gradient(
    ellipse 85% 75% at 50% 45%,
    transparent 55%,
    rgba(10, 13, 16, 0.35) 100%
  );
}

/* The right rail column: #command-rail and #dash-inspector both live here,
   sharing the exact same footprint, one hidden via .is-hidden at any time
   (see setInspectorPanelVisible). A plain flex sibling of the map column,
   not floating over it, so the map's own ~60% is genuinely uncovered
   width, not width shared with an overlay. */
body.portfolio-dash #command-view-rail-col {
  flex: 0 0 400px;
  position: relative;
  border-left: 1px solid var(--border-subtle);
  background: var(--glass-panel-strong);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  overflow-y: auto;
}
body.portfolio-dash #command-rail,
body.portfolio-dash #command-view-rail-col #dash-inspector {
  padding: var(--space-3);
}
body.portfolio-dash #command-view-rail-col #dash-inspector {
  border-left: none;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  overflow-y: visible;
  height: auto;
}

/* ================================================================
   TOP BAR: thin floating instrument strip over the map, glass, not a
   solid bar. Every rule below re-skins portfolio.html's own IDs/classes;
   nothing here is a new element (see the D5 build report for the full
   element inventory).
   ================================================================ */

body.portfolio-dash #dash-topbar {
  border-bottom: none;
  background: var(--glass-panel-strong);
  backdrop-filter: blur(16px) saturate(1.1);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
  border: 1px solid var(--border-subtle);
  border-radius: 2px;
  margin: 10px 10px 0 10px;
  box-shadow: var(--shadow-md);
  position: relative;
  z-index: 40;
}

body.portfolio-dash #dash-topbar .db-brand svg path,
body.portfolio-dash #dash-topbar .db-brand svg circle,
body.portfolio-dash #dash-topbar .db-brand svg rect {
  opacity: 0.85;
}

body.portfolio-dash .db-book-name {
  font-weight: var(--font-medium);
  letter-spacing: -0.005em;
  max-width: 34ch;
}

/* The demoted parenthetical/metadata portion (see renderTopBar's D5 split):
   a quiet mono chip, never competing with the human title beside it. */
body.portfolio-dash .db-book-name .db-book-id {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: var(--font-regular);
  color: var(--on-surface-2);
  letter-spacing: 0;
  margin-left: 2px;
}

/* Integrity chip: a quiet hairline-bordered badge with a steady glow dot,
   not a green pill. The dot's colour still communicates verified/pending/
   failed (never colour-alone: the text label right beside it always says
   so too), but the CHROME is identical across all three states. */
body.portfolio-dash .integrity-chip {
  background: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: 2px;
  color: var(--on-surface-1);
  gap: 7px;
  padding: 5px 10px 5px 9px;
}
body.portfolio-dash .integrity-chip svg { display: none; }
body.portfolio-dash .integrity-chip::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--on-surface-2);
  flex-shrink: 0;
}
body.portfolio-dash .integrity-chip.verified {
  background: transparent;
  color: var(--on-surface-0);
  border-color: var(--border-strong);
}
body.portfolio-dash .integrity-chip.verified::before {
  background: var(--accent-600);
  box-shadow: 0 0 0 3px var(--accent-soft);
  animation: d5-glow-pulse 2.6s ease-in-out infinite;
}
body.portfolio-dash .integrity-chip.failed {
  background: transparent;
  color: var(--danger-600);
  border-color: var(--danger-600);
}
body.portfolio-dash .integrity-chip.failed::before { background: var(--danger-600); }

@keyframes d5-glow-pulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--accent-soft); }
  50% { box-shadow: 0 0 0 5px var(--accent-soft); }
}

body.portfolio-dash .db-metric {
  border-left: 1px solid var(--border-subtle);
}
body.portfolio-dash .db-metric-value {
  font-family: var(--font-mono);
  font-weight: var(--font-medium);
  font-size: var(--text-body-m);
  letter-spacing: 0;
}
body.portfolio-dash .db-outcome-split .part-susceptibility_indicated { color: var(--outcome-indicated); }
body.portfolio-dash .db-outcome-split .part-susceptibility_not_indicated { color: var(--success-700); }
body.portfolio-dash .db-outcome-split .part-mixed_evidence { color: var(--outcome-mixed); }
body.portfolio-dash .db-outcome-split .part-insufficient_data { color: var(--outcome-insufficient); }

/* Insufficient-data outcome chip (cell inspector, asset inspector): console.css's
   shared .chip-neutral is a generic "no strong signal" style reused elsewhere in
   the console, so it stays untouched; this page-scoped override targets the
   outcome-insufficient_data class the JS also sets on that one chip, so only
   THIS outcome gets the dedicated graphite token, not every neutral chip in the
   product. */
body.portfolio-dash .chip.outcome-insufficient_data {
  background: var(--outcome-insufficient-soft);
  color: var(--outcome-insufficient);
  border-color: var(--outcome-insufficient);
}

/* ================================================================
   TWO-STATE TOGGLE: the hero instrument (approved wording change,
   iteration 2, task 3). Same DOM, same events (state-toggle-a/-b clicks,
   aria-pressed), restyled as a labelled instrument rather than a generic
   pill switch. Each button's PRIMARY label is now an ISO date read
   straight off that state's own signed bundle (renderStateToggleDates in
   portfolio-dashboard.js), set in mono; the caption above reads "Evidence
   as of" in small caps. The A/B letters are demoted to a small secondary
   marker (.stb-letter) beside each date, kept for aria/back-compat only.
   ================================================================ */

body.portfolio-dash #state-toggle {
  gap: 10px;
  padding-left: 12px;
  margin-left: 4px;
  border-left: 1px solid var(--border-subtle);
}

body.portfolio-dash .state-toggle-track {
  background: var(--surface-2);
  border: 1px solid var(--border-subtle);
  border-radius: 2px;
  padding: 2px;
}

body.portfolio-dash .state-toggle-btn {
  border-radius: 2px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--on-surface-1);
  padding: 6px 14px;
  transition: color 200ms ease;
}
/* Active state: the thumb underneath (below) is the accent fill; the
   active button's own text takes --on-accent (near-black on dark brass,
   white on the darker light brass) so it stays legible sitting on that
   thumb, the same "one accent colour, text adapts for contrast"
   treatment the rest of this file uses (e.g. .state-toggle-btn.active in
   portfolio.html's own base rule). */
body.portfolio-dash .state-toggle-btn.active { color: var(--on-accent); font-weight: var(--font-semibold); }

/* opacity 0.85, matching portfolio.html's own base rule: WCAG AA needs
   4.5:1, and this D5 skin's --on-surface-1 is itself only 0.60 alpha, so a
   more muted opacity here compounds below that floor against the track's
   --surface-2 background. */
body.portfolio-dash .state-toggle-btn .stb-letter {
  font-family: var(--font-mono);
  opacity: 0.85;
}

body.portfolio-dash .state-toggle-thumb {
  background: var(--accent-600);
  border-radius: 2px;
  transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Small-caps "Evidence as of" caption: same --text-micro/0.08em/uppercase
   treatment as the other panel-eyebrow labels above, kept in its own rule
   (rather than folded into that shared selector list) because this element
   also carries the mono face the rest of the toggle uses, which those
   labels do not. */
body.portfolio-dash #state-toggle-caption {
  color: var(--on-surface-2);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: var(--font-regular);
}

/* ================================================================
   VERDICT BANNER: single type colour, mono numerals, one thin cyan
   hairline on the left edge. EXACT text unchanged; emphasis by weight/
   size only, never a second colour in the prose.
   ================================================================ */

body.portfolio-dash #verdict-banner {
  background: var(--glass-panel-soft);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: none;
  border-left: 2px solid var(--accent-600);
  margin: 8px 10px 0 10px;
  border-radius: 2px;
  padding: 14px 20px;
}

body.portfolio-dash .verdict-headline {
  color: var(--on-surface-0);
  font-family: var(--font-body);
  font-weight: var(--font-regular);
  font-size: var(--text-body-m);
  letter-spacing: -0.005em;
}

/* All three "coloured phrase" spans collapse to the SAME primary white
   TEXT COLOUR (keep total chroma low, D9 palette order item 1): the only
   colour signal in the banner is now the small .vh-dot prefix each
   sentence carries (see portfolio.html's own .vh-dot rule and
   renderVerdictBanner), not the sentence text itself. */
body.portfolio-dash .verdict-headline .vh-indicated,
body.portfolio-dash .verdict-headline .vh-mixed,
body.portfolio-dash .verdict-headline .vh-insufficient {
  color: var(--on-surface-0);
  font-weight: var(--font-semibold);
}

/* ================================================================
   DIFF STRIP: mono numbers, quiet text, hairline chips.
   ================================================================ */

/* D9: #diff-strip lives inside #command-rail now (a rail block, not a
   full-width strip above the map), so it takes no independent margin/
   border/background of its own; portfolio.html's own base rule (a plain
   flex-direction: column stack) is the whole of its layout here. */
body.portfolio-dash #diff-strip {
  background: transparent;
  border: none;
  margin: 0;
  padding: 0;
}

body.portfolio-dash .diff-chip {
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: 2px;
  color: var(--on-surface-1);
  transition: border-color 180ms ease;
}
body.portfolio-dash .diff-chip:hover { border-color: var(--accent-600); }
body.portfolio-dash .diff-chip .dc-delta.positive { color: var(--accent-600); }
body.portfolio-dash .diff-chip .dc-delta.negative { color: var(--success-700); }
body.portfolio-dash .diff-chip .dc-only-in { color: var(--accent-600); }
body.portfolio-dash .diff-strip-symmetry-warning { color: var(--accent-600); }

/* ================================================================
   RIGHT RAIL COLUMN: a floating glass panel over the map's right ~40%
   (#command-view-rail-col itself carries the glass treatment, see that
   selector's own rule near the top of this file); #command-rail and
   #dash-inspector, nested inside it, take no background/blur/border of
   their own here (see that selector's own D9 rule for the reset).
   #dash-rail is BELOW THE FOLD now (D9): it is a plain in-flow panel
   there (portfolio.html's own #below-fold #dash-rail rule gives it a
   simple border/background), not a floating glass panel, so it carries
   no rule in this block any more.
   ================================================================ */

body.portfolio-dash .rail-heading {
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border-subtle);
}

/* Custom checkboxes: 14px, 2px radius, cyan check on active. Zero
   default browser checkbox chrome. */
body.portfolio-dash .layer-toggle input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  border: 1px solid var(--border-strong);
  border-radius: 2px;
  background: var(--surface-2);
  margin: 0;
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color 150ms ease, background 150ms ease;
}
body.portfolio-dash .layer-toggle input[type="checkbox"]:hover {
  border-color: var(--on-surface-1);
}
body.portfolio-dash .layer-toggle input[type="checkbox"]:checked {
  background: var(--accent-600);
  border-color: var(--accent-600);
}
body.portfolio-dash .layer-toggle input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 3px;
  height: 7px;
  border: solid var(--on-accent);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
body.portfolio-dash .layer-toggle input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent-600);
  outline-offset: 2px;
}

body.portfolio-dash .lt-swatch {
  border-radius: 2px;
}

/* Legend swatches align to a strict grid: fixed column for the swatch,
   consistent gap, so every row's label starts at the same x. */
body.portfolio-dash .layer-toggle.legend-row {
  display: grid;
  grid-template-columns: 14px 1fr;
  gap: 8px;
  align-items: start;
  padding: 7px 4px;
}
body.portfolio-dash .layer-toggle.legend-row .lt-swatch {
  margin-top: 3px;
}

/* #db-book-summary carries long values (a full portfolio id like
   "portfolio-demo-tw-four-counties-state-b", long wrapped labels like
   "Unknown coverage cells (no evidence source)"), where the base grid
   (console.css: 1fr auto, dd right-aligned on the same row as dt)
   squeezes the label into an awkward wrap. Stacked label-over-value
   layout for THIS panel only; #evidence-sources-body keeps the base
   side-by-side grid (its labels are short and its values are short
   counts, which read better inline). Same dt/dd text content in both
   cases, just two different, deliberately chosen layouts. */
body.portfolio-dash #db-book-summary {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
body.portfolio-dash #db-book-summary dt {
  margin-bottom: 2px;
}
body.portfolio-dash #db-book-summary dd {
  font-weight: var(--font-medium);
  text-align: left;
  word-break: break-word;
}
body.portfolio-dash #evidence-sources-body dd {
  font-weight: var(--font-medium);
}

body.portfolio-dash .cutoff-disclosure {
  border-color: var(--accent-600);
  background: var(--surface-2);
  border-radius: 2px;
}
body.portfolio-dash .cutoff-disclosure-heading { color: var(--accent-600); }

body.portfolio-dash .insp-title {
  font-family: var(--font-body);
  font-weight: var(--font-medium);
  letter-spacing: -0.01em;
}

/* Book-summary panel headline (art-direction fix, iteration 2): the SAME
   display-name-mapped-title-plus-demoted-id treatment renderTopBar/
   .db-book-id already applies, consuming the existing display_name mapping
   rather than a new one; see showBookLevelInspector's own note. Only
   present when a display_name is mapped (falls back to the single
   .insp-title line otherwise, byte-for-byte pre-existing behaviour,
   including its own unmodified margin-bottom: var(--space-2) from
   portfolio.html's base rule: the tighter spacing below only engages when
   .insp-book-id actually follows). */
body.portfolio-dash .insp-title:has(+ .insp-book-id) {
  margin-bottom: 2px;
}
body.portfolio-dash .insp-book-id {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: var(--font-regular);
  color: var(--on-surface-2);
  letter-spacing: 0;
  margin-bottom: var(--space-2);
}

body.portfolio-dash .judgment-form-list li {
  border-left-color: var(--accent-600);
}
body.portfolio-dash .judgment-form-list li .jf-tag {
  color: var(--accent-600);
}

body.portfolio-dash .insp-note {
  border: 1px solid var(--border-subtle);
  border-style: solid;
  background: var(--surface-2);
  border-radius: 2px;
}

body.portfolio-dash .evidence-point-item.selected {
  background: var(--accent-soft);
  border-color: var(--accent-600);
}

/* ================================================================
   MAP CANVAS: fills edge to edge; panels float over it. No letterboxed
   rectangle. The base tile filter itself is unchanged (still the CARTO
   dark-inversion filter from portfolio.html's own <style>); this section
   only affects the DOM chrome layered on top of the canvas.
   ================================================================ */

body.portfolio-dash #dash-body {
  gap: 0;
}

body.portfolio-dash .maplibregl-popup-content {
  background: var(--glass-panel-strong);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border-subtle);
  border-radius: 2px;
  box-shadow: var(--shadow-md);
}

/* D9: #dash-map-col is now a plain flex sibling of #command-view-rail-col
   (a real ~60/40 split, see portfolio-dash-d5.css's own #dash-body note),
   not full-bleed underneath floating panels on both sides, so MapLibre's
   native top-right/bottom-right corner anchoring is clear of every panel
   by construction; the CSS repositioning this section used to need (both
   corners relocated left to dodge the OLD absolutely positioned rail) is
   no longer necessary and is removed. Only cosmetic (token glass) styling
   remains. */
body.portfolio-dash .maplibregl-ctrl-group {
  background: var(--glass-panel-strong);
  border: 1px solid var(--border-subtle);
  border-radius: 2px;
  box-shadow: none;
}
body.portfolio-dash .maplibregl-ctrl-group button {
  background: transparent;
}
body.portfolio-dash .maplibregl-ctrl-group button + button {
  border-top: 1px solid var(--border-subtle);
}
body.portfolio-dash .maplibregl-ctrl-attrib {
  background: var(--glass-panel-soft);
  border-radius: 2px;
}
body.portfolio-dash .maplibregl-ctrl-attrib a { color: var(--on-surface-1); }

/* ================================================================
   BOTTOM STRIP: instrument styling; mono ids; full reason text with a
   2-line clamp instead of mid-sentence truncation (see the JS change in
   renderBottomStrip for why the string itself is no longer sliced).
   ================================================================ */

/* D9: #dash-bottom lives below the fold now, in normal document flow (see
   portfolio.html's own #below-fold #dash-bottom rule for its
   border/background there), not a floating glass panel over the map, so
   this no longer needs its own backdrop-filter/margin/max-height; the
   panel simply takes its natural content height within #below-fold's
   layout. */
body.portfolio-dash #dash-bottom {
  border-top: none;
  border-radius: 2px;
}
body.portfolio-dash #dash-bottom.collapsed { max-height: 38px; }

body.portfolio-dash .bottom-strip-head {
  border-bottom: 1px solid var(--border-subtle);
}

body.portfolio-dash table.table-dense thead th {
  background: transparent;
  border-bottom: 1px solid var(--border-subtle);
}

body.portfolio-dash table.table-dense tbody tr:nth-child(even) {
  background: var(--zebra-stripe);
}

body.portfolio-dash table.table-dense tbody tr.row-clickable:hover,
body.portfolio-dash #bottom-table-wrap table.table-dense tbody tr:hover {
  background: var(--accent-soft);
}

body.portfolio-dash #bottom-table-wrap table.table-dense tbody tr.selected {
  background: var(--accent-soft);
  outline: 1px solid var(--accent-600);
  outline-offset: -1px;
}

/* Reason column (approved fix, iteration 2, task 4): renders the FULL
   sentence, wrapped across however many lines it needs. Only a row long
   enough to exceed 4 lines is clamped at all, and that clamp always ships
   with a visible "Show more" affordance (wireReasonExpandButtons in
   portfolio-dashboard.js) rather than silently cutting the sentence off
   mid-word with no way to read the rest. The full text remains reachable
   via the browser's own find-in-page and via document.body.innerText
   regardless of clamp state. */
body.portfolio-dash span.reason-cell {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.4;
  max-width: 46ch;
}
body.portfolio-dash span.reason-cell.expanded {
  display: block;
  -webkit-line-clamp: unset;
  overflow: visible;
}
body.portfolio-dash td.reason-td {
  vertical-align: top;
}
body.portfolio-dash .reason-expand-btn {
  display: block;
  margin-top: 4px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent-600);
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: var(--font-medium);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
body.portfolio-dash .reason-expand-btn:hover { text-decoration-thickness: 2px; }
body.portfolio-dash .reason-expand-btn.is-hidden { display: none; }

/* Column widths: the Asset column's mono ids ("tw_four_counties-00350")
   were wrapping onto 2 lines at the base table's default proportions
   while the Cell column sat far wider than its own content needed.
   Rebalanced so the mono id fits one line without shrinking Reason's
   own room; table-layout: fixed makes these percentages the actual
   column widths rather than content-driven auto-sizing. */
body.portfolio-dash #dash-bottom table.table-dense {
  table-layout: fixed;
}
body.portfolio-dash #dash-bottom table.table-dense th:nth-child(1),
body.portfolio-dash #dash-bottom table.table-dense td:nth-child(1) {
  width: 32px;
}
body.portfolio-dash #dash-bottom table.table-dense th:nth-child(2),
body.portfolio-dash #dash-bottom table.table-dense td:nth-child(2) {
  width: 235px;
}
body.portfolio-dash #dash-bottom table.table-dense th:nth-child(3),
body.portfolio-dash #dash-bottom table.table-dense td:nth-child(3) {
  width: 234px;
}
/* Asset/Cell ids: single-line mono ids fit their (now-widened) column at
   this viewport; if a future longer id ever overflowed, this wraps to a
   second line rather than truncating (ids are identifiers, not prose, so
   this is a materially different case from the Reason column above, but
   still never drops characters silently). */
body.portfolio-dash #dash-bottom table.table-dense td:nth-child(2),
body.portfolio-dash #dash-bottom table.table-dense td:nth-child(3) {
  overflow-wrap: break-word;
}
body.portfolio-dash #dash-bottom table.table-dense th:nth-child(4),
body.portfolio-dash #dash-bottom table.table-dense td:nth-child(4) {
  width: auto;
}

/* ================================================================
   ENTRANCE: one orchestrated load sequence, under 900ms total.
   Map fades in, then panels slide 12px + fade with a stagger, then the
   banner text reveals. prefers-reduced-motion is handled globally by
   console.css's own *,*::before,*::after animation/transition-duration
   kill switch, so no separate media query is needed here: every
   animation/transition below already collapses to ~instant under it.
   ================================================================ */

body.portfolio-dash #dash-map-col {
  animation: d5-fade-in 400ms ease-out both;
}

body.portfolio-dash #dash-topbar {
  animation: d5-slide-in 380ms cubic-bezier(0.16, 1, 0.3, 1) 120ms both;
}
body.portfolio-dash #verdict-banner {
  animation: d5-slide-in 380ms cubic-bezier(0.16, 1, 0.3, 1) 260ms both;
}
/* D9: #dash-rail and #dash-bottom moved below the fold (off-screen at
   load, see portfolio.html's own #below-fold section), so their entrance
   slide-in is dropped: an animation on a not-yet-visible element is inert
   at best. #command-view-rail-col (the new right-rail column, holding
   #command-rail and #dash-inspector) takes the slide-in instead, the same
   stagger position the old #dash-rail held. */
body.portfolio-dash #command-view-rail-col {
  animation: d5-slide-in 380ms cubic-bezier(0.16, 1, 0.3, 1) 340ms both;
}

@keyframes d5-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes d5-slide-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}
