/* ──────────────────────────────────────────────────────────────────────
   device-detail.css — chrome for the device-detail redesign.
   Loaded only by flask_app/templates/devices/device_detail.html.
   PR-3 of the device-detail refactor — see
   memory-bank/tasks/feature-v0.8.0-stage-1/analysis/phase-1/
   DEVICE-DETAIL-PLAN.md.

   Token hygiene:
   - Brand + status colours use --color-* tokens already defined in
     flask_app/static/css/design-system.css (loaded globally).
   - Grayscale uses --device-gray-*, scoped to .device-detail-page so it
     can't leak into other pages. Values match the --cp-gray-* scale
     (compliance/_tokens.css) — when design-system.css grows a global
     gray scale, this block can be deleted and the var() refs will fall
     through to the global definition.
   ────────────────────────────────────────────────────────────────────── */

.device-detail-page,
.devices-page,
.gateways-page,
.gateway-detail-page,
.tenants-page,
.positioning-page,
.network-health-page {
    /* SINGLE source of truth for v0.8.0 analyze-section page chrome
       (device-detail, devices list, gateways list, gateway-detail,
       standalone positioning).
       All pages share:
         - the same --device-gray-* scale (matches --cp-gray-*)
         - the same 88rem max-width (matches compliance .fw-page +
           .compliance-landing)
         - the same left-aligned padding
       The .dd-* component rules (.dd-hero, .dd-kpi*, .dd-stage, .dd-rail*)
       below are not scoped to the wrapper — they apply wherever the
       markup appears. Add new analyze-section pages to this selector
       to inherit the wrapper styling. */
    --device-gray-50: var(--bg-page);
    --device-gray-100: var(--status-neutral-bg);
    --device-gray-200: var(--border-base);
    --device-gray-300: var(--border-medium);
    --device-gray-400: var(--border-strong);
    --device-gray-500: var(--fg-4);
    /* Scoped to .network-health-page, NOT :root — every consumer outside
       that scope MUST keep a fallback or the declaration is dropped. */
    --device-gray-600: var(--fg-3);
    --device-gray-700: var(--fg-body-strong);
    --device-gray-800: var(--fg-heading);
    --device-gray-900: var(--fg-1);

    /* Width + padding come from design-system.css :root variables so
       a single edit there updates every page (compliance + analyze). */
    max-width: var(--page-max-width);
    margin: 0;
    padding: var(--page-padding);
    color: var(--device-gray-900);
    font-family: var(--font-base);
    font-size: var(--fs-base);
    line-height: var(--lh-normal);
}

.device-detail-page h1,
.device-detail-page h2,
.device-detail-page h3,
.devices-page h1,
.devices-page h2,
.devices-page h3,
.gateways-page h1,
.gateways-page h2,
.gateways-page h3,
.gateway-detail-page h1,
.gateway-detail-page h2,
.gateway-detail-page h3,
.tenants-page h1,
.tenants-page h2,
.tenants-page h3,
.positioning-page h1,
.positioning-page h2,
.positioning-page h3,
.network-health-page h1,
.network-health-page h2,
.network-health-page h3 {
    font-family: var(--font-display);
    margin: 0;
}

/* ── Hero header ────────────────────────────────────────────────────── */
.dd-hero {
    background: var(--bg-card);
    border: 1px solid var(--device-gray-200);
    border-radius: 0.75rem;
    padding: 1.125rem 1.25rem;
    margin-bottom: 1rem;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.dd-hero-title-block {
    min-width: 0;
}

.dd-hero-title {
    /* Canonical page-title typography — Ubuntu Sans Medium 30px, matching
       ``.patitur-main-page-title`` (design-tokens.css §h1) in font/size/weight.
       COLOUR intentionally diverges: detail-hero titles use near-black
       ``--fg-1`` rather than brand-green, because the big green wordmark read
       as a link (link green ``var(--link-internal-fg)`` is a near-identical shade). Keeping the
       type but dropping the green removes that "is this heading clickable?"
       ambiguity while list-page titles keep the brand-green macro. Scoped,
       deliberate deviation from the one-colour title convention — not drift.
       Layout properties (inline-flex + gap + flex-wrap) stay so the title sits
       beside the EUI badge / pill cluster inside the hero card. */
    font-family: var(--font-display);
    font-weight: var(--fw-medium);
    font-size: var(--fs-2xl);
    line-height: var(--lh-snug); /* 39px. Was a bare 28px — a line box SMALLER
       than the 30px text, so a wrapping title overlapped its own next line.
       Matches the h2 rule's --lh-snug and what the four `leading-tight` call
       sites were reaching for; those never applied, because this stylesheet
       loads after Tailwind's CDN and won on source order. */
    color: var(--fg-1);
    letter-spacing: normal;
    margin: 0;
    display: inline-flex;
    align-items: baseline;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* Slideout / detail-panel header (product-model panel, application devices
   slideout). One shared class replacing the identical ``text-2xl font-bold
   text-[var(--fg-1)]`` string that was hand-rolled in two templates — the
   title/header consolidation (v0.11.0 Finding 1). Keeps the current size/weight
   (24px bold near-black) so the migration is visually a no-op; pair it with the
   ``truncate`` utility on the element for the ellipsis. */
.dd-slideout-title {
    font-family: var(--font-display);
    font-weight: var(--fw-bold);
    font-size: var(--fs-xl);
    line-height: var(--lh-snug);
    color: var(--fg-1);
    margin: 0;
}

.dd-hero-eui {
    font-family:
        ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--device-gray-500);
}

.dd-hero-pills {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-top: 0.5rem;
}

.dd-hero-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    flex-shrink: 0;
}

/* ── Action chip (matches device-detail mockup chip pattern) ────────── */
.dd-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    border-radius: 9999px;
    font-size: var(--fs-sm);
    font-weight: 500;
    border: 1px solid var(--device-gray-300);
    color: var(--device-gray-700);
    background: var(--bg-card);
    cursor: pointer;
    font-family: inherit;
    transition:
        background-color 0.12s,
        border-color 0.12s;
}
.dd-chip:hover {
    background: var(--device-gray-50);
    border-color: var(--device-gray-400);
}
.dd-chip svg {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}

/* ── Pills (re-uses the production --color-status-pill-* token pairs) */
.dd-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.1875rem 0.625rem;
    border-radius: 9999px;
    font-size: var(--fs-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
}
/* Modifier — slightly de-emphasized weight for status/window pills
   layered next to a panel title (Signal-tab window count, Map-tab
   gateways-heard tag). Replaces the inline ``style="font-weight: 500;"``
   pattern that used to live on those call sites. */
.dd-pill-medium {
    font-weight: 500;
}
.dd-pill-online {
    background: var(--color-status-pill-bg-online);
    color: var(--color-status-pill-text-online);
}
.dd-pill-offline {
    background: var(--color-status-pill-bg-danger, var(--status-danger-bg));
    color: var(--color-status-pill-text-danger);
}
.dd-pill-warning {
    background: var(--color-status-pill-bg-warning, var(--status-warning-bg));
    color: var(--color-status-pill-text-warning);
}
.dd-pill-info {
    background: var(--color-status-pill-bg-info, var(--status-info-bg));
    color: var(--color-status-pill-text-info);
}
.dd-pill-neutral {
    background: var(--color-status-pill-bg-neutral, var(--status-neutral-bg));
    color: var(--color-status-pill-text-neutral, var(--fg-4));
}
/* Fallback for any importance value without a dedicated rule (e.g. the
   `Unknown` enum member, or a future importance level). Without this, an
   unmapped `dd-pill-importance-*` renders as a shapeless, colorless pill
   because base `.dd-pill` sets no background/color. The specific
   -low/-medium/-high/-critical rules below override this by source order. */
[class*="dd-pill-importance-"] {
    background: var(--color-status-pill-bg-neutral, var(--status-neutral-bg));
    color: var(--color-status-pill-text-neutral, var(--fg-4));
}
.dd-pill-importance-low {
    background: var(--color-status-pill-bg-info, var(--status-info-bg));
    color: var(--color-status-pill-text-info);
}
.dd-pill-importance-medium {
    background: var(--color-status-pill-bg-warning, var(--status-warning-bg));
    color: var(--color-status-pill-text-warning);
}
.dd-pill-importance-high {
    background: var(--color-status-pill-bg-warning, var(--status-warning-bg));
    color: var(--color-status-pill-text-warning);
}
.dd-pill-importance-critical {
    background: var(--color-status-pill-bg-danger, var(--status-danger-bg));
    color: var(--color-status-pill-text-danger);
}
/* v0.9.2 device State — lifecycle pill (operational / pending) + reason chip
   (warn / error / benign). Reuses the status-pill colour tokens. Orthogonal to
   the connectivity badge; raw enum values never rendered (mapped in
   constants/devices.py via DeviceStateView). */
.dd-pill-state-operational {
    background: var(--color-status-pill-bg-online);
    color: var(--color-status-pill-text-online);
}
.dd-pill-state-pending {
    background: var(--color-status-pill-bg-warning, var(--status-warning-bg));
    color: var(--color-status-pill-text-warning);
}
.dd-chip-reason {
    display: inline-flex;
    align-items: center;
    padding: 0.125rem 0.4375rem;
    border-radius: 0.375rem;
    font-size: var(--fs-2xs);
    font-weight: 600;
    letter-spacing: 0.02em;
}
.dd-chip-reason-warn {
    background: var(--color-status-pill-bg-warning, var(--status-warning-bg));
    color: var(--color-status-pill-text-warning);
}
.dd-chip-reason-error {
    background: var(--color-status-pill-bg-danger, var(--status-danger-bg));
    color: var(--color-status-pill-text-danger);
}
.dd-chip-reason-benign {
    background: var(--color-status-pill-bg-info, var(--status-info-bg));
    color: var(--color-status-pill-text-info);
}
.dd-pill-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    display: inline-block;
}
.dd-pill-dot.online {
    background: var(--color-chart-status-online);
}
.dd-pill-dot.offline {
    background: var(--color-chart-status-offline);
}
.dd-pill-dot.warning {
    background: var(--color-chart-importance-medium);
}
.dd-pill-dot.acked {
    background: var(--color-chart-importance-low);
}
.dd-pill-dot.resolved {
    background: var(--color-chart-status-online);
}

/* ── KPI strip ──────────────────────────────────────────────────────
   Default is 6 tiles (device-detail). Add ``.dd-kpi-strip-5`` for the
   devices/gateways list pages; ``.dd-kpi-strip-4`` for tighter strips
   (gateways list per PR-8 §3.2). The breakpoint behaviour is shared. */
.dd-kpi-strip {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 0.75rem;
    margin-bottom: 1rem;
}
.dd-kpi-strip.dd-kpi-strip-5 {
    grid-template-columns: repeat(5, minmax(0, 1fr));
}
.dd-kpi-strip.dd-kpi-strip-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1100px) {
    .dd-kpi-strip,
    .dd-kpi-strip.dd-kpi-strip-5,
    .dd-kpi-strip.dd-kpi-strip-4 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
@media (max-width: 700px) {
    .dd-kpi-strip,
    .dd-kpi-strip.dd-kpi-strip-5,
    .dd-kpi-strip.dd-kpi-strip-4 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.dd-kpi {
    background: var(--bg-card);
    border: 1px solid var(--device-gray-200);
    border-radius: 0.75rem;
    padding: 0.875rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.dd-kpi-label {
    font-size: var(--fs-xs); /* matches .kpi-tile-label */
    font-weight: 700;
    color: var(--device-gray-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.dd-kpi-value {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(
        --fs-xl
    ); /* 24px — slight reduction from 1.75rem because 6 tiles cramp */
    color: var(--device-gray-900);
    line-height: 1.15;
}
.dd-kpi-value-unit {
    font-size: var(--fs-sm);
    font-weight: 500;
    color: var(--device-gray-500);
    margin-left: 0.1875rem;
}
.dd-kpi-sub {
    font-size: var(--fs-xs);
    color: var(--device-gray-500);
    margin-top: 0.125rem;
}
.dd-kpi-sub-empty {
    color: var(--device-gray-400);
    font-style: italic;
}

/* ── Stage: tab content + always-on rail ──────────────────────────── */
.dd-stage {
    display: grid;
    /* Rail width comes from --page-rail-width in design-system.css —
       single source of truth shared with future analyze pages. PR-7
       had 320px; PR-8 slimmed to 240px to free horizontal space for
       wide tables. Bump the var to revisit. */
    grid-template-columns: minmax(0, 1fr) var(--page-rail-width);
    gap: 1.125rem;
    /* Rail collapse (plan 03): animate the rail column when
       .rail-collapsed toggles. Expanded template is unchanged. */
    transition: grid-template-columns 0.2s ease;
}
/* Modifier — pages that don't ship a right rail (currently /tenants per
   LIST-PAGES-REDESIGN.md §"Tab structure per page"). Collapses the
   stage to a single full-width column instead of leaving an empty
   rail gutter. */
.dd-stage.dd-stage-no-rail {
    grid-template-columns: minmax(0, 1fr);
}
@media (max-width: 1100px) {
    .dd-stage,
    .dd-stage.rail-collapsed {
        grid-template-columns: 1fr;
    }
}
/* Devices LIST squeeze fix. The list table carries a ~800px min-width; beside
   the 240px rail it drops below that between ~1100-1400px, so its right columns
   (Last Seen / Actions) scroll-clip *under* the rail. Stack the rail below the
   full-width table until the viewport can hold the table's min-width + the rail.
   Scoped to .devices-page only — detail pages keep the 1100px breakpoint, their
   main column is flexible (chart), not min-width-bound. Headroom left for the
   upcoming device-State column (wider table). */
@media (max-width: 1400px) {
    .devices-page .dd-stage,
    .devices-page .dd-stage.rail-collapsed {
        grid-template-columns: 1fr;
    }
}

/* == Right-rail collapse (plan 03 / handoff .generated/handoff-alerts)
   ===================================================================
   Additive only -- the base .dd-stage above keeps its
   minmax(0,1fr) var(--page-rail-width) template and .dd-stage-no-rail
   modifier. One chevron at the top of the rail collapses both cards
   into a ~44px vertical strip, freeing horizontal width for the table.
   Same pattern/glyph as the left-nav + tenant-map gateway-rail
   collapses. NOTE: the handoff snippet redeclared a conflicting base
   .dd-stage in design-system.css; that was intentionally NOT taken
   (the canonical rule lives here) -- see plan 03 sec 2.1. */
.dd-stage.rail-collapsed {
    grid-template-columns: minmax(0, 1fr) 44px;
}

/* Toggle row sits above the cards: right-aligned when expanded,
   centered over the strip when collapsed. */
.dd-rail-toggle-row {
    display: flex;
    justify-content: flex-end;
}
.dd-stage.rail-collapsed .dd-rail-toggle-row {
    justify-content: center;
}
.dd-rail-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: 1px solid var(--border-base);
    background: var(--bg-card);
    border-radius: 6px;
    color: var(--fg-4);
    cursor: pointer;
    padding: 0;
    transition:
        background 0.15s,
        color 0.15s,
        border-color 0.15s;
    flex: none;
}
.dd-rail-toggle:hover {
    background: var(--color-panel-header-tinted);
    color: var(--coverage-strong-fg);
    border-color: var(--policy-stricter-bg);
}
.dd-rail-toggle:focus-visible {
    outline: 2px solid var(--color-brand-green);
    outline-offset: 2px;
}
.dd-rail-toggle svg {
    transition: transform 0.2s;
}
.dd-stage.rail-collapsed .dd-rail-toggle svg {
    transform: rotate(180deg);
}

/* Collapsed: card body hides; header reorients to a vertical text
   strip (same writing-mode trick as the tenant-map gateway rail).
   The titles are wrapped in a <span> in
   _alerts_page_content_generic.html for this rule to match.

   SCOPING: these MUST be qualified by .dd-stage-rail. The Coverage-map
   card on the Map tab reuses .dd-rail-card* but lives in
   .dd-stage-main; an unscoped `.dd-stage.rail-collapsed
   .dd-rail-card-header` matched it too, so collapsing the rail forced
   the "Coverage map" header to flex-column and `> :not(span)` hid the
   Expand button (the "title + Expand on two lines / chrome breaks when
   collapsed" bug). Rail-only keeps the vertical-strip trick where it
   belongs and leaves main-column cards untouched. */
.dd-stage.rail-collapsed .dd-stage-rail .dd-rail-card-body {
    display: none;
}
.dd-stage.rail-collapsed .dd-stage-rail .dd-rail-card-header {
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    padding: 14px 4px;
    border-bottom: 0;
    min-height: 140px;
}
.dd-stage.rail-collapsed .dd-stage-rail .dd-rail-card-header > span {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    letter-spacing: 0.04em;
    font-size: var(--fs-sm);
}
/* Collapsed strip shows only the rotated label -- hide any other
   header children (e.g. the gateway-rail "See all" aux link) so the
   44px strip stays clean. */
.dd-stage.rail-collapsed .dd-stage-rail .dd-rail-card-header > :not(span) {
    display: none;
}
.dd-stage-main {
    min-width: 0;
    /* Wide tables (devices list table has 7 columns including Actions)
       can exceed the 1fr column at narrow viewports — without
       overflow-x:auto they render *under* the 320px rail and clip the
       Actions column. Scoped to direct table descendants so the rest of
       the page chrome stays in normal flow. */
    overflow-x: hidden;
}
.dd-stage-main > div,
.dd-stage-main .table-container,
.dd-stage-main .table-container-box {
    max-width: 100%;
    overflow-x: auto;
}

/* Off-state uses :not(.active) so its specificity beats Tailwind
   single-class utilities like ``flex`` / ``block`` that callers may
   put on the wrapper (the tenant-detail Map sub-tabs do, so the map
   fills the column height when active). The active state has no
   explicit display rule on purpose — the element falls back to its
   natural display (``block`` for sections/divs) or to whatever
   utility class the caller applied (``flex`` for the heat-map
   wrapper that needs the column-height fill). */
.dd-tab-content:not(.active) {
    display: none;
}

/* ── Right rail card chrome (filled by PR-4 / PR-5) ───────────────── */
.dd-rail-card {
    background: var(--bg-card);
    border: 1px solid var(--device-gray-200);
    border-radius: 0.75rem;
    overflow: hidden;
}
.dd-rail-card + .dd-rail-card {
    margin-top: 0.875rem;
}
.dd-rail-card-header {
    padding: 0.75rem 0.875rem;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--device-gray-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom: 1px solid var(--device-gray-100);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
/* Body padding mirrors the header so the rail content has a visible
   12/14 px gutter on every side. Without this rule the body text sat
   flush to the card border (operator feedback: "no spacing"). Cards
   that need a different inner layout (e.g. dd-rail-bar-row with its
   own padded rows) can override on more specific selectors. */
.dd-rail-card-body {
    padding: 0.75rem 0.875rem;
}

/* Leaflet-managed containers should never grow a UA scrollbar. Same
   pattern as the .patitur-tab-bar fix above: a CORS-blocked third-
   party stylesheet (Flowbite, likely its responsive-table preset)
   applies ``overflow-x: auto`` on map containers and the small extra
   tile/SVG-pane content past the viewport triggers a 2 px scrollbar
   gutter on the bottom edge. Leaflet does its own clipping via
   absolute positioning on the panes; the container itself never
   needs scrollbars. ``!important`` because the third-party rule has
   higher cascade specificity. */
.leaflet-container {
    overflow: hidden !important;
}

/* Configuration-tab card width — the .dd-rail-card / .dd-rail-row
   chrome was designed for the 240 px right-rail (label + value
   tight against each other). On the gateway-detail Configuration
   tab those same cards live in the full-width .dd-stage-main, where
   ``justify-content: space-between`` smears the label and value to
   opposite ends with a huge empty gap between. Pinning the card to a
   readable max-width keeps the operator's eye-track short. Scoped to
   the Configuration tab only — other tabs (Map, Devices, Alerts,
   Positioning) host full-width content that the rule should NOT
   touch. */
#gw-tabpanel-config .dd-rail-card,
#device-tabpanel-config .dd-rail-card {
    max-width: 640px;
}

/* Positioning-toolbar mode buttons — the Method / Clusters / Radial-
   scatter trio. Previously only had Tailwind utilities (px-3 py-1.5
   font-medium), so the inactive Clusters button rendered as plain
   text and read like a label. These rules give the unpressed state a
   hover affordance and the aria-pressed=true state a brand-tinted fill
   so the operator can tell at a glance which mode is active. */
.pos-mode-btn {
    background: var(--bg-card);
    color: var(--device-gray-700, var(--bg-sidebar-hover));
    cursor: pointer;
    transition:
        background 0.12s,
        color 0.12s;
}
.pos-mode-btn:hover:not(:disabled):not([aria-pressed="true"]) {
    background: var(--device-gray-50, var(--bg-page));
    color: var(--device-gray-900, var(--fg-1));
}
.pos-mode-btn[aria-pressed="true"] {
    background: color-mix(in srgb, var(--color-brand-green) 12%, transparent);
    color: var(--color-brand-green);
}
.pos-mode-btn:disabled {
    cursor: not-allowed;
}
.dd-rail-card-header-aux {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--fs-xs);
    color: var(--device-gray-700);
    letter-spacing: 0;
    text-transform: none;
}
.dd-rail-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.5rem 0.875rem;
    font-size: var(--fs-sm);
    border-bottom: 1px solid var(--device-gray-50);
}
.dd-rail-row:last-child {
    border-bottom: none;
}
.dd-rail-row-label {
    color: var(--device-gray-500);
    flex-shrink: 0;
}
.dd-rail-row-value {
    color: var(--device-gray-900);
    font-weight: 500;
    text-align: right;
    min-width: 0;
    overflow-wrap: anywhere;
}
.dd-rail-row-value.mono {
    font-family:
        ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-size: var(--fs-xs);
}

/* Standalone monospace utility — same stack as .dd-rail-row-value.mono
   but usable on any element that doesn't already carry the rail-row
   value chrome (e.g. inline coordinate strings inside a flex row). */
.dd-mono {
    font-family:
        ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

/* ── Rail proportion-bar rows (Status / Importance breakdown cards on
   the devices + gateways list rails — PR-8). Two-line row: top has a
   label on the left and ``count · pct`` on the right; below sits a 4 px
   track with a coloured fill. Inline ``style="width: N%"`` drives the
   fill width; class drives the colour. */
.dd-rail-bar-row {
    padding: 0.5rem 0.875rem;
    border-bottom: 1px solid var(--device-gray-50);
    font-size: var(--fs-sm);
}
.dd-rail-bar-row:last-child {
    border-bottom: none;
}
.dd-rail-bar-row-meta {
    display: flex;
    justify-content: space-between;
    margin-bottom: 0.25rem;
}
.dd-rail-bar-row-label {
    color: var(--device-gray-700);
    font-weight: 500;
}
.dd-rail-bar-row-count {
    color: var(--device-gray-500);
    font-size: var(--fs-xs);
    font-variant-numeric: tabular-nums;
}
.dd-rail-bar-track {
    height: 4px;
    background: var(--device-gray-100);
    border-radius: 2px;
    overflow: hidden;
}
.dd-rail-bar-fill {
    height: 100%;
    background: var(--device-gray-400);
    transition: width 0.2s ease;
}

.dd-rail-bar-fill-online {
    background: var(--color-status-pill-text-online);
}
.dd-rail-bar-fill-offline {
    background: var(--color-status-pill-text-danger);
}
.dd-rail-bar-fill-critical {
    background: var(--color-status-pill-text-danger);
}
.dd-rail-bar-fill-high {
    background: var(--sev-dot-high);
}
.dd-rail-bar-fill-medium {
    background: var(--color-status-pill-text-warning);
}
.dd-rail-bar-fill-low {
    background: var(--color-status-pill-text-info);
}
.dd-rail-bar-fill-unknown {
    background: var(--device-gray-400);
}

/* ── Recent alerts (rail card 3) ───────────────────────────────────────
   Mirrors device-detail-tenant.html mockup: 4px severity bar, two-line
   meta (LAF + severity, then alert name), right-aligned timestamp. The
   row is a button (HTMX click → alert details modal); the bar uses
   ``align-self: stretch`` so it spans the full row height regardless of
   how the meta wraps. */
.dd-rail-alert {
    display: grid;
    grid-template-columns: 4px 1fr auto;
    gap: 0.625rem;
    padding: 0.625rem 0.875rem;
    border-bottom: 1px solid var(--device-gray-100);
    background: none;
    border-left: none;
    border-right: none;
    border-top: none;
    width: 100%;
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-sm);
    color: inherit;
    align-items: center;
}
.dd-rail-alert:last-child {
    border-bottom: none;
}
.dd-rail-alert:hover {
    background: var(--device-gray-50);
}
.dd-rail-alert-bar {
    background: var(--color-status-pill-text-warning);
    border-radius: 2px;
    align-self: stretch;
}
/* Severity-bar palette. Critical uses the danger pill token; low uses
   the info pill token. High (var(--sev-dot-high)) and medium (var(--sev-dot-medium)) don't have
   1:1 equivalents in design-system.css today — high is a *lighter*
   red than --color-status-pill-text-danger so the bar stack stays
   visually distinct row-to-row, and medium (var(--sev-dot-medium)) matches
   GATEWAY_DIFFERENCE_COLOR in flask_app/constants/signal_metrics.py.
   Worth a follow-up to mint --color-severity-bar-{high,medium} tokens
   if the palette gets reused elsewhere, but until then the partial
   var() coverage is intentional, not a typo. */
.dd-rail-alert.sev-critical .dd-rail-alert-bar {
    background: var(--color-status-pill-text-danger);
}
.dd-rail-alert.sev-high .dd-rail-alert-bar {
    background: var(--sev-dot-high);
}
.dd-rail-alert.sev-medium .dd-rail-alert-bar {
    background: var(--sev-dot-medium);
}
.dd-rail-alert.sev-low .dd-rail-alert-bar {
    background: var(--color-status-pill-text-info);
}
.dd-rail-alert-meta {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}
.dd-rail-alert-laf {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--fs-2xs);
    letter-spacing: 0.04em;
    color: var(--color-status-pill-text-warning);
    text-transform: uppercase;
}
.dd-rail-alert.sev-critical .dd-rail-alert-laf {
    color: var(--status-danger-fg);
}
.dd-rail-alert.sev-high .dd-rail-alert-laf {
    color: var(--sev-dot-high);
}
.dd-rail-alert.sev-low .dd-rail-alert-laf {
    color: var(--color-status-pill-text-info);
}
.dd-rail-alert-name {
    font-weight: 600;
    color: var(--device-gray-900);
    overflow-wrap: anywhere;
}
.dd-rail-alert-time {
    font-size: var(--fs-2xs);
    color: var(--device-gray-500);
    align-self: center;
    white-space: nowrap;
}

/* ── Empty-state placeholder (Alerts tab + rail before PR-4 / PR-5) ── */
/* A compact empty state, for a panel too short for the default 2.5rem block.
   Was an inline padding override on 10 of the 45 call sites. */
.dd-empty-state--compact {
    padding: 1rem 0.875rem;
}

/* A pagination chip that cannot be followed. The markup already says so with
   aria-disabled, so the appearance follows the attribute rather than a second
   hand-written style. */
.dd-chip[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}

.dd-empty-state {
    background: var(--bg-card);
    border: 1px dashed var(--device-gray-300);
    border-radius: 0.75rem;
    padding: 2.5rem 1.5rem;
    text-align: center;
    color: var(--device-gray-500);
    font-size: var(--fs-base);
}
.dd-empty-state .dd-empty-state-title {
    font-family: var(--font-display);
    font-weight: 600;
    color: var(--device-gray-700);
    margin-bottom: 0.375rem;
    font-size: 0.9375rem;
}
/* Modifier — compact variant for rail cards and KPI tile sub-areas
   where the full 2.5rem padding feels heavy. Replaces the inline
   ``style="padding:1.25rem 0.875rem;"`` + ``style="font-size:var(--fs-xs);"``
   patterns that used to live on those call sites. */
.dd-empty-state-compact {
    padding: 1.25rem 0.875rem;
}
/* Scoped to the component, not to one compact variant: the codebase carries
   BOTH .dd-empty-state-compact and .dd-empty-state--compact, and this rule only
   matched the first, so the same subtitle rendered at 11px in the device rail
   and at the inherited 14px on the devices page — which is why those call sites
   still carried an inline font-size. */
.dd-empty-state .dd-empty-state-aux {
    font-size: var(--fs-xs);
}

/* ── Signal-tab per-gateway chrome (Wave-0) ───────────────────────────
   Field-tab bar / window selector / mode bar / gateway chip bar / per-
   gateway summary cards. All scoped to .dd-signal-panel so they don't
   leak into other panels that use the same dashboard-card chrome.
   See memory-bank/tasks/feature-v0.8.0-stage-1/analysis/phase-2/
   DEVICE-DETAIL-WAVE-0-PLAN.md §3.6 for the design and
   device-detail-redesigned.html lines 271-398 for the visual target. */

.dd-field-bar {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    flex-wrap: wrap;
    padding: 0.875rem 0 0.75rem;
    border-bottom: 1px solid var(--device-gray-100);
    margin-bottom: 0.875rem;
}
.dd-field-bar-label {
    font-size: var(--fs-xs);
    font-weight: 700;
    color: var(--device-gray-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-right: 0.25rem;
}
.dd-field-bar-divider {
    width: 1px;
    align-self: stretch;
    background: var(--device-gray-200);
    margin: 0 0.375rem;
}
.dd-field-unit {
    font-size: var(--fs-2xs);
    opacity: 0.7;
    font-family:
        ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

.dd-window-bar {
    display: inline-flex;
    background: var(--device-gray-100);
    border-radius: 0.5rem;
    padding: 0.1875rem;
    gap: 0.125rem;
}
.dd-window-bar button {
    padding: 0.3125rem 0.75rem;
    font-size: var(--fs-sm);
    font-weight: 500;
    border-radius: 0.375rem;
    color: var(--device-gray-500);
    background: transparent;
    border: none;
    cursor: pointer;
    font-family: var(--font-display);
    min-width: 2.25rem;
}
.dd-window-bar button.active {
    background: var(--device-gray-900);
    color: var(--fg-on-dark);
}

.dd-mode-bar {
    display: inline-flex;
    background: var(--device-gray-100);
    border-radius: 0.625rem;
    padding: 0.1875rem;
    gap: 0.125rem;
}
.dd-mode-bar button {
    padding: 0.375rem 0.875rem;
    font-size: var(--fs-sm);
    font-weight: 500;
    border-radius: 0.5rem;
    color: var(--device-gray-500);
    background: transparent;
    border: none;
    cursor: pointer;
    font-family: inherit;
}
.dd-mode-bar button.active {
    background: var(--color-brand-green);
    color: var(--fg-on-dark);
}
.dd-mode-bar button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.dd-gw-chip-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.875rem 0;
    border-bottom: 1px solid var(--device-gray-100);
    background: var(--device-gray-50);
    margin: 0 -0.875rem 0.875rem;
    padding-left: 0.875rem;
    padding-right: 0.875rem;
}
.dd-gw-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.75rem;
    border-radius: 9999px;
    border: 1.5px solid var(--device-gray-300);
    background: var(--bg-card);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--device-gray-700);
    cursor: pointer;
    user-select: none;
    font-family: inherit;
    transition:
        opacity 0.12s,
        border-color 0.12s;
}
.dd-gw-chip-dot {
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 50%;
    flex-shrink: 0;
}
.dd-gw-chip:not(.on) {
    opacity: 0.45;
    text-decoration: line-through;
    background: var(--device-gray-50);
}
.dd-gw-chip.on {
    /* Per-gateway colour piped via inline ``--gw-color`` custom prop
       in the template — keeps the palette server-side per
       constants/signal_metrics.py:GATEWAY_PALETTE without inflating
       this stylesheet with N hard-coded colours. */
    border-color: var(--gw-color, var(--device-gray-300));
}
.dd-gw-chip-aggregate {
    border-style: dashed;
}

.dd-gw-summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 0.75rem;
    padding-top: 0.875rem;
    border-top: 1px solid var(--device-gray-100);
    margin-top: 0.875rem;
}
.dd-gw-summary {
    border: 1px solid var(--device-gray-200);
    border-radius: 0.625rem;
    padding: 0.75rem 0.875rem;
    transition: opacity 0.12s;
}
.dd-gw-summary.off {
    opacity: 0.45;
}
.dd-gw-summary-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.dd-gw-summary-name {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.8125rem;
    color: var(--device-gray-900);
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}
.dd-gw-summary-dot {
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 50%;
    flex-shrink: 0;
}
.dd-gw-summary-eui {
    font-family:
        ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-size: var(--fs-2xs);
    color: var(--device-gray-500);
    margin-top: 0.125rem;
    margin-left: 1.125rem;
}
.dd-gw-summary-stats {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 0.5rem;
}
/* The gateway-summary stat is the KPI tile at a smaller scale: same label,
   value and unit structure, 15px instead of 24px. It had its own three class
   names (.dd-gw-stat-label / -value / -unit) describing the same three parts,
   so .dd-kpi--sm folds it in and the old names become aliases of these rules
   rather than a fourth vocabulary.

   The 9px label is not on the type scale — 10px is the smallest step — so it
   comes up to --fs-2xs like every other off-scale size did. */
.dd-kpi--sm .dd-kpi-label {
    font-size: var(--fs-2xs);
}

.dd-kpi--sm .dd-kpi-value {
    font-family: var(--font-display);
    font-size: var(--fs-base);
    margin-top: 0.125rem;
}

.dd-kpi--sm .dd-kpi-value-unit {
    font-size: var(--fs-2xs);
    margin-left: 0.125rem;
}

.dd-gw-stat-label {
    font-size: 0.5625rem;
    font-weight: 700;
    color: var(--device-gray-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.dd-gw-stat-value {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.9375rem;
    color: var(--device-gray-900);
    margin-top: 0.125rem;
}
.dd-gw-stat-unit {
    font-size: var(--fs-2xs);
    font-weight: 500;
    color: var(--device-gray-500);
    margin-left: 0.125rem;
}

.dd-gw-status-tag {
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    padding: 0.125rem 0.5rem;
    border-radius: 9999px;
    flex-shrink: 0;
}
/* Signal-band swatches (mirror flask_app/constants/signal_metrics.py
   BAND_COLORS — single source of truth across Python, CSS, and the
   gateway-marker JS which reads ``entry.band_color`` directly).
   Strong/fair share their tints with --color-status-pill-*-online and
   --color-status-pill-*-warning; weak intentionally uses a softer red
   (var(--danger-strong-fg)) than the full-danger pill (var(--status-danger-fg)) so a gateway in the
   Weak band reads distinct from a fully offline device. Keep the
   literals — consolidating into shared CSS vars here would couple the
   "weak signal" semantic to "offline device", which they aren't. */
.dd-gw-status-tag-strong {
    background: var(--status-online-bg);
    color: var(--status-online-fg);
}
.dd-gw-status-tag-fair {
    background: var(--status-warning-bg);
    color: var(--status-warning-fg);
}
.dd-gw-status-tag-weak {
    background: var(--status-danger-bg);
    color: var(--danger-strong-fg);
}
.dd-gw-status-tag-unknown {
    background: var(--device-gray-100);
    color: var(--device-gray-500);
}

/* Rail "Gateways heard" card rows — one row per gateway, click-through
   to gateway-detail. Compact two-column stats footer keeps the row
   under 70px so 5 rows fit in the rail without scroll. */
.dd-rail-gateway {
    display: block;
    padding: 0.625rem 0.875rem;
    border-bottom: 1px solid var(--device-gray-100);
    text-decoration: none;
    color: inherit;
    transition: background-color 0.12s;
}
.dd-rail-gateway:last-child {
    border-bottom: none;
}
.dd-rail-gateway:hover {
    background: var(--device-gray-50);
}
.dd-rail-gateway-name {
    display: block;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.8125rem;
    color: var(--device-gray-900);
    margin-bottom: 0.125rem;
}
.dd-rail-gateway-eui {
    display: block;
    font-family:
        ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-size: var(--fs-2xs);
    color: var(--device-gray-500);
    margin-bottom: 0.375rem;
}
.dd-rail-gateway-stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.375rem;
    font-size: var(--fs-xs);
}
.dd-rail-gateway-stat-label {
    color: var(--device-gray-500);
    margin-right: 0.25rem;
}
.dd-rail-gateway-stat-value {
    color: var(--device-gray-900);
    font-family: var(--font-display);
    font-weight: 700;
}

/* ── Network Health (stage-1 phase-1 §3.2 Lite) ────────────────────
   Standalone Network Health page chrome. Mirrors the analyze-section
   wrapper rule above; the KPI grid + placeholder pill below are
   page-specific so they don't leak. The phase-2 tiles render in a
   muted state so the operator sees what's coming without confusing
   "0" with "not implemented." */
/* Network Health KPI tiles use the canonical design-system .dd-kpi
   classes verbatim (same look as /gateways + /devices + /alerts) —
   the prior bespoke .nh-kpi-grid / .nh-kpi-tile / .nh-kpi-label /
   .nh-kpi-value / .nh-kpi-fraction / .nh-kpi-sub variants were
   removed in favour of .dd-kpi-strip / .dd-kpi / .dd-kpi-label /
   .dd-kpi-value / .dd-kpi-value-unit / .dd-kpi-sub. Only the
   icon-inline-with-label modifier + accent variants live here. */
.nh-kpi-label-icon {
    display: inline-flex;
    align-items: center;
    margin-right: 0.375rem;
    color: var(--device-gray-400, var(--fg-5));
    vertical-align: -2px;
}
/* Headline-tile colour accents — applied to the 5 .dd-kpi tiles that
   answer health questions (Gateways online, Open alerts, Mean RSSI,
   Resilient positioning, Fragile cells). Two reinforcing channels
   per tile (coloured value text + tinted leading icon). The other
   4 count-context tiles (Active devices, Devices located, Good-
   signal band, Signal clusters) stay neutral so colour means
   something. Hexes mirror SIGNAL_BAND_STRONG/FAIR/WEAK_COLOR in
   constants/signal_metrics.py — same palette the hero verdict and
   the redundancy map cells read against. Flat fills only, no
   asymmetric thick edges or shadows (those read as bevels). */
.dd-kpi.nh-kpi-accent-green .dd-kpi-value,
.dd-kpi.nh-kpi-accent-green .nh-kpi-label-icon {
    color: var(--status-online-fg);
}
.dd-kpi.nh-kpi-accent-amber .dd-kpi-value,
.dd-kpi.nh-kpi-accent-amber .nh-kpi-label-icon {
    color: var(--status-warning-fg);
}
.dd-kpi.nh-kpi-accent-red .dd-kpi-value,
.dd-kpi.nh-kpi-accent-red .nh-kpi-label-icon {
    color: var(--danger-strong-fg);
}
.nh-kpi-tile--accent-neutral {
    /* Sentinel — applied when the underlying value is None (the
       headline tile literally has nothing to verdict). Same look as
       the base .nh-kpi-tile so the absence reads as "no data yet";
       icon stays in its neutral grey default. */
}

/* Hero verdict banner — single coloured strip above the KPI grid,
   answers "should I worry?" at a glance. Driven by the same
   resilient_positioning_pct the map cells aggregate against.
   Compact one-line layout: bold label + trailing summary on the
   same row, no stacked eyebrow row. Flat 1px border — no
   asymmetric thick edge (read as a bevel); the verdict pop comes
   from the bg tint + the label colour. */
.nh-verdict {
    border: 1px solid;
    border-radius: 0.5rem;
    padding: 0.625rem 1rem;
    margin-bottom: 1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.625rem;
}
.nh-verdict-label {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--fs-md);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    flex-shrink: 0;
}
.nh-verdict-summary {
    font-size: var(--fs-base);
    color: var(--device-gray-700, var(--bg-sidebar-hover));
    margin: 0;
    flex: 1;
    min-width: 0;
}
.nh-verdict--robust {
    background: var(--status-online-bg);
    border-color: var(--status-online-fg);
}
.nh-verdict--robust .nh-verdict-label {
    color: var(--status-online-fg);
}
.nh-verdict--mixed {
    background: var(--banner-warning-bg);
    border-color: var(--status-warning-fg);
}
.nh-verdict--mixed .nh-verdict-label {
    color: var(--status-warning-fg);
}
.nh-verdict--fragile {
    background: var(--sev-high-bg);
    border-color: var(--danger-strong-fg);
}
.nh-verdict--fragile .nh-verdict-label {
    color: var(--danger-strong-fg);
}
.nh-verdict--unknown {
    background: var(--device-gray-50, var(--bg-page));
    border-color: var(--device-gray-300, var(--border-medium));
}
.nh-verdict--unknown .nh-verdict-label {
    color: var(--device-gray-600, var(--fg-3));
}

/* Org-page worst-tenants table — chrome uses Tailwind utility
   classes inline in the template (matches the design-system pattern
   the policies + glossary + compliance tables use). Only the two
   table-specific bits that don't reduce cleanly to Tailwind live
   here: the sortable column-header button + the per-row verdict
   pill colour variants. */
.nh-sort-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0;
    background: transparent;
    border: 0;
    font: inherit;
    color: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    cursor: pointer;
}
.nh-sort-btn:hover,
.nh-sort-btn:focus-visible {
    color: var(--device-gray-900, var(--fg-1));
    outline: none;
}
.nh-sort-arrow {
    min-width: 0.75rem;
    text-align: center;
    color: var(--device-gray-400, var(--fg-5));
    font-weight: 700;
}
/* Per-row verdict pill — same palette the hero banner + map cells
   use, just shrunk for the table cell. */
.nh-org-tenants-pill {
    display: inline-block;
    padding: 0.125rem 0.5rem;
    border-radius: 9999px;
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.nh-org-tenants-pill--robust {
    background: var(--status-online-bg);
    color: var(--status-online-fg);
}
.nh-org-tenants-pill--mixed {
    background: var(--banner-warning-bg);
    color: var(--status-warning-fg);
}
.nh-org-tenants-pill--fragile {
    background: var(--sev-high-bg);
    color: var(--danger-strong-fg);
}
.nh-org-tenants-pill--unknown {
    background: var(--device-gray-100, var(--status-neutral-bg));
    color: var(--device-gray-600, var(--fg-3));
}

/* Phase-2 pill — mirrors the alert-map "Per-device disabled" affordance:
   visible-but-not-faked. Operator reads "we know this exists, it's not
   yet wired" without seeing a zero that could be misread as a real
   value. */
.nh-phase-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.125rem 0.5rem;
    border-radius: 9999px;
    background: var(--device-gray-100, var(--status-neutral-bg));
    color: var(--device-gray-600, var(--fg-3));
    font-size: var(--fs-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Map-area placeholder card — same dashed-border / muted-bg posture
   as the phase-2 KPI tiles. */
.nh-map-placeholder {
    background: var(--device-gray-50, var(--bg-page));
    border: 1px dashed var(--device-gray-300, var(--border-medium));
    border-radius: 0.75rem;
    padding: 2.5rem 1.25rem;
    text-align: center;
}
.nh-map-placeholder-inner {
    max-width: 38rem;
    margin: 0 auto;
}
.nh-map-placeholder-icon {
    width: 2.5rem;
    height: 2.5rem;
    color: var(--device-gray-300, var(--border-medium));
    margin: 0 auto 0.75rem;
    display: block;
}
.nh-map-placeholder-title {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--fs-md);
    color: var(--device-gray-700, var(--bg-sidebar-hover));
    margin: 0 0 0.5rem;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}
.nh-map-placeholder-body {
    font-size: 0.8125rem;
    color: var(--device-gray-500, var(--fg-4));
    margin: 0;
    line-height: var(--lh-normal);
}
.nh-map-placeholder-actions {
    margin: 1rem 0 0;
    font-size: 0.8125rem;
}
.nh-heatmap-container,
.nh-redundancy-container {
    min-height: 540px;
    border: 1px solid var(--device-gray-200, var(--border-base));
    border-radius: 0.75rem;
    background: var(--bg-card);
    overflow: hidden;
}
.nh-heatmap-footnote {
    margin: 0.5rem 0 0;
    font-size: var(--fs-sm);
    color: var(--device-gray-500, var(--fg-4));
    text-align: right;
}

/* ============================================================
   Cluster Posterior tab (FE 1.4, v0.9.0) — tier hero, posterior
   bars, factor waterfall. Flat fills only (no bevels). Tokens
   from design-tokens.css.
   ============================================================ */

.dd-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
}

/* ── Tier hero ─────────────────────────────────────────────── */
.tier-hero {
    border: 1px solid var(--border-base);
    border-radius: 10px;
    padding: 16px 18px;
    background: var(--bg-page);
    margin-bottom: 16px;
}
.tier-hero-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.tier-hero-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border-radius: 9999px;
    font-weight: var(--fw-semibold);
    border: 1px solid var(--border-base);
    color: var(--fg-2);
}
.tier-hero-pill .shape-i {
    display: inline-flex;
}
.tier-hero-pill.definitely {
    color: var(--color-brand-green);
    border-color: var(--color-brand-green);
}
.tier-hero-pill.probably {
    color: var(--sev-medium-fg);
    border-color: var(--sev-medium-fg);
}
.tier-hero-pill.weakly {
    color: var(--sev-high-fg);
    border-color: var(--sev-high-fg);
}
.tier-hero-pill.unclustered {
    color: var(--fg-4);
    border-color: var(--border-base);
}
.tier-hero-cluster-link {
    font-weight: var(--fw-semibold);
    color: var(--color-brand-green);
}
.tier-hero-confidence {
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: var(--fs-lg, 1.125rem);
    color: var(--fg-1);
    text-align: right;
}
.tier-hero-confidence-sub {
    display: block;
    font-family: var(--font-base, inherit);
    font-size: var(--fs-xs);
    font-weight: 400;
    color: var(--fg-4);
}
.tier-hero-secondary {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 22px;
    margin-top: 12px;
    font-size: var(--fs-sm);
    color: var(--fg-3);
}

/* ── Card with head ────────────────────────────────────────── */
.card-with-head {
    border: 1px solid var(--border-base);
    border-radius: 10px;
    margin-bottom: 16px;
    background: var(--bg-card);
}
.card-with-head .head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-base);
}
.card-with-head .head h3 {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--fg-1);
    margin: 0;
}
.card-with-head .head .sub {
    font-size: var(--fs-xs);
    color: var(--fg-4);
}
.card-with-head .body {
    padding: 14px 16px;
}

/* ── Posterior bars ────────────────────────────────────────── */
.posterior-row {
    display: grid;
    grid-template-columns: 22px 1fr 2fr 44px;
    align-items: center;
    gap: 10px;
    padding: 5px 0;
    font-size: var(--fs-sm);
}
.posterior-row .icon {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--bg-page);
    color: var(--fg-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-xs);
    border: 1px solid var(--border-base);
}
.posterior-row.most-likely .icon {
    background: var(--color-brand-green);
    color: var(--fg-on-dark);
    border-color: var(--color-brand-green);
}
.posterior-row .name {
    font-weight: 500;
    color: var(--fg-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.posterior-row .name .muted {
    opacity: 0.7;
    font-style: italic;
}
.posterior-row.most-likely .name {
    color: var(--color-brand-green);
    font-weight: 600;
}
.posterior-row .bar {
    height: 8px;
    background: var(--bg-page);
    border-radius: 9999px;
    overflow: hidden;
}
.posterior-row .fill {
    height: 100%;
    /* Server-sent share, read from a custom property rather than written as a
       declaration in the markup. */
    width: var(--bar-pct, 0%);
    background: var(--sev-medium-fg);
    border-radius: 9999px;
}
.posterior-row.most-likely .fill {
    background: var(--color-brand-green);
}
.posterior-row.background .fill {
    background: var(--fg-5);
}
.posterior-row .pct {
    text-align: right;
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: var(--fs-sm);
    color: var(--fg-1);
}
.posterior-entropy {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--border-base);
    font-size: var(--fs-xs);
    color: var(--fg-4);
}
.posterior-entropy .val {
    font-family: var(--font-mono);
    color: var(--fg-2);
    font-weight: 600;
}

/* ── Factor waterfall ──────────────────────────────────────── */
.waterfall-row {
    display: grid;
    grid-template-columns: 1fr 2fr 56px;
    align-items: center;
    gap: 10px;
    padding: 4px 0;
    font-size: var(--fs-sm);
}
.waterfall-row .label {
    color: var(--fg-2);
}
.waterfall-track {
    height: 8px;
    background: var(--bg-page);
    border-radius: 4px;
    position: relative;
    overflow: hidden;
}
.waterfall-fill {
    display: block;
    height: 100%;
    border-radius: 4px;
}
.waterfall-fill.pos {
    background: var(--color-brand-green);
}
.waterfall-fill.neg {
    background: var(--sev-high-fg);
}
.waterfall-row .val {
    text-align: right;
    font-family: var(--font-mono);
    font-weight: 700;
}
.waterfall-row .val.pos {
    color: var(--color-brand-green);
}
.waterfall-row .val.neg {
    color: var(--sev-high-fg);
}
.waterfall-total {
    display: grid;
    grid-template-columns: 1fr 56px;
    gap: 10px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border-base);
    font-weight: var(--fw-semibold);
}
.waterfall-total .val {
    text-align: right;
    font-family: var(--font-mono);
    font-weight: 700;
    color: var(--fg-1);
}
.waterfall-footer {
    margin-top: 12px;
    font-size: var(--fs-xs);
    color: var(--fg-4);
}

/* ── Positioning tier chip (device Positioning tab) ─────────────────────────
   The "how much to trust this fix" headline: method tier badge + 4-rung ladder
   + the two orthogonal uncertainty axes (accuracy_m size, confidence badge).
   Ladder colour is the spike-08 tier ladder, driven off the .pos-tier--{rank}
   class → --pos-tier-c. Flat fills only (no bevels/gradients). */
.pos-tier-chip {
    border: 1px solid var(--border-base);
    border-radius: 12px;
    background: var(--bg-card);
    padding: 14px 16px;
    margin-bottom: 16px;
}
/* Canonical tier colour by METHOD — shared with the positioning map palette
   (design-tokens.css --tier-*). Keyed by method, not rank, because the rank
   merges bilateration + trilateration into "Geometric" but the palette colours
   them differently. Cluster + terrain fold into the "located" green. */
.pos-method--none {
    --pos-tier-c: var(--fg-5);
} /* not computed */
.pos-method--radial {
    --pos-tier-c: var(--tier-rad);
} /* 1 gateway — blue */
.pos-method--bilateration {
    --pos-tier-c: var(--tier-bi);
} /* 2 gateways — orange */
.pos-method--trilateration {
    --pos-tier-c: var(--tier-tri);
} /* 3+ gateways — green */
.pos-method--cluster {
    --pos-tier-c: var(--tier-cluster);
} /* cluster prior — violet */
.pos-method--terrain {
    --pos-tier-c: var(--tier-terrain);
} /* terrain + habitat — teal (strongest) */
.pos-tier-chip-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.pos-tier-badge {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-display);
    font-weight: 500;
    font-size: var(--fs-lg);
    color: var(--fg-1);
}
.pos-tier-dot {
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: var(--pos-tier-c);
    flex: 0 0 13px;
}
.pos-tier-step {
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--fg-5);
}
.pos-tier-ladder {
    display: flex;
    gap: 4px;
    margin: 12px 0 14px;
}
.pos-tier-seg {
    flex: 1;
    height: 6px;
    border-radius: 3px;
    background: var(--border-base);
}
.pos-tier-seg.is-lit {
    background: var(--pos-tier-c);
}
.pos-tier-axes {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-bottom: 10px;
}
.pos-tier-axis {
    border: 1px solid var(--border-base);
    border-radius: 10px;
    padding: 10px 12px;
    background: var(--bg-page);
}
.pos-tier-axis-k {
    display: block;
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--fg-5);
    font-family: ui-monospace, Menlo, monospace;
}
.pos-tier-axis-v {
    display: block;
    font-family: var(--font-display);
    font-size: var(--fs-lg);
    font-weight: 500;
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
}
.pos-tier-axis-sub {
    display: block;
    font-size: var(--fs-xs);
    color: var(--fg-4);
    margin-top: 3px;
}
.pos-tier-conf-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-sm);
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 999px;
    border: 1px solid var(--pos-tier-c);
    color: var(--pos-tier-c);
    margin-top: 2px;
}
/* Engine honesty flag ON — a caution badge, not a muted one. Faint red fill +
   the exclamation-triangle glyph give it the prominence the engine asked for
   (device 252 read "100% certain" on a 2-packet fix before this). Flat tint,
   no gradient. */
.pos-tier-conf-badge.is-low {
    border-color: var(--danger-soft-fg);
    color: var(--danger-strong-fg);
    background: var(--sev-high-bg);
}
/* Not computed yet — a normal state for a new device. Muted grey, never the red
   caution (that would conflate "no data" with "bad data"). */
.pos-tier-conf-badge.is-none {
    border-color: var(--border-base);
    color: var(--fg-5);
}
/* ── Placement caveats — "why this position is uncertain" ────────────────────
   The reason(s) the engine attached to an uncertain fix, shown WITH the
   accuracy_m above (never a bare number). Calm, flat tints, 1px borders, no
   bevels. Tone is the severity axis: review=red, caution=amber, info=neutral —
   a single-gateway fix (73% of the fleet) is informational, never a warning. */
.pos-tier-caveats {
    margin: 4px 0 8px;
}
.pos-tier-caveats-head {
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--fg-4);
    margin-bottom: 6px;
}
.pos-caveat {
    display: flex;
    gap: 8px;
    align-items: baseline;
    border: 1px solid var(--border-base);
    border-radius: 8px;
    padding: 7px 10px;
    margin-bottom: 6px;
}
.pos-caveat-sev {
    flex: none;
    font-size: var(--fs-xs); /* was 10.5px -> 11px */
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 1px 7px;
    border-radius: 999px;
    border: 1px solid currentColor;
    white-space: nowrap;
}
.pos-caveat-text {
    font-size: var(--fs-sm); /* was 12.5px -> 12px */
    line-height: 1.45;
    color: var(--fg-body-strong);
}
.pos-caveat.is-review {
    border-color: var(--danger-soft-fg);
    background: var(--sev-high-bg);
}
.pos-caveat.is-review .pos-caveat-sev {
    color: var(--danger-strong-fg);
}
.pos-caveat.is-caution {
    border-color: var(--policy-looser-bg);
    background: var(--sev-medium-bg);
}
.pos-caveat.is-caution .pos-caveat-sev {
    color: var(--warning-strong-fg);
}
.pos-caveat.is-info {
    border-color: var(--border-base);
    background: var(--bg-page);
}
.pos-caveat.is-info .pos-caveat-sev {
    color: var(--fg-3);
}
.pos-caveat-more {
    margin-top: 2px;
}
.pos-caveat-more > summary {
    cursor: pointer;
    font-size: var(--fs-sm); /* was 11.5px -> 12px */
    font-weight: 600;
    color: var(--fg-4);
    padding: 2px 0 6px;
    list-style: none;
}
.pos-caveat-more > summary::-webkit-details-marker {
    display: none;
}
.pos-caveat-more > summary::before {
    content: "\25B8";
    display: inline-block;
    margin-right: 6px;
    transition: transform 0.12s ease;
}
.pos-caveat-more[open] > summary::before {
    transform: rotate(90deg);
}

.pos-tier-terrain-badge {
    display: inline-block;
    font-size: var(--fs-sm); /* was 11.5px -> 12px */
    font-weight: 600;
    color: var(--color-brand-green-hover);
    border: 1px solid var(--pos-terrain-border);
    border-radius: 8px;
    padding: 4px 9px;
    margin-bottom: 8px;
}
.pos-tier-cluster {
    font-size: var(--fs-sm); /* was 12.5px -> 12px */
    color: var(--fg-body-strong);
    margin-bottom: 8px;
}
.pos-tier-prov {
    font-family: ui-monospace, Menlo, monospace;
    font-size: var(--fs-xs);
    color: var(--fg-5);
}

/* ── Habitat & placement pills (device-class catalog metadata) ──────────────
   Flat fills, 1px borders, no bevels; per-value colour via the .dd-pp--<v>
   modifier. Mirrors design/03-device-habitat-placement-pills.html. */
.dd-pp-group {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}
.dd-pp {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: var(--fs-sm);
    font-weight: 500;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid var(--border-base);
    background: var(--bg-card);
    color: var(--fg-3);
    white-space: nowrap;
}
.dd-pp i {
    font-size: var(--fs-xs);
}
.dd-pp--land {
    background: var(--status-resolved-bg);
    color: var(--pos-land-fg);
    border-color: var(--flash-success-bg);
}
.dd-pp--water {
    background: var(--status-info-bg);
    color: var(--pos-water-fg);
    border-color: var(--info-bg);
}
.dd-pp--amph {
    background: var(--status-online-bg);
    color: var(--status-online-fg);
    border-color: var(--banner-soft-end);
}
.dd-pp--indoor {
    background: var(--row-highlight);
    color: var(--pos-indoor-fg);
    border-color: var(--pos-indoor-border);
}
.dd-pp--outdoor {
    background: var(--status-resolved-bg);
    color: var(--pos-land-fg);
    border-color: var(--flash-success-bg);
}
.dd-pp--mixed {
    background: var(--status-warning-bg);
    color: var(--status-warning-fg);
    border-color: var(--sev-medium-border);
}
.dd-pp--fixed {
    background: var(--status-neutral-bg);
    color: var(--fg-3);
    border-color: var(--border-base);
}
.dd-pp--nomadic {
    background: var(--status-warning-bg);
    color: var(--status-warning-fg);
    border-color: var(--sev-medium-border);
}
.dd-pp--mobile {
    background: var(--status-info-bg);
    color: var(--pos-water-fg);
    border-color: var(--info-bg);
}
.dd-pp--unknown {
    background: var(--bg-page);
    color: var(--fg-5);
    border-color: var(--border-base);
    border-style: dashed;
}

/* ── Positioning tier trail (device Positioning tab) ────────────────────────
   Sparkline of recent fixes over time; bar height = tier rank (the climb),
   colour = the pos-tier--<rank> ladder (shared with the chip). */
.pos-trail {
    border: 1px solid var(--border-base);
    border-radius: 12px;
    background: var(--bg-card);
    padding: 12px 16px 14px;
    margin-bottom: 16px;
}
.pos-trail-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 10px;
}
.pos-trail-title {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: var(--fs-base);
    color: var(--fg-1);
}
.pos-trail-sub {
    font-size: var(--fs-sm); /* was 11.5px -> 12px */
    color: var(--fg-5);
    font-variant-numeric: tabular-nums;
}
.pos-trail-track {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 64px;
    overflow-x: auto;
}
.pos-trail-bar {
    flex: 1 0 4px;
    min-width: 4px;
    border-radius: 2px 2px 0 0;
    background: var(--pos-tier-c, var(--fg-5));
    height: calc(14px + var(--r, 0) * 11px);
}

/* Colour key beneath the trail — reads the same method palette as the bars,
   so the operator never has to hover to learn what a colour means. */
.pos-trail-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    margin-top: 12px;
    font-size: var(--fs-sm); /* was 11.5px -> 12px */
    color: var(--fg-4); /* muted */
}
.pos-trail-key {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.pos-trail-sw {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--pos-tier-c, var(--fg-5));
    flex: 0 0 10px;
}
.pos-trail-legend .link-glossary {
    margin-left: auto;
}

/* ── Per-gateway colour coding (device Activity/Signal) ───────────────────────
   Gateway EUIs differ by one character, so colour is the real differentiator.
   ``--gw`` carries the gateway's chart-palette colour (set inline); the dot +
   pill outline use it so the rail card, receptions table and signal chart stay
   colour-matched for the same gateway. */
.dd-gw-swatch {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--gw, var(--color-gray-600));
    flex: 0 0 9px;
}
.dd-gw-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px;
    border-radius: 9999px;
    border: 1.5px solid var(--gw, var(--border-base));
    background: var(--bg-card);
    font-size: var(--fs-sm);
    color: var(--bg-sidebar);
    white-space: nowrap;
}
/* The rail gateway name is a green internal link; keep that colour even though
   it sits inside the card-wide anchor, and let the colour dot sit before it. */
.dd-rail-gateway .dd-rail-gateway-name.link-internal {
    color: var(--link-internal-fg);
}

/* Last-activity staleness colour coding. act-fresh keeps the default ink; only
   a stale (>24h, amber) or old (>7d, red) device draws the eye. */
.act-stale {
    color: var(--status-warning-fg);
}
.act-old {
    color: var(--status-danger-fg);
}
.act-never {
    color: var(--fg-5);
}

/* ── Gateway calibration state pill (v0.10.5) ─────────────────────────────
   The calibration-state badge, shared by the gateway list state column and the
   detail Configuration card's "Calibration" row (the card itself is the shared
   info_panel chrome). Flat fills only — bordered tint pills, no
   bevels/shadows/gradients. Icon + label, never colour alone (house rule). */
.cal-state {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border-radius: 9999px;
    border: 1px solid transparent;
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}
.cal-state i {
    font-size: var(--fs-xs);
}
.cal-tone-success {
    background: var(--status-online-bg);
    border-color: var(--policy-stricter-bg);
    color: var(--color-brand-green-deep);
}
.cal-tone-warning {
    background: var(--banner-warning-bg);
    border-color: var(--banner-warning-border);
    color: var(--warning-strong-fg);
}
.cal-tone-muted {
    background: var(--status-neutral-bg);
    border-color: var(--border-base);
    color: var(--fg-4);
}
.cal-tone-neutral {
    background: var(--bg-page);
    border-color: var(--border-base);
    border-style: dashed;
    color: var(--fg-5);
}

/* ── Predicted-cell breakdown (positioning-map-breakdown.js) ────────────
   The 49 stored r11 rows inside one drawn r9 hexagon, in the 240 px rail.
   Authored against the 210 px content box (.dd-rail-card-body's padding), so
   every width here is a fraction and nothing assumes a wider column.

   At 49 cells each hex is ~28 px, which is under the 44 px touch target. That
   is accepted rather than solved by paging or zooming: the grid's job is to
   show the WHOLE cell at once — a paged grid would hide exactly the thing it
   exists to reveal. Below 1100 px the stage collapses to one column and the
   rail goes full width, so the touch case gets the larger rendering.

   Colours come from the --device-gray-* scale and --color-brand-green already
   in this sheet. The only new values are the metric ramp, which lives in JS
   (BREAKDOWN_RAMP) because it is data-driven, not chrome. */
/* First block in the inspector, so no top rule or gap — the divider belongs
   BELOW it, separating the grid from the summary it explains. */
.pm-bd {
    margin-bottom: 0.75rem;
    padding-bottom: 0.625rem;
    border-bottom: 1px solid var(--device-gray-100);
}
.pm-bd-h {
    margin: 0;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--device-gray-900);
}
.pm-bd-sub {
    margin: 0.125rem 0 0.5rem;
    font-size: var(--fs-xs);
    line-height: 1.45;
    color: var(--device-gray-500);
}
.pm-bd-metric {
    width: 100%;
    margin-bottom: 0.5rem;
    padding: 0.3125rem 0.4375rem;
    font: inherit;
    font-size: var(--fs-sm);
    color: var(--device-gray-900);
    background: var(--bg-card);
    border: 1px solid var(--device-gray-200);
    border-radius: 0.375rem;
}
.pm-bd-metric:focus-visible {
    outline: 2px solid var(--color-brand-green);
    outline-offset: 1px;
}
.pm-bd-comb {
    display: block;
    width: 100%;
    height: auto;
}
/* Stroke is the card background, so adjacent cells read as separate hexes
   without a border colour competing with the fill. */
.pm-bd-hx {
    stroke: var(--gw-marker-halo);
    stroke-width: 0.5;
    cursor: pointer;
}
/* A cell the read never returned: outline only, so a partial hexagon looks
   partial. Drawing it filled — or omitting it — would both say the read
   covered ground it did not. */
.pm-bd-hx.is-absent {
    fill: none;
    stroke: var(--device-gray-300);
    stroke-width: 0.4;
    stroke-dasharray: 1 1;
    cursor: default;
}
.pm-bd-hx:hover {
    opacity: 0.7;
}
.pm-bd-hx:focus-visible,
.pm-bd-hx.is-selected {
    outline: none;
    stroke: var(--device-gray-900);
    stroke-width: 1.4;
}
/* The row the rollup printed as the whole hexagon's value. */
.pm-bd-crown {
    fill: none;
    stroke: var(--device-gray-900);
    stroke-width: 1;
    pointer-events: none;
}
.pm-bd-legend {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0.375rem 0 0;
    font-size: var(--fs-2xs);
    line-height: 1.45;
    color: var(--device-gray-500);
}
.pm-bd-dot {
    flex: none;
    width: 0.375rem;
    height: 0.375rem;
    border: 1px solid var(--device-gray-900);
    border-radius: 9999px;
}
/* The gradient strip. An earlier revision printed the range as text only, so
   the ramp the cells were painted with was never actually shown. */
.pm-bd-rampbar {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    margin: 0.375rem 0 0;
}
.pm-bd-strip {
    flex: 1;
    min-width: 3.75rem;
    height: 0.4375rem;
    border-radius: 0.25rem;
}
/* A field whose spread is below its epsilon: greyed, because drawing a live
   gradient over 0.00006 of variation is the map overstating what it knows. */
.pm-bd-strip.is-flat {
    background: var(--device-gray-300);
}
.pm-bd-end {
    font-size: 0.5625rem;
    font-variant-numeric: tabular-nums;
    color: var(--device-gray-500);
    white-space: nowrap;
}
.pm-bd-flat {
    margin: 0.25rem 0 0;
    font-size: 0.5625rem;
    font-style: italic;
    line-height: 1.4;
    color: var(--device-gray-500);
}
.pm-bd-sect {
    margin: 0.5rem 0 0.25rem;
    font-size: 0.5625rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--device-gray-500);
}
/* Range bar: the span across all children, marked where this one falls. Not a
   min/median/max bar — a child here is ONE row, so the question is how it
   compares to its siblings, not how varied it is inside itself. */
.pm-bd-row {
    margin-bottom: 0.4375rem;
}
.pm-bd-row-h {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.125rem;
    font-size: var(--fs-2xs);
    color: var(--device-gray-500);
}
/* The zone a value falls in, beside its label — the colour's meaning in
   words, so the strip is not the only way to read the grid. */
.pm-bd-zone {
    font-style: normal;
    font-size: 0.5625rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--device-gray-400);
}
.pm-bd-row-h b {
    font-size: var(--fs-xs);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--device-gray-900);
}
.pm-bd-row.is-active .pm-bd-row-h {
    color: var(--color-brand-green-deep);
}
.pm-bd-track {
    position: relative;
    height: 0.375rem;
    border-radius: 0.1875rem;
    background: var(--device-gray-100);
}
.pm-bd-fill {
    position: absolute;
    inset: 0;
    border-radius: 0.1875rem;
    background: var(--color-brand-green);
    opacity: 0.28;
}
.pm-bd-mark {
    /* The marker's position is the datum; the renderer hands it over as
       --pm-bd-at rather than writing a left rule from JS. */
    left: calc(var(--pm-bd-at, 0%) - 1px);
    position: absolute;
    top: -0.125rem;
    width: 2px;
    height: 0.625rem;
    border-radius: 1px;
    background: var(--color-brand-green-hover);
}
/* A flat field gets a hatched empty track: there is no position to mark, and a
   marker at an arbitrary point would invent one. */
.pm-bd-row.is-flat .pm-bd-track {
    background: repeating-linear-gradient(
        90deg,
        var(--device-gray-100) 0 3px,
        transparent 3px 6px
    );
}
.pm-bd-ends {
    display: flex;
    justify-content: space-between;
    margin-top: 0.0625rem;
    font-size: 0.5625rem;
    font-variant-numeric: tabular-nums;
    color: var(--device-gray-500);
}
.pm-bd-tag {
    margin-left: 0.3125rem;
    padding: 0.0625rem 0.3125rem;
    font-size: 0.5rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    vertical-align: 0.0625rem;
    color: var(--device-gray-700);
    border: 1px solid var(--device-gray-300);
    border-radius: 9999px;
}
.pm-bd-child {
    margin: 0.625rem 0 0;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--device-gray-900);
}
.pm-bd-id {
    margin: 0.0625rem 0 0.375rem;
    font-size: var(--fs-2xs);
    color: var(--device-gray-500);
    overflow-wrap: anywhere;
}
.pm-bd-hint,
.pm-bd-note {
    margin: 0.25rem 0 0;
    font-size: var(--fs-2xs);
    line-height: 1.45;
    color: var(--device-gray-500);
}
.pm-bd-hint {
    font-style: italic;
}
@media (prefers-reduced-motion: no-preference) {
    .pm-bd-hx {
        transition: opacity 0.12s ease;
    }
}

/* A dot-plus-count run inside a detail header, e.g. "* 3 active". */
.dd-inline-meta {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

/* The aux figure in a rail card header, at normal weight. The header is bold
   display type; a running total beside it is not a heading. The call site used
   to write font-weight inline and restate text-transform and letter-spacing
   that .dd-rail-card-header-aux already sets. */
.dd-rail-card-header-aux--light {
    font-weight: 500;
}
