/* =====================================================================
   LightPane embedded pane — self-contained CSS (v1, 2026-04-23)
   =====================================================================
   Everything here is scoped under `.lp-pane`. We never touch the host
   page's globals — no `html`, `body`, `*`, or bare-element rules.
   Selectors are wrapped in `:where()` so specificity stays at (0,1,0)
   or lower; that means an embedder can override any rule with a single
   class on their own page without needing `!important`.

   Configurable via CSS custom properties. Override them in host-page
   CSS (e.g. `.my-theme .lp-pane { --lp-pane-radius: 0; }`) to re-skin
   without forking this file.
   --------------------------------------------------------------------- */

/* Token files (Phase 8 / doc 36). Imported in dependency order so
   every consumer that loads sd-embed.css gets the full chain:
     reserved   — status + chart-series tokens (theme-invariant)
     functional — spacing / type / overflow geometry (theme-invariant)
     theme      — palette + functional-name mappings (default amber;
                  switch via <html data-lp-theme="..."> + alt theme file)
   Migrated panes reference --lp-fn-* tokens which only resolve when
   the active theme file is loaded — without this import they would
   render unstyled. */
@import url("./sd-tokens-reserved.css");
@import url("./sd-tokens-functional.css");
@import url("./sd-theme-amber.css");

/* ====================================================================
   COLOUR TOKEN CONTRACT (Scope 1, 2026-07-14)
   Every colour in this stylesheet is a CSS variable — no bare hex in
   element rules (enforced by pane-pipeline/harness/lint_css.py --embed).
   To recolour centrally, redefine tokens AFTER this file, scoped to
   .lp-pane (equal specificity, later source order wins):
       .lp-pane { --lp-pane-amber-700: #...; --lp-pane-tag-success-bg: #...; }
   Token namespaces in play:
     --lp-pane-*   pane chrome / layout / tag+status tones  (canonical here)
     --lp-rag-*    RAG status palette (solid + -soft AA pairs)
     --lp-fn-      functional theme layer (used by the .lp-r- helper block;
                   defined by the @imported sd-tokens / sd-theme files)
   NOTE (Scope 2): --lp-pane-* and --lp-fn-* are two parallel systems;
   unifying them into one documented public theming contract is the
   deferred design-system pass. Cross-origin embed/v1 needs a theme= param
   (parent CSS can't reach in). See project_sd_embed_renderer_port memory.
   ==================================================================== */

.lp-pane,
.lp-pane-modal {
    /* -----------------------------------------------------------------
       Tunables — override at :root or on a parent container to re-skin.
       Declared on BOTH .lp-pane and .lp-pane-modal because the policy
       viewer modal is appended to document.body (outside any .lp-pane)
       but still needs the amber palette.
       ----------------------------------------------------------------- */
    --lp-pane-font-family:
        ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --lp-pane-font-size:       14px;
    --lp-pane-line-height:     1.5;

    /* -----------------------------------------------------------------
       Brand amber palette. Carried inside the embed stylesheet so host
       pages don't need the Sunlit theme installed; the tokens are
       aliased below into the specific slots (head bg, thead bg, hover,
       etc.) so an embedder can either swap the whole palette here or
       override individual slots.
       ----------------------------------------------------------------- */
    /* Synced to the tw-amber accessibility palette 2026-07-22 (was the near-white
       default; dynamiccloud already used tw-amber). Deepened washes separate from
       white; accent #b45309 = 5.02:1 on white (WCAG AA). Old values preserved in
       sd-theme-amber-legacy.css. */
    --lp-pane-amber-900:       #451a03;   /* deepest amber — body ink on amber bg */
    --lp-pane-amber-800:       #92400e;   /* deep amber — pane head fill */
    --lp-pane-amber-700:       #b45309;   /* rich amber — accents (5.02:1 AA) */
    --lp-pane-amber-500:       #f5c518;   /* brand core — spinner, focus (kept gold) */
    --lp-pane-amber-300:       #fbbf24;   /* mid golden amber — thead fill */
    --lp-pane-amber-200:       #fcd34d;   /* pale amber */
    --lp-pane-amber-100:       #fde68a;   /* deepened wash — row hover / key-wash */
    --lp-pane-amber-50:        #fef3c7;   /* amber wash — detail drawer */

    --lp-pane-bg:              #ffffff;

    /* Pane head (service label strip at top). Meta ink matches the
       title so the region + source read cleanly rather than fading
       out at 72%. */
    --lp-pane-head-bg:         var(--lp-pane-amber-300);
    --lp-pane-head-ink:        var(--lp-pane-amber-900);
    --lp-pane-head-meta-ink:   var(--lp-pane-amber-900);

    /* Table header row */
    --lp-pane-thead-bg:        var(--lp-pane-amber-100);
    --lp-pane-thead-ink:       var(--lp-pane-amber-900);

    /* Row hover. The drawer wash below is deliberately MORE yellow
       than the row hover, so (i) value cells inside the drawer
       (which use the amber-50 hover wash) clearly stand out against
       the drawer wash, and (ii) the drawer itself reads as a sunken
       yellow panel rather than just more white. */
    --lp-pane-row-hover-bg:    var(--lp-pane-amber-50);
    /* Soft pale amber for the expanded-drawer band — chosen to sit
       between the value-cell white (#fffbeb) and the thead-row
       amber (#fef3c7). The earlier #fdedb0 read too heavy. */
    --lp-pane-detail-bg:       #fef3c7;   /* drawer band — tw-amber sync 2026-07-22 */

    /* Borders — warm-tinted so they don't read as cold grey next to
       amber. --lp-pane-outer-border is deliberately stronger than
       internal dividers so a pane embedded on a similarly-toned host
       page still reads as a discrete object. */
    --lp-pane-outer-border:    #a5620e;   /* dark amber (amber-700) — phase iii, approved 2026-07-01 */
    --lp-pane-border:          #e8dfc9;
    /* Header-box border — a dedicated, DARKER mid-grey (2026-07-16). Kept
       separate from --lp-pane-border (which also draws table row dividers, cell
       borders, subtable frames, …) so the boxes get definition without turning
       every table into a heavy grid. Override per-pane for a custom box border. */
    --lp-pane-header-box-border: #8f8b82;
    --lp-pane-divider:         #f3ebd6;

    /* Body text */
    --lp-pane-ink:             #2b2a1e;   /* warm near-black */
    --lp-pane-ink-dim:         #6b6452;   /* warm grey for meta / labels */
    --lp-pane-ink-faint:       #9c9583;   /* null dashes, chevron */

    --lp-pane-accent:          var(--lp-pane-amber-500);  /* spinner accent */
    /* Code/pre blocks inside detail panels — kept on the lightest
       background in the palette (pure white) so the JSON stands out
       against the cream detail-bg around it. */
    --lp-pane-code-bg:         var(--lp-pane-bg);
    --lp-pane-code-border:     var(--lp-pane-amber-300, #fde9a6);
    --lp-pane-code-ink:        #4a2a00;

    --lp-pane-err-bg:          #fffbeb;
    --lp-pane-err-border:      #fcd34d;
    --lp-pane-err-ink:         #92400e;

    /* High-contrast RAG set for banners / status footers — saturated bg with
       accessible text (all >=4.5:1). Approved 2026-07-01 (phase iii). */
    --lp-rag-critical-bg:      #d2222d;   --lp-rag-critical-ink: #ffffff;
    --lp-rag-warn-bg:          #ffbf00;   --lp-rag-warn-ink:     #000000;
    --lp-rag-success-bg:       #1e7d1e;   --lp-rag-success-ink:  #ffffff;   /* 5.24:1 on white — WCAG AA */
    --lp-rag-info-bg:          #1552c9;   --lp-rag-info-ink:     #ffffff;

    /* Soft RAG variant — pale tint + dark ink, for lighter-weight tone surfaces
       (filter pills, gentle inline highlights) that shouldn't read as heavy as
       the solid bands. Same hue family as the solid set; all pairs verified
       >=4.5:1 (measured 6.9–8.0:1). Single source of truth so pills/tags don't
       fork on raw hex. */
    --lp-rag-critical-soft-bg: #fef2f2;   --lp-rag-critical-soft-ink: #991b1b;   --lp-rag-critical-soft-border: #fecaca;
    --lp-rag-warn-soft-bg:     #fff7ed;   --lp-rag-warn-soft-ink:     #9a3412;   --lp-rag-warn-soft-border:     #fed7aa;
    --lp-rag-success-soft-bg:  #ecfdf5;   --lp-rag-success-soft-ink:  #065f46;   --lp-rag-success-soft-border:  #a7f3d0;
    --lp-rag-info-soft-bg:     #eff6ff;   --lp-rag-info-soft-ink:     #1e40af;   --lp-rag-info-soft-border:     #bfdbfe;

    /* Tag/pill tones — customer-overridable semantic tokens. Inks are the
       WCAG-AA soft RAG inks; warn/info map straight to the RAG soft set.
       success/critical keep the more-saturated green/red bg+border chosen
       2026-07-14 (dynamiccloud parity, "more legible") over the paler RAG tint. */
    --lp-pane-tag-success-bg:  #d1fae5;                     --lp-pane-tag-success-ink:  var(--lp-rag-success-soft-ink);   --lp-pane-tag-success-border:  #6ee7b7;
    --lp-pane-tag-warn-bg:     var(--lp-rag-warn-soft-bg);  --lp-pane-tag-warn-ink:     var(--lp-rag-warn-soft-ink);      --lp-pane-tag-warn-border:     var(--lp-rag-warn-soft-border);
    --lp-pane-tag-critical-bg: #fee2e2;                     --lp-pane-tag-critical-ink: var(--lp-rag-critical-soft-ink);  --lp-pane-tag-critical-border: #fca5a5;
    --lp-pane-tag-info-bg:     var(--lp-rag-info-soft-bg);  --lp-pane-tag-info-ink:     var(--lp-rag-info-soft-ink);      --lp-pane-tag-info-border:     var(--lp-rag-info-soft-border);
    /* Neutral status tone (state--neutral). */
    --lp-pane-tag-neutral-bg:  #f1f5f9;  --lp-pane-tag-neutral-ink:  #475569;  --lp-pane-tag-neutral-border:  #cbd5e1;
    /* Cap-banner tones (resource_usage: warn / truncated / blocked). */
    --lp-pane-cap-warn-bg:      #fff4d6;  --lp-pane-cap-warn-ink:      #7a4f00;  --lp-pane-cap-warn-border:      #f0c674;
    --lp-pane-cap-truncated-bg: #ffe5cf;  --lp-pane-cap-truncated-ink: #7a3a00;  --lp-pane-cap-truncated-border: #f3b27a;
    --lp-pane-cap-blocked-bg:   #fde8e8;  --lp-pane-cap-blocked-ink:   #6a1a1a;  --lp-pane-cap-blocked-border:   #f0bfbf;
    /* Pressed/active pill (pillSelector aria-pressed) + strong ink (refresh hover). */
    --lp-pane-pill-active-bg:   var(--lp-pane-amber-500);  --lp-pane-pill-active-ink: #1a1814;  --lp-pane-pill-active-border: #d9a800;
    --lp-pane-ink-strong:       #000000;

    --lp-pane-radius:          10px;
    --lp-pane-radius-sm:       6px;
    --lp-pane-shadow:          0 1px 2px rgba(17, 24, 39, 0.04),
                               0 1px 3px rgba(17, 24, 39, 0.02);

    --lp-pane-pad-x:           16px;
    --lp-pane-pad-y:           12px;
    --lp-pane-cell-pad-y:      10px;

    --lp-pane-gap:             4px;

    /* Label-column width inside the expander drawer (and anywhere
       else a label:value grid is used). */
    --lp-pane-stack-label-col: 120px;
}

/* Container-box layout applies only to the .lp-pane outer wrapper —
   NOT to the modal, which has its own dimensions, border, and
   positioning driven by .lp-pane-modal[open] further down. */
.lp-pane {
    box-sizing: border-box;
    /* Query container so pane @container breakpoints respond to the pane's own
       (iframe/host) width, not the viewport — phase iii responsive primitives. */
    container-type: inline-size;
    font-family: var(--lp-pane-font-family);
    font-size: var(--lp-pane-font-size);
    line-height: var(--lp-pane-line-height);
    color: var(--lp-pane-ink);
    background: var(--lp-pane-bg);
    border: 1px solid var(--lp-pane-outer-border);
    border-radius: var(--lp-pane-radius);
    box-shadow: var(--lp-pane-shadow);
    overflow: hidden;
    /* No external margin — spacing between stacked panes is owned by the host
       container (gap), not the component. (Removed margin-bottom:16px, which
       caused a gap + doubled bottom border inside a wrapping frame.) */
}

/* Reset box-sizing for all descendants so host-page resets don't
   corrupt our internal layout maths. */
:where(.lp-pane) *,
:where(.lp-pane) *::before,
:where(.lp-pane) *::after {
    box-sizing: border-box;
}

/* Screen-reader-only utility: visually hidden but exposed to assistive tech.
   Used for chart data-table alternatives (a navigable text equivalent of a
   visualisation) and other off-screen labels. Standard clip technique — stays
   focusable-safe (no display:none) but takes no visual space. */
.lp-sr-only {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------------
   Head — service label + region/source meta row.
   --------------------------------------------------------------------- */

:where(.lp-pane-head) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: var(--lp-pane-pad-y) var(--lp-pane-pad-x);
    background: var(--lp-pane-head-bg);
    border-bottom: 1px solid var(--lp-pane-amber-900);
}

:where(.lp-pane-head-left) {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;   /* allow the title to ellipsise */
}

:where(.lp-pane-head-right) {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}

:where(.lp-pane-title) {
    font-weight: 600;
    color: var(--lp-pane-head-ink);
    letter-spacing: -0.005em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

:where(.lp-pane-meta) {
    color: var(--lp-pane-head-meta-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

:where(.lp-pane-meta .lp-pane-meta-sep) {
    margin: 0 6px;
    /* Separator stays slightly faded so the meta text itself — now
       full amber-900 — reads as the dominant element. */
    color: rgba(61, 33, 0, 0.45);
}

/* Provider badge — small rounded pill at the start of the pane head.
   Background + foreground colours are set inline from
   PROVIDER_BADGES in sd-embed.js so an embedder can't override one
   half and break contrast. */
:where(.lp-pane-badge) {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
    line-height: 1.3;
    white-space: nowrap;
    text-transform: uppercase;
    flex-shrink: 0;
}

/* Sample badge — rendered next to the service title whenever the
   SD response carries source === "mock". Subtle by design and the
   sole visual indicator of mock state since the diagonal-stripe
   modifiers were removed 2026-05-29 (catalog pages render hundreds
   of these; the badge is enough). */
:where(.lp-pane-sample-badge) {
    display: inline-flex;
    align-items: center;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 3px;
    background: var(--lp-pane-amber-100, #fef3c7);
    color: var(--lp-pane-amber-800, #92400e);
    border: 1px solid var(--lp-pane-amber-300, #fcd34d);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    line-height: 1.3;
    text-transform: uppercase;
    flex-shrink: 0;
    cursor: help;
}

/* Refresh button — lives in the pane head's right slot, shows a
   live countdown until the cache's fresh_until has elapsed, then
   becomes an active "Refresh" affordance. Data-state transitions
   (locked / ready / busy) are driven by sd-embed.js's
   tickRefreshButton(). Tier / budget denials are silently
   downgraded server-side; there is no UI affordance for them. */
:where(.lp-pane-refresh) {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px 10px;
    border: 1px solid transparent;
    border-radius: 999px;
    font: inherit;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease,
                color 120ms ease;
    background: rgba(61, 33, 0, 0.08);
    color: var(--lp-pane-amber-900);
    border-color: rgba(61, 33, 0, 0.15);
    white-space: nowrap;
}

:where(.lp-pane-refresh[data-state="locked"]) {
    /* Countdown running — not clickable, dimmed. */
    cursor: default;
    background: transparent;
    color: rgba(61, 33, 0, 0.65);
    border-color: rgba(61, 33, 0, 0.20);
}

:where(.lp-pane-refresh[data-state="ready"]) {
    /* Cache past fresh_until — solid "Refresh" button. */
    background: var(--lp-pane-amber-900);
    color: var(--lp-pane-bg);
    border-color: var(--lp-pane-amber-900);
}

:where(.lp-pane-refresh[data-state="ready"]:hover) {
    background: var(--lp-pane-ink-strong);
    border-color: var(--lp-pane-ink-strong);
}

:where(.lp-pane-refresh[data-state="busy"]) {
    cursor: progress;
    background: rgba(61, 33, 0, 0.08);
    color: var(--lp-pane-amber-900);
    border-color: rgba(61, 33, 0, 0.25);
}

:where(.lp-pane-refresh[data-state="busy"] .lp-pane-refresh-icon) {
    animation: lp-pane-spin 0.9s linear infinite;
}

:where(.lp-pane-refresh-icon) {
    display: block;
    flex-shrink: 0;
}

/* ---------------------------------------------------------------------
   Body — a table wrapper. Horizontal scroll is a fallback only;
   the responsive-row stack (below) is the primary narrow-screen story.
   --------------------------------------------------------------------- */

:where(.lp-pane-body) {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Body padding for panes built via lpPane.shell() — gives header
   boxes, pill selectors, lists, etc. breathing room from the pane
   edges. NOT applied to the bare .lp-pane-body used by sd-embed
   inventory tables, because those tables add their own horizontal
   padding at the cell level (var(--lp-pane-pad-x) per <td>/<th>)
   and would double-pad. */
:where(.lp-pane-body--padded) {
    padding: 12px var(--lp-pane-pad-x);
}

/* -------- FILL MODE (data-lp-fill) — app-shell flex column ----------
   When the host gives the pane a definite height, the pane becomes a
   flex column that fills it: fixed head / single SCROLLING body / fixed
   footer. The body is the ONLY scroller (no nested viewports fighting),
   and flexbox sizes it — no height math. The width container query still
   drives the column reflow (inline-size containment is width-only). */
.lp-pane[data-lp-fill] {
    display: flex;
    flex-direction: column;
    height: 100%;
}
/* Every slot is fixed (flex:0) EXCEPT the body — the golden rule: exactly one
   scroller. Slot order = DOM order: head / toolbar(1a…) / body / controls(3a…) /
   footer. Multiple toolbar or controls slots just stack. */
.lp-pane[data-lp-fill] > :where(.lp-pane-head, .lp-pane-toolbar, .lp-pane-controls, .lp-pane-footer) {
    flex: 0 0 auto;
}
.lp-pane[data-lp-fill] > :where(.lp-pane-body) {
    flex: 1 1 auto;
    min-height: 0;          /* let the flex child shrink so it can scroll */
    overflow-y: auto;
    overflow-x: hidden;
}
/* Column headers stay put while the body scrolls (thead bg is opaque). */
.lp-pane[data-lp-fill] > .lp-pane-body .lp-xtable > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 1;
}

/* Always-present pane footer (DIV 3) — count/provenance today; the home
   for pagination controls or a close affordance. */
:where(.lp-pane-footer) {
    padding: 6px var(--lp-pane-pad-x);
    font-size: 11px;
    line-height: 1.4;
    color: var(--lp-pane-ink-dim);
    background: var(--lp-pane-head-bg);
    border-top: 1px solid var(--lp-pane-amber-700);
}
/* Paged footer: count on the left, pager on the right. The nested pager
   already sits inside the footer band, so strip its own bg / top divider /
   block padding (they'd double up with the footer's). */
:where(.lp-pane-footer-row) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
}
:where(.lp-pane-footer-count) { flex: 1 1 auto; min-width: 0; }
.lp-pane-footer .lp-r-pager {
    padding: 0;
    background: transparent;
    border-top: 0;
}

/* Toolbar slot (Div 1a…) — fixed pane controls ABOVE the body (search, filters).
   A quiet band with a bottom divider so it reads as chrome, not content. */
:where(.lp-pane-toolbar) {
    padding: 7px var(--lp-pane-pad-x);
    background: var(--lp-pane-head-bg);
    border-bottom: 1px solid var(--lp-pane-amber-700);
}
/* Controls slot (Div 3a…) — fixed pane controls BELOW the body, above the footer
   (e.g. a pager on its own strip rather than merged into the footer). */
:where(.lp-pane-controls) {
    padding: 6px var(--lp-pane-pad-x);
    background: var(--lp-pane-head-bg);
    border-top: 1px solid var(--lp-pane-amber-700);
}
.lp-pane-controls .lp-r-pager { padding: 0; background: transparent; border-top: 0; }

/* Filter toolbar — a search input + a live match count. */
:where(.lp-pane-filter) {
    display: flex;
    align-items: center;
    gap: 8px;
}
:where(.lp-pane-filter-input) {
    flex: 1 1 auto;
    min-width: 0;
    padding: 5px 9px;
    font: inherit;
    font-size: 12px;
    color: var(--lp-pane-ink);
    background: var(--lp-pane-bg);
    border: 1px solid var(--lp-pane-amber-700);
    border-radius: 5px;
}
:where(.lp-pane-filter-input:focus-visible) {
    outline: 2px solid var(--lp-pane-amber-900);
    outline-offset: 1px;
}
:where(.lp-pane-filter-count) {
    flex: 0 0 auto;
    font-size: 11px;
    color: var(--lp-pane-ink-dim);
}
/* Rows hidden by the client-side filter (row + its drawer). */
.lp-row-filtered { display: none !important; }

/* Resource-cap banner — emitted by sd-embed when the discovery
   response carries a resource_usage block with status ∈ {warn,
   truncated, blocked}. Sits at the top of .lp-pane-body, full
   width inside the pane. Colours are local (don't depend on the
   amber theme tokens) so they read the same across every theme. */
:where(.lp-pane-cap-banner) {
    padding: 10px 14px;
    font-size: 13px;
    line-height: 1.45;
    display: flex; align-items: flex-start; gap: 10px;
}
:where(.lp-pane-cap-banner .lp-pane-cap-icon) {
    flex: 0 0 auto;
    width: 18px; height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    font-weight: 700; font-size: 11px;
    line-height: 1;
}
:where(.lp-pane-cap-banner.warn) {
    background: var(--lp-pane-cap-warn-bg); color: var(--lp-pane-cap-warn-ink); border: 1px solid var(--lp-pane-cap-warn-border);
}
:where(.lp-pane-cap-banner.warn .lp-pane-cap-icon) {
    background: var(--lp-pane-cap-warn-border); color: var(--lp-pane-cap-warn-ink);
}
:where(.lp-pane-cap-banner.truncated) {
    background: var(--lp-pane-cap-truncated-bg); color: var(--lp-pane-cap-truncated-ink); border: 1px solid var(--lp-pane-cap-truncated-border);
}
:where(.lp-pane-cap-banner.truncated .lp-pane-cap-icon) {
    background: var(--lp-pane-cap-truncated-border); color: var(--lp-pane-cap-truncated-ink);
}
:where(.lp-pane-cap-banner.blocked) {
    background: var(--lp-pane-cap-blocked-bg); color: var(--lp-pane-cap-blocked-ink); border: 1px solid var(--lp-pane-cap-blocked-border);
}
:where(.lp-pane-cap-banner.blocked .lp-pane-cap-icon) {
    background: var(--lp-pane-cap-blocked-border); color: var(--lp-pane-cap-blocked-ink);
}
:where(.lp-pane-cap-banner strong) { font-weight: 600; }

:where(.lp-pane-table) {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    margin: 0;
}

:where(.lp-pane-table thead th) {
    font-weight: 600;
    text-align: left;
    font-size: 12px;
    letter-spacing: 0.01em;
    color: var(--lp-pane-thead-ink);
    padding: var(--lp-pane-cell-pad-y) var(--lp-pane-pad-x);
    border-bottom: 1px solid var(--lp-pane-amber-700);
    background: var(--lp-pane-thead-bg);
    white-space: nowrap;
}

:where(.lp-pane-table tbody td) {
    padding: var(--lp-pane-cell-pad-y) var(--lp-pane-pad-x);
    border-bottom: 1px solid var(--lp-pane-divider);
    vertical-align: top;
    font-size: var(--lp-pane-font-size);
    color: var(--lp-pane-ink);
    /* Permit long strings to wrap rather than force horizontal scroll. */
    overflow-wrap: anywhere;
    word-break: break-word;
}

:where(.lp-pane-table tbody tr:last-child td) {
    border-bottom: 0;
}

:where(.lp-pane-table tbody tr:hover td) {
    background: var(--lp-pane-row-hover-bg);
}

/* ---------------------------------------------------------------------
   Expandable rows — when a service has more attributes than fit as
   columns, the overflow attributes live in a hidden detail row that
   the user reveals by clicking anywhere on the primary row (or the
   chevron). Driven by the `lp-pane-row--expandable` class on the tr,
   `aria-expanded="true|false"`, and the `hidden` attribute on the
   following `.lp-pane-detail` row.
   --------------------------------------------------------------------- */

:where(.lp-pane-row--expandable) {
    cursor: pointer;
}

:where(.lp-pane-row--expandable:hover .lp-pane-expander) {
    color: var(--lp-pane-ink);
    background: var(--lp-pane-row-hover-bg);
}

:where(.lp-pane-th-expander),
:where(.lp-pane-expander-cell) {
    width: 36px;
    padding: 0 8px 0 0;
    text-align: right;
    /* Body cells default to vertical-align: top so long values start
       at the top of the row — good for data, wrong for the chevron.
       Centre the chevron on the row regardless of value height. */
    vertical-align: middle;
    white-space: nowrap;
}

:where(.lp-pane-expander) {
    appearance: none;
    background: transparent;
    border: 0;
    cursor: pointer;
    padding: 6px 8px;
    /* Brand amber instead of the neutral faint ink — a filled triangle
       in amber-700 reads clearly against every row bg (white, amber-50
       hover, amber-100 thead) without needing a decorative outline. */
    color: var(--lp-pane-amber-700);
    font: inherit;
    line-height: 0;
    border-radius: var(--lp-pane-radius-sm);
    transition: color 120ms ease, background-color 120ms ease;
}

:where(.lp-pane-expander:hover) {
    color: var(--lp-pane-amber-900);
}

:where(.lp-pane-chevron) {
    display: block;
    transition: transform 150ms ease;
    transform-origin: center;
}

:where(.lp-pane-row[aria-expanded="true"] .lp-pane-chevron) {
    transform: rotate(90deg);
}

/* Detail row — one per expandable row. `hidden` toggled by JS. */
/* Drawer wrapper: padding around the embedded detail table so it
   sits visually inside the pane rather than bleeding to the edges.
   The top is deliberately deeper than the bottom, giving the
   embedded table room to sit under the gradient shadow that
   simulates the primary row "casting shadow" into the drawer.
   Collapses to zero on narrow panes via .lp-pane-detail--tight. */
:where(.lp-pane-detail > td) {
    padding: 18px var(--lp-pane-pad-x) 14px;
    background:
        /* Top shadow — dark amber fading out over 12px to suggest
           the primary row above casting into the drawer. */
        linear-gradient(180deg,
            rgba(61, 33, 0, 0.14) 0,
            rgba(61, 33, 0, 0.04) 6px,
            transparent 14px),
        var(--lp-pane-detail-bg);
    /* Border-top matches the unexpanded inter-row divider — the
       sense of depth comes from the gradient + inset shadow below,
       NOT from a dark 1px line which reads as a hard edge rather
       than a shadow. Bottom is a heavy amber-900 line that demarks
       the end of this drawer so stacked expansions read as distinct
       rather than bleeding into each other. */
    border-top: 1px solid var(--lp-pane-divider);
    border-bottom: 2px solid var(--lp-pane-amber-900);
    /* Side rails — 1px amber-500 left/right so the drawer reads as a
       contained block rather than bleeding into the pane chrome on
       either side. Lighter than the table-frame's amber-700 so the
       drawer feels recessed rather than welded to the row above. */
    border-left: 1px solid var(--lp-pane-amber-500);
    border-right: 1px solid var(--lp-pane-amber-500);
    /* Second inset highlight at the very bottom returns the sense of
       the drawer rising back up to the next row. */
    box-shadow:
        inset 0 4px 6px -4px rgba(61, 33, 0, 0.18),
        inset 0 -1px 0 rgba(255, 255, 255, 0.45);
}

:where(.lp-pane-detail--tight > td) {
    padding: 10px 0 6px;
}

/* The embedded detail table: a real <table> with 1px grid lines so
   each row/value pair is visually bounded. 1px amber-700 outer
   border isolates it from the surrounding drawer wash. */
:where(.lp-pane-detail-table) {
    width: 100%;
    border-collapse: collapse;
    /* 2px outer border — deliberately heavier than the 1px inner
       grid lines so the embedded detail table reads as a discrete
       object against the surrounding drawer wash. */
    border: 2px solid var(--lp-pane-amber-700);
    border-radius: var(--lp-pane-radius-sm);
    overflow: hidden;
    font-size: 13px;
    background: var(--lp-pane-bg);
}

:where(.lp-pane-detail--tight .lp-pane-detail-table) {
    /* When padding is gone, borders flush to the drawer's amber-200
       top border — drop radius + outer horizontal borders so the
       edges line up with the primary table above. */
    border-radius: 0;
    border-left: 0;
    border-right: 0;
}

:where(.lp-pane-detail-table th),
:where(.lp-pane-detail-table td) {
    padding: 7px 12px;
    text-align: left;
    vertical-align: top;
    border: 1px solid var(--lp-pane-divider);
    font-weight: normal;
}

/* Key cell — same wash as the primary thead row for consistency. */
:where(.lp-pane-detail-table th) {
    background: var(--lp-pane-thead-bg);
    color: var(--lp-pane-amber-900);
    font-weight: 500;
    font-size: 12px;
    width: 1%;              /* shrink-to-fit */
    white-space: nowrap;
    letter-spacing: 0.01em;
}

/* Value cell — same wash as row hover, so the drawer reads as an
   extension of the interactive row above. */
:where(.lp-pane-detail-table td) {
    background: var(--lp-pane-amber-50);
    color: var(--lp-pane-ink);
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* Carved-depth hover — the same inset shadow recipe the pill-nav
   uses on the Sunlit site. Applied to each cell in the hovered row
   so the effect spans the full width. No layout shift. */
:where(.lp-pane-detail-table tbody tr:hover > *) {
    box-shadow: inset 0 1px 2px rgba(26, 24, 20, 0.09),
                inset 0 -1px 0 rgba(255, 255, 255, 0.6);
}

/* Padding cells used to align the final row of a wide drawer when
   overflow / tags count is odd. Both the th and td pad take the
   value-cell colour so the empty half-row is a uniform continuation
   of the table rather than a bright white slot that pulls the eye. */
:where(.lp-pane-detail-table .lp-pane-detail-pad) {
    background: var(--lp-pane-amber-50);
}

/* Shared section-head for any sub-table rendered inside the
   drawer (tags, security-group rules, route-table details, …). Sits
   across the full column span of its sub-table and provides the
   dark-amber title bar that distinguishes each section from the
   surrounding drawer wash. */
:where(.lp-pane-section-head) {
    background: var(--lp-pane-amber-700);
    color: var(--lp-pane-bg);
    font-weight: 600;
    font-size: 13px;
    padding: 8px 14px;
    text-align: left;
    letter-spacing: 0.02em;
    /* Override the generic detail-table th — the title bar needs to
       be solid amber edge to edge, not interrupted by the 1px
       divider border that the other ths use. */
    border: 0;
}

/* Empty-state row inside any drawer sub-table (tags or compound).
   Italic muted text on the amber-50 value-cell wash, matching the
   styling the "No policy" message uses in the modal. */
:where(.lp-pane-section-empty) {
    padding: 10px 14px;
    background: var(--lp-pane-amber-50);
    color: var(--lp-pane-ink-dim);
    font-style: italic;
    font-size: 12px;
}

/* Compound sub-tables — security-group rules, route-table details,
   network-ACL rules. Wrapped in an overflow-x container so a sub-
   table with long content (ARNs, CIDR ranges) on a narrow pane
   scrolls horizontally rather than squashing each column to
   unreadable widths. The column count itself is driven by the
   data — not a responsive 2/4-col switch like the extended-attrs
   table. */
:where(.lp-pane-compound-wrap) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Space between stacked drawer sub-tables. Applies uniformly to
   compound wrappers and tables, so the drawer's order
   (extended-attrs → compound sub-tables → tags) gets consistent
   14px gaps between adjacent sections without needing each
   section to know its neighbours. */
:where(.lp-pane-detail > td > *:not(:first-child)) {
    margin-top: 14px;
}

/* Null / empty value. */
:where(.lp-pane-nil) {
    color: var(--lp-pane-ink-faint);
}

/* ---------------------------------------------------------------------
   State / status pill — small coloured rectangle. Three signalling
   tones (green = ok, amber = transitional, red = bad) + a neutral
   fallback for state values we don't have tone metadata for. Used
   on columns whose id is `state`, `status`, or ends `_state` /
   `_status`. The amber tone reuses our brand palette for visual
   continuity with the rest of the pane.
   --------------------------------------------------------------------- */
:where(.lp-pane-state) {
    display: inline-flex;
    align-items: center;
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.55;
    white-space: nowrap;
    border: 1px solid transparent;
    /* Force display case so mixed-case API values (EC2's "running",
       Lambda's "Active", CloudWatch's "INSUFFICIENT_DATA") all read
       consistently. Underlying DOM text stays untouched so copy/paste
       picks up the real API value. */
    text-transform: lowercase;
}

:where(.lp-pane-state--ok) {
    background: var(--lp-pane-tag-success-bg);
    color:      var(--lp-pane-tag-success-ink);
    border-color: var(--lp-pane-tag-success-border);
}

:where(.lp-pane-state--warn) {
    background: var(--lp-pane-amber-200);
    color:      var(--lp-pane-amber-900);
    border-color: var(--lp-pane-amber-700);
}

:where(.lp-pane-state--bad) {
    background: var(--lp-pane-tag-critical-bg);
    color:      var(--lp-pane-tag-critical-ink);
    border-color: var(--lp-pane-tag-critical-border);
}

:where(.lp-pane-state--neutral) {
    background: var(--lp-pane-tag-neutral-bg);
    color:      var(--lp-pane-tag-neutral-ink);
    border-color: var(--lp-pane-tag-neutral-border);
}

/* ---------------------------------------------------------------------
   Policy link — cells in columns with format: 'policy_link' render
   as an inline link-styled button. Click pops up the shared
   native <dialog> (defined below) which fetches the policy
   document on demand via action=get_policy.
   --------------------------------------------------------------------- */
:where(.lp-pane-policy-link) {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 0;
    margin: 0;
    cursor: pointer;
    font: inherit;
    color: var(--lp-pane-amber-900);
    text-decoration: underline;
    text-decoration-color: rgba(61, 33, 0, 0.35);
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
    transition: text-decoration-color 120ms ease;
}

:where(.lp-pane-policy-link:hover) {
    text-decoration-color: currentColor;
}

/* ---------------------------------------------------------------------
   Policy viewer modal — shared native <dialog> appended to
   document.body. Native element gives us the browser's top-layer +
   ESC close + focus trap for free. Backdrop styled via ::backdrop.
   --------------------------------------------------------------------- */
.lp-pane-modal[open] {
    max-width: min(800px, 92vw);
    max-height: 82vh;
    width: min(800px, 92vw);
    padding: 0;
    border: 1px solid var(--lp-pane-outer-border);
    border-radius: var(--lp-pane-radius);
    box-shadow: 0 16px 48px rgba(17, 24, 39, 0.35),
                0 4px 12px rgba(17, 24, 39, 0.25);
    overflow: hidden;
    font-family: var(--lp-pane-font-family);
    font-size: var(--lp-pane-font-size);
    color: var(--lp-pane-ink);
    /* Fully opaque fallback colour before the body paints — browsers
       can briefly render the native dialog default (sometimes
       white-ish but not always, and on dark host pages that can
       flicker) before our descendant .lp-pane-modal-body takes
       over. */
    background: var(--lp-pane-bg);
    /* Structure the dialog as a column of head + body so the body
       is what scrolls while the head stays pinned. */
    display: flex;
    flex-direction: column;
}

/* Backdrop — a dark warm amber at ~50% so the host page recedes
   without being completely hidden. Keeping it slightly see-through
   keeps the modal feeling anchored to the page it opened from. */
.lp-pane-modal::backdrop {
    background: rgba(61, 33, 0, 0.5);
}

.lp-pane-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px 10px 16px;
    background: var(--lp-pane-head-bg);
    color: var(--lp-pane-head-ink);
    border-bottom: 1px solid var(--lp-pane-amber-900);
    flex-shrink: 0;
}

.lp-pane-modal-title {
    font-weight: 600;
    font-size: 14px;
    color: var(--lp-pane-head-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.lp-pane-modal-close {
    appearance: none;
    /* Fixed-size square with flex centring around the SVG icon.
       Using an SVG × (two crossed lines in a viewBox) rather than
       the U+00D7 glyph — character metrics put the glyph below
       line centre in most fonts, so a glyph-based × visually
       sits on the baseline even with flex alignment. SVG has a
       true geometric centre. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: 0;
    color: var(--lp-pane-head-ink);
    line-height: 0;
    cursor: pointer;
    border-radius: 4px;
    transition: background-color 120ms ease;
    flex-shrink: 0;
}

.lp-pane-modal-close:hover {
    background: rgba(61, 33, 0, 0.15);
}

.lp-pane-modal-body {
    flex: 1;
    overflow: auto;
    padding: 16px;
    background: var(--lp-pane-bg);
}

.lp-pane-policy-doc {
    margin: 0;
    font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo,
                 Consolas, Liberation Mono, monospace;
    font-size: 12px;
    line-height: 1.55;
    background: var(--lp-pane-detail-bg);
    color: var(--lp-pane-amber-900);
    padding: 14px 16px;
    border-radius: var(--lp-pane-radius-sm);
    border: 1px solid var(--lp-pane-border);
    white-space: pre-wrap;
    word-break: break-word;
}

.lp-pane-modal-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 40px 20px;
    color: var(--lp-pane-ink-dim);
    font-size: 13px;
}

.lp-pane-modal-error {
    padding: 14px 16px;
    border-radius: var(--lp-pane-radius-sm);
    background: var(--lp-pane-tag-critical-bg);
    color: var(--lp-pane-tag-critical-ink);
    border: 1px solid var(--lp-pane-tag-critical-border);
    font-size: 13px;
}

.lp-pane-modal-message {
    padding: 20px;
    text-align: center;
    color: var(--lp-pane-ink-dim);
    font-size: 13px;
    font-style: italic;
}

/* Small-viewport behaviour: take the full width with a small
   margin so narrow screens get usable space rather than a
   cramped dialog. */
@media (max-width: 520px) {
    .lp-pane-modal[open] {
        max-width: calc(100vw - 16px);
        width: calc(100vw - 16px);
        max-height: calc(100vh - 32px);
    }
    .lp-pane-modal-head { padding: 8px 8px 8px 12px; }
    .lp-pane-modal-body { padding: 12px; }
}

/* Object / array value rendered as mono code.
   NOT :where() here (unlike most primitive rules): .lp-pane-code renders a real
   <code> element, and a host page's own `code { background: … }` rule (bare
   element, specificity 0,0,1) would otherwise beat a zero-specificity :where()
   and repaint our cell — e.g. dynamiccloud's `code{background:var(--dc-bg-sunk)}`
   turned the JSON dark-on-dark on the dark testbed. The plain class (0,1,0) wins
   over any bare element selector, keeping the pane self-contained when embedded. */
.lp-pane-code {
    font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo,
                 Consolas, Liberation Mono, monospace;
    font-size: 12px;
    background: var(--lp-pane-code-bg);
    color: var(--lp-pane-code-ink);
    padding: 1px 6px;
    border-radius: var(--lp-pane-radius-sm);
    /* Wrap long unbreakable tokens (ARNs, keys) instead of forcing the drawer /
       cell wider with a horizontal scroll. pre-wrap keeps JSON newlines + indent;
       overflow-wrap:anywhere breaks a long token AND lets the cell shrink to the
       container (it reduces min-content, unlike break-word). */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    max-width: 100%;
}

/* ---------------------------------------------------------------------
   Empty / loading / error states.
   --------------------------------------------------------------------- */

:where(.lp-pane-empty) {
    padding: var(--lp-pane-pad-y) var(--lp-pane-pad-x);
    color: var(--lp-pane-ink-dim);
    font-size: 13px;
}

/* "Feature not enabled" / soft-error variant — server provides a
   contextual note via metadata.empty_state_note (e.g. Shield Standard
   active, RIs not subscribed). Amber-dashed treatment matches the
   bespoke panes' equivalent empty states (compute-optimizer, rightsize,
   ri-savings, free-tier). */
:where(.lp-pane-empty--note) {
    margin: var(--lp-pane-pad-y) var(--lp-pane-pad-x);
    padding: 16px;
    background: var(--lp-pane-amber-50, #fff8eb);
    border: 1px dashed var(--lp-pane-amber-300, #f5cf86);
    border-radius: 6px;
    color: var(--lp-pane-ink, #1f2937);
    line-height: 1.55;
}

:where(.lp-pane--loading .lp-pane-head) {
    border-bottom: 0;
}

:where(.lp-pane-loading-row) {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: var(--lp-pane-pad-y) var(--lp-pane-pad-x);
    color: var(--lp-pane-ink-dim);
    font-size: 13px;
}

:where(.lp-pane-spinner) {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid var(--lp-pane-border);
    border-top-color: var(--lp-pane-accent);
    animation: lp-pane-spin 0.8s linear infinite;
    flex-shrink: 0;
}

@keyframes lp-pane-spin {
    to { transform: rotate(360deg); }
}

:where(.lp-pane--error) {
    background: var(--lp-pane-err-bg);
    border-color: var(--lp-pane-err-border);
    box-shadow: none;
}

:where(.lp-pane--error .lp-pane-head) {
    background: transparent;
    border-bottom: 0;
}

:where(.lp-pane--error .lp-pane-title) {
    color: var(--lp-pane-err-ink);
}

:where(.lp-pane-error-body) {
    padding: 0 var(--lp-pane-pad-x) var(--lp-pane-pad-y);
    font-size: 13px;
    color: var(--lp-pane-err-ink);
}

/* ───── scrollRegion (G7) — bounded-height scroll with a bottom fade ───── */
/* Full-bleed to the pane edges (negative margin cancels the pane-body padding)
   with the padding restored INSIDE the scroll body. Two effects:
   (a) the vertical scrollbar sits in the right padding — to the RIGHT of the
       content + chevrons, never over them;
   (b) a full-bleed child (the expandableTable drawer, margin:0 -pad-x) now
       reaches exactly the scroll-body edge instead of overflowing it by pad-x
       each side (which was adding a stray horizontal scrollbar on feeds with
       expand-to-detail). Assumes the region is a direct child of the padded
       pane body. */
:where(.lp-pane-scroll) {
    position: relative;
    margin-left: calc(-1 * var(--lp-pane-pad-x));
    margin-right: calc(-1 * var(--lp-pane-pad-x));
}
:where(.lp-pane-scroll-body) {
    max-height: var(--lp-scroll-max, 280px);
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    padding-left: var(--lp-pane-pad-x);
    padding-right: var(--lp-pane-pad-x);
}
:where(.lp-pane-scroll-body:focus-visible) {
    outline: 2px solid var(--lp-pane-amber-700); outline-offset: -2px;
    border-radius: var(--lp-pane-radius-sm);
}
/* "scroll for more ↓" hint — a sticky bottom band that fades the content
   edge AND names the affordance. lpPane.wire toggles --hidden once the region
   isn't overflowing or has been scrolled to the bottom (so it never falsely
   implies more content). pointer-events:none so it never blocks scrolling. */
:where(.lp-pane-scroll-hint) {
    position: sticky;
    bottom: 0;
    display: flex;
    justify-content: center;
    pointer-events: none;
    padding: 14px 0 5px;
    margin-top: -8px;
    background: linear-gradient(to top, var(--lp-pane-bg) 45%, transparent);
    transition: opacity 160ms ease;
}
/* The label sits in a light-amber pill so it reads clearly over the list. */
:where(.lp-pane-scroll-hint span) {
    background: var(--lp-pane-amber-100, #fdecc8);
    color: var(--lp-pane-amber-900, #7a5a17);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 3px 12px;
    border-radius: 999px;
}
:where(.lp-pane-scroll-hint--hidden) { opacity: 0; }

/* ----- feedList (timestamped event feed) ----- */
:where(.lp-pane-feed-meta),
:where(.lp-pane-feed-time) {
    color: var(--lp-pane-meta-ink, #6b645a);
    font-size: 11px;
    white-space: nowrap;
}
:where(.lp-pane-feed-time) { font-variant-numeric: tabular-nums; }

/* ───── expandableTable (G2) — native table + disclosure drawer ─────
   A real <table> (table-layout:auto) so column widths are SHARED down every
   row: values sit LEFT-aligned under their header. The primary column (and any
   wrap:true column) wraps for long names; every other column stays on ONE line
   so short codes like "eu-west-1" size the column to themselves rather than
   fragmenting under a terse header. Mirrors the discovery inventory table.
   Keyboard-operable disclosure via a real <button>; the detail row's <td> is a
   recessed amber band. Non-responsive tables get a scroll-x wrapper. Phase iii. */
:where(.lp-xtable-wrap) { width: 100%; overflow-x: auto; }
:where(.lp-xtable) {
    width: 100%; border-collapse: separate; border-spacing: 0; margin: 0;
    font-size: 12.5px;
}
/* header — left-aligned column labels; native table shares their widths with
   the body cells below, so every row aligns to the header. */
:where(.lp-xtable > thead > tr > th) {
    text-align: left; vertical-align: bottom;
    /* 7px symmetric h-padding: a consistent non-zero padding-left so no column
       (esp. inside a framed embedded sub-table) sits flush against the table
       edge, while the 14px inter-column gap (7+7) is unchanged. Flush inventory
       tables still override the first/last cell to pad-x via .lp-xtable--flush. */
    padding: 9px 7px; white-space: nowrap;
    /* Match the discovery-inventory thead: light-amber band, mixed CASE
       PRESERVED (not all-caps), amber-900 ink, 12px. (Ported from
       .lp-pane-table thead th so the primitive tables read identically.) */
    font-size: 12px; letter-spacing: 0.01em;
    font-weight: 600; color: var(--lp-pane-thead-ink);
    background: var(--lp-pane-thead-bg);
    border-bottom: 1px solid var(--lp-pane-amber-700);
}
:where(.lp-xtable-th-expander) { width: 30px; padding: 0; }
/* Sticky header when the table is in a capped scroll/paged viewport
   (data-sticky-header, set by expandableTable's scroll/paged mode). The th are
   otherwise transparent, so give them the SAME opaque amber thead-bg the normal
   header uses — NOT white — so the sticky header keeps the theme colour while
   still covering the rows scrolling underneath. (Was --lp-pane-bg / white, which
   showed only in scroll+paged mode; fixed 2026-07-14.) */
:where(.lp-xtable[data-sticky-header] > thead > tr > th) {
    position: sticky; top: 0; z-index: 2; background: var(--lp-pane-thead-bg);
}
/* section-header row (expandableTable `sections`) — a light-blue band matching
   the subhead, so grouped tables share one aligned column layout. */
/* NB: NOT wrapped in :where() — it must out-specify the general body-cell rule
   `:where(.lp-xtable > tbody > tr > td)` below (which zeroes padding-left to keep
   the first data column flush). A section td IS a body td, so at equal (:where,
   zero) specificity the later general rule would win and drop this inset. The
   0,1,1 selector beats it without touching data cells. Same for -section-empty. */
.lp-xtable-section > td {
    /* Inset the label + count by pad-x so the text sits inside the full-width
       blue band, matching the standalone .lp-pane-subhead (which insets its
       text by the same pad-x). The band itself still spans the whole table. */
    padding: 9px var(--lp-pane-pad-x, 16px) 6px;
    background: var(--lp-pane-blue-50, #eff6ff);
    /* The shared headline-banner border on ALL 4 sides so the section band reads
       as a defined bar rather than a pale wash. border-collapse:separate on
       .lp-xtable renders the box cleanly. */
    border: 1px solid var(--lp-pane-headline-border, #93c5fd);
    font-size: 13px; font-weight: 600; color: var(--lp-pane-blue-900, #1e3a8a);
}
:where(.lp-xtable-section:first-child > td) { border-top: none; }
:where(.lp-xtable-section-count) {
    float: right; font-weight: 400; color: var(--lp-pane-ink-dim, #8a8378);
    font-variant-numeric: tabular-nums;
}
.lp-xtable-section-empty > td {
    padding: 11px var(--lp-pane-pad-x, 16px); color: var(--lp-pane-ink-dim, #8a8378); font-style: italic;
}
/* body cells — top-aligned so wrapped values start at the row top; 14px
   inter-column gap via right padding (first column flush-left to the table). */
:where(.lp-xtable > tbody > tr > td) {
    /* Single-line cells (pills, sparklines, values) centre against a tall row
       (e.g. a two-line primary name/meta); the wrapping columns below override
       back to top so wrapped text still starts at the row top. */
    padding: 11px 7px; vertical-align: middle;   /* 7px symmetric — see thead note */
    color: var(--lp-pane-ink);
}
/* Data cells stay on ONE line by default (short codes don't fragment). Only the
   primary column and explicit wrap:true columns wrap; a genuinely long value in
   a nowrap column widens the column (scroll wrapper is the fallback). */
:where(.lp-xtable-cell) { white-space: nowrap; }
:where(.lp-xtable-cell--wrap) {
    white-space: normal; overflow-wrap: anywhere; word-break: break-word;
    vertical-align: top;   /* wrapped/primary text starts at the row top */
}
/* …EXCEPT a non-primary wrap column whose content is a status pill: the
   column is wrap:true only so a long pill LABEL can break onto two lines, but
   the pill should still CENTRE against a taller row (a multi-line primary
   name/ARN), not ride at the top. Genuine multi-line text columns keep top.
   Real specificity (not :where) so it beats the zero-specificity rule above. */
.lp-xtable-cell--wrap:not(.lp-xtable-cell--primary):has(> .lp-pane-tag) {
    vertical-align: middle;
}
:where(.lp-xtable-cell--primary) { font-weight: 600; color: var(--lp-pane-ink); }
/* Non-primary text columns clip a monster value (ellipsis) so it can't set the
   column width and squeeze the primary column. max-content is capped at ~34ch,
   so table-layout:auto gives these columns at most that and hands the rest to
   the (wrapping) primary. The DOM text is intact → copy-paste keeps the full
   value; a title tooltip + the collapse-drawer carry it in full. */
:where(.lp-xtable-clip) {
    display: block; max-width: 34ch;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* num = tabular digits, still LEFT-aligned (every column left by default). */
:where(.lp-xtable-th--num), :where(.lp-xtable-cell--num) { font-variant-numeric: tabular-nums; }
/* Full-bleed (flush) mode — the table sits edge-to-edge inside a bare
   .lp-pane-body (no --padded box), matching the discovery-inventory layout. Only
   the EDGE cells gain side padding (--lp-pane-pad-x) so leading/trailing content
   isn't flush against the pane edge; the primitive's 14px inter-column spacing is
   unchanged. The drawer row already carries --lp-pane-pad-x, so it's excluded.
   Real specificity (not :where) so padding-left/right beat the base `padding`
   shorthand regardless of source order. */
.lp-xtable--flush > thead > tr > th:first-child,
.lp-xtable--flush > tbody > tr.lp-xtable-row > td:first-child {
    padding-left: var(--lp-pane-pad-x);
}
.lp-xtable--flush > thead > tr > th:last-child,
.lp-xtable--flush > tbody > tr.lp-xtable-row > td:last-child {
    padding-right: var(--lp-pane-pad-x);
}
/* When the expander column is reclaimed (--noexpand-all hides it), move the
   right-edge padding to the now-last visible cell. */
.lp-xtable--flush.lp-xtable--noexpand-all > thead > tr > th:nth-last-child(2),
.lp-xtable--flush.lp-xtable--noexpand-all > tbody > tr.lp-xtable-row > td:nth-last-child(2) {
    padding-right: var(--lp-pane-pad-x);
}
/* row divider on the body cells (border-collapse:separate → border on the td) */
:where(.lp-xtable-row > td) { border-bottom: 1px solid var(--lp-pane-border); }
/* Trailing non-expandable row drops its divider (an expandable row is never
   :last-child — its hidden detail row follows it). */
:where(.lp-xtable-row:last-child > td) { border-bottom: none; }
/* Open row: drop its divider — the drawer band below is the section-end line. */
:where(.lp-xtable-row--expandable:has(.lp-xtable-expander[aria-expanded="true"]) > td) {
    border-bottom: none;
}
:where(.lp-xtable-row--expandable) { cursor: pointer; }
:where(.lp-xtable-row--expandable:hover > td) { background: var(--lp-pane-row-hover-bg); }
/* A responsive row with NO drawer content at the current width (no custom
   detail AND no column collapsed into it) drops its chevron and stops looking
   interactive — set per-width by the responsive apply(). Prevents a click on an
   empty drawer. Parity with the discovery renderer's per-row rowHasDrawer(). */
:where(.lp-xtable-row--noexpand .lp-xtable-expander) { display: none; }
:where(.lp-xtable-row--noexpand) { cursor: default; }
:where(.lp-xtable-row--noexpand:hover > td) { background: transparent; }
/* When NO row can expand at this width, reclaim the reserved expander column. */
:where(.lp-xtable--noexpand-all .lp-xtable-th-expander),
:where(.lp-xtable--noexpand-all .lp-xtable-expander-cell) { display: none; }
/* expander column — fixed hard-right; a real <button> carrying the chevron.
   3px right padding keeps the button off the table edge so its 2px focus ring
   isn't clipped by the pane's rounded-corner overflow. */
:where(.lp-xtable-expander-cell) {
    width: 30px; padding: 0 3px 0 4px; text-align: right;
    vertical-align: middle; white-space: nowrap;
}
:where(.lp-xtable-expander) {
    appearance: none; background: transparent; border: 0; cursor: pointer;
    padding: 6px 2px; line-height: 0; color: var(--lp-pane-ink-dim);
    border-radius: var(--lp-pane-radius-sm); transition: color 120ms ease;
}
:where(.lp-xtable-expander:hover) { color: var(--lp-pane-amber-900); }
:where(.lp-xtable-expander:focus-visible) {
    color: var(--lp-pane-amber-900);
    box-shadow: 0 0 0 2px var(--lp-pane-amber-700);
    outline: 2px solid transparent; outline-offset: 1px;  /* visible in forced-colors */
}
/* Rotate the (square) glyph about its own centre → spins in place. */
:where(.lp-xtable-chevron) {
    display: block; transition: transform 0.15s ease; transform-origin: center;
}
:where(.lp-xtable-expander[aria-expanded="true"] .lp-xtable-chevron) { transform: rotate(90deg); }
:where(.lp-xtable-footnote) { padding: 8px 0 2px; font-size: 11px; color: var(--lp-pane-ink-dim); }
@container (max-width: 560px) {
    :where(.lp-xtable) { font-size: clamp(10.5px, 2.7cqi, 12.5px); }
    :where(.lp-xtable > thead > tr > th) { font-size: clamp(9px, 2.2cqi, 10.5px); }
}
/* detail row — its <td> (colspan across the table) is the recessed amber band:
   top shadow-gradient (row above casting in), amber-900 close line, side rails. */
:where(.lp-xtable-detailrow[hidden]) { display: none; }
:where(.lp-xtable-detailrow > td) {
    padding: 14px var(--lp-pane-pad-x) 13px;
    background:
        linear-gradient(180deg, rgba(61,33,0,0.13) 0, rgba(61,33,0,0.04) 6px, transparent 14px),
        var(--lp-pane-amber-50);
    border-top: 1px solid var(--lp-pane-border);
    border-bottom: 2px solid var(--lp-pane-amber-900);
    border-left: 1px solid var(--lp-pane-amber-300);
    border-right: 1px solid var(--lp-pane-amber-300);
    box-shadow: inset 0 4px 6px -4px rgba(61,33,0,0.18);
}
:where(.lp-xtable-drawer) { font-size: 12px; line-height: 1.55; color: var(--lp-pane-ink); }
/* The drawer lives in a colspan detail cell. Left alone, its content's MAX-content
   width — most sharply the flex tag chips, whose max-content is "every chip on one
   line" and grows with the tag count — feeds into the parent's table-layout:auto
   and resizes the columns the moment the row expands (dramatic on tag-heavy panes;
   panes with no tags are unaffected). width:0 makes its contribution to the column
   algorithm zero; min-width:100% still renders it the full cell width, and the
   content (chips / embedded tables) wraps within. Real specificity, not :where. */
.lp-xtable-drawer { width: 0; min-width: 100%; }
/* responsiveColumns: a collapsed column is hidden from the row + header (plain
   class so it beats the cell default) and its value appears as a row in the ONE
   merged drawer table. That table hides itself when it has no visible rows. */
.lp-xcol-collapsed { display: none; }
:where(.lp-xtable-drawer > .lp-xtable-detail-table:not(:has(tr:not([hidden])))) { display: none; }
/* Separator between the merged column table and trailing drawer content (an
   embedded pane/chart): 8px · 1px amber-900 rule (full column width) · 8px.
   Hidden when the column table has no visible rows (nothing above to divide). */
:where(.lp-xtable-drawer-sep) { height: 1px; margin: 8px 0; background: var(--lp-pane-amber-900); }
:where(.lp-xtable-detail-table:not(:has(tr:not([hidden]))) + .lp-xtable-drawer-sep) { display: none; }
/* Drawer SECTION — one self-contained block per data set after the extended
   attributes (tags, routes, subnet-associations…). data-section names it so the
   DOM is self-documenting; the top margin keeps sections from running together.
   GOLDEN RULE: anything that alters width/display (the full-bleed title, an
   embedded table) is contained + CLOSED within this div — no leaking to the next
   section, so a broken section is trivially isolated. */
.lp-xtable-drawer-section { margin-top: 12px; }
/* Section title — full-bleed amber band (thead-bg + 1px amber-700 rules), the
   SAME treatment for tags and every embedded sub-table. Negative side margins
   cancel the drawer <td>'s pad-x so it reaches the drawer edges (matches the
   pane's table head). */
.lp-xtable-drawer-section-title {
    margin: 0 calc(-1 * var(--lp-pane-pad-x)) 8px;
    padding: 5px var(--lp-pane-pad-x);
    background: var(--lp-pane-thead-bg);
    border-top: 1px solid var(--lp-pane-amber-700);
    border-bottom: 1px solid var(--lp-pane-amber-700);
    font-size: 11.5px; font-weight: 600; color: var(--lp-pane-amber-900);
}
/* Count matches the label's weight + colour (was a lighter dim grey) so
   "Tags: 6" reads as one heading and keeps AA contrast on the amber band. */
.lp-xtable-drawer-section-count { font-weight: 600; color: var(--lp-pane-amber-900); }
/* Drop the primitive's drawer separator immediately before the first section —
   the section's own title band + margin do the dividing. */
.lp-xtable-drawer-sep:has(+ .lp-xtable-drawer-section) { display: none; }
/* An embedded sub-table inside a section (route/SG/NACL rules) reads like the
   extended-attribute table: 2px dark-amber frame, rounded, with explicit white
   rows (over the drawer's amber wash). */
.lp-xtable-drawer-section .lp-xtable {
    border: 2px solid var(--lp-pane-amber-700);
    background: var(--lp-pane-bg);
}
.lp-xtable-drawer-section .lp-xtable > tbody > tr > td { background: var(--lp-pane-bg); }

/* Wide drawer (opt-in via expandableTable wideDrawer) — the attribute list
   reflows to TWO key/value pairs per row (4 columns) when the pane is wide, and
   ONE pair per row (2 columns) below. Driven by a @container query (the .lp-pane
   is the query container), so it reflows with the pane width without a re-render.
   Mirrors the discovery renderer's wide/narrow drawer. */
.lp-xtable-detail-table--wide tbody {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);   /* narrow: 1 pair / row */
}
@container (min-width: 600px) {
    .lp-xtable-detail-table--wide tbody {
        grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr);
    }
}
/* Let each <tr>'s th/td flow directly into the grid (hidden overflow rows keep
   display:none, so they don't leave gaps). Grid drops border-collapse, so redo
   the separators as border-bottom only — the amber-key / white-value backgrounds
   supply the vertical division. */
.lp-xtable-detail-table--wide tr:not([hidden]) { display: contents; }
.lp-xtable-detail-table--wide th,
.lp-xtable-detail-table--wide td { border: 0; border-bottom: 1px solid var(--lp-pane-divider); }
/* The base th `width: 1%` is a TABLE shrink-to-fit hack (collapse the key column
   to content). In this grid it would instead pin the key cell to 1% of the grid,
   so its amber wash fills only a sliver — reset to auto so the cell stretches to
   fill its `max-content` track (which already does the shrink-to-fit). */
.lp-xtable-detail-table--wide th { width: auto; }

/* Tags section chips read like the attribute rows: key on the amber thead wash,
   value on white, the drawer table's font size, roomier padding + gap. Scoped to
   the tags section (migrated panes' tagChips elsewhere are untouched). */
/* Even the vertical breathing room around the chips: the drawer <td> gives ~14px
   below the last section, but the title band only gave 8px above — bump it to 14px
   so the chips sit symmetrically (was 8 above / 14 below). */
.lp-xtable-drawer-section[data-section="tags"] .lp-xtable-drawer-section-title { margin-bottom: 14px; }
.lp-xtable-drawer-section[data-section="tags"] .lp-pane-tagchips { gap: 8px; }
.lp-xtable-drawer-section[data-section="tags"] .lp-pane-tagchip { font-size: 12.5px; }
.lp-xtable-drawer-section[data-section="tags"] .lp-pane-tagchip-k {
    background: var(--lp-pane-thead-bg); color: var(--lp-pane-amber-900); padding: 3px 9px;
}
.lp-xtable-drawer-section[data-section="tags"] .lp-pane-tagchip-v {
    background: var(--lp-pane-bg); color: var(--lp-pane-ink); padding: 3px 9px;
}

/* ----- structured cell renderers (expandableTable column render hints) ----- */
:where(.lp-pane-cell-count) { color: var(--lp-pane-meta-ink, #6b645a); font-variant-numeric: tabular-nums; }
:where(.lp-pane-meta-dash) { color: var(--lp-pane-ink-dim, #a89f8c); }
:where(.lp-pane-tagchips) { display: flex; flex-wrap: wrap; gap: 5px; }
:where(.lp-pane-tagchip) {
    display: inline-flex; font-size: 11px; line-height: 1.5;
    /* amber-700 to match the expanded-attributes detail-table border (was the
       weak amber-300 hairline). */
    border: 1px solid var(--lp-pane-amber-700); border-radius: 5px; overflow: hidden;
    max-width: 100%;   /* §15: never overflow the container */
}
:where(.lp-pane-tagchip-k) {
    padding: 1px 6px; background: var(--lp-pane-amber-50);
    color: var(--lp-pane-amber-900); font-weight: 600;
    flex: 0 0 auto;   /* key never truncates */
}
:where(.lp-pane-tagchip-v) {
    padding: 1px 6px; color: var(--lp-pane-ink-2, #3a352e);
    /* §15: bound a long value with an ellipsis (full value on hover via the
       chip's title attr) instead of clipping characters off the pill edge. */
    max-width: 26ch; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:where(.lp-pane-subtable) {
    width: 100%; border-collapse: collapse; font-size: 12px;
    background: var(--lp-pane-bg);
    border: 1px solid var(--lp-pane-amber-300); border-radius: 6px; overflow: hidden;
}
:where(.lp-pane-subtable th), :where(.lp-pane-subtable td) {
    text-align: left; padding: 4px 9px; border-bottom: 1px solid var(--lp-pane-border);
}
:where(.lp-pane-subtable th) {
    background: var(--lp-pane-amber-100); color: var(--lp-pane-amber-900); font-weight: 600;
}
:where(.lp-pane-subtable tr:last-child td) { border-bottom: none; }
:where(.lp-pane-subtable th.num), :where(.lp-pane-subtable td.num) {
    text-align: right; font-variant-numeric: tabular-nums;
}
/* recursion defence (bounded nesting, XT_MAX_NEST=1) — one nested table is
   interactive (parent row → child table rows), but a row TWO drawers deep is
   flat: hide its chevron and neutralise the expander so it reads as a plain row
   (the click handler already refuses to toggle rows that deep). */
:where(.lp-xtable-drawer .lp-xtable-drawer .lp-xtable-chevron) { display: none; }
:where(.lp-xtable-drawer .lp-xtable-drawer .lp-xtable-expander) { cursor: default; }
:where(.lp-xtable-drawer .lp-xtable-drawer .lp-xtable-row--expandable) { cursor: default; }
:where(.lp-xtable-drawer .lp-xtable-drawer .lp-xtable-row--expandable:hover > td) { background: transparent; }
/* embedded detail table (detailTable) — pane-bg cells on the band */
:where(.lp-xtable-detail-table) {
    width: 100%;
    /* SEPARATE, not collapse. A border-collapse:collapse table renders
       border-radius + overflow + the outer border INCONSISTENTLY across engines:
       per spec, radius/overflow have no effect on a collapsed table and the outer
       border is subject to collapse conflict resolution — so Firefox obeys (no
       radius; the 2px table border wins → amber) while Chrome/WebKit apply the
       radius and clip the amber border to the white cell/background edge. The
       separate model makes all four behave per spec in every browser.
       (2026-07-15 cross-browser fix — Firefox vs Chrome/Safari/Vivaldi divergence.) */
    border-collapse: separate; border-spacing: 0; font-size: 12.5px;
    border: 2px solid var(--lp-pane-amber-700); border-radius: var(--lp-pane-radius-sm);
    overflow: hidden; background: var(--lp-pane-bg);
}
:where(.lp-xtable-detail-table th), :where(.lp-xtable-detail-table td) {
    padding: 7px 12px; text-align: left; vertical-align: top;
    /* Row separators only — vertical column division comes from the amber-key /
       white-value backgrounds (same as the --wide grid variant). All-around cell
       borders would double under border-spacing:0. */
    border: 0; border-bottom: 1px solid var(--lp-pane-divider); font-weight: normal;
}
/* Last row: no bottom rule (it would sit against the table's 2px border). */
:where(.lp-xtable-detail-table tbody tr:last-child > *) { border-bottom: 0; }
:where(.lp-xtable-detail-table th) {
    background: var(--lp-pane-thead-bg); color: var(--lp-pane-amber-900);
    font-weight: 500; font-size: 11.5px; width: 1%; white-space: nowrap;
}
:where(.lp-xtable-detail-table td) {
    background: var(--lp-pane-bg); color: var(--lp-pane-ink);
    overflow-wrap: anywhere; word-break: break-word;
}
:where(.lp-xtable-detail-table tbody tr:hover > *) {
    box-shadow: inset 0 1px 2px rgba(26,24,20,0.09), inset 0 -1px 0 rgba(255,255,255,0.6);
}

/* Respect reduced motion. Spinner is slowed (a frozen spinner reads as broken);
   all other transitions are effectively disabled. */
@media (prefers-reduced-motion: reduce) {
    :where(.lp-pane-spinner) {
        animation-duration: 2.4s;
    }
    :where(.lp-pane) *,
    :where(.lp-pane) *::before,
    :where(.lp-pane) *::after {
        transition-duration: 0.001ms !important;
    }
}

/* Static render mode — for PNG/email/Slack via a headless snapshot of the
   embed (?static=1 sets .lp-static on the root). Browser-only affordances
   (hover readouts, refresh, interactive chevrons) are hidden; primitives that
   carry [data-browser-only] opt in. Phase iii scaffolding. */
.lp-static [data-browser-only] { display: none !important; }

/* Narrow-container behaviour is handled by sd-embed.js's
   ResizeObserver-driven primary-column count: on tight panes we show
   1 or 2 primary columns and move the rest into the expander drawer.
   No viewport-based @media rule is needed — which means host-page
   layout changes (e.g. a sidebar opening and halving the pane width)
   still drive a clean re-render rather than a visual-only reflow. */

/* ─── Stacked multi-binding render — Doc 42 Window 2 ─────────
   When a pane uses `data-lp-match=…` and the selector resolves to
   N > 1 bindings, sd-embed.js wraps the target div in a
   `.lp-pane--stacked` outer and renders one `.lp-stacked-card`
   per binding inside it. Each card carries a small banner that
   labels which binding it represents (alias + region); the rest
   of the card is a full pane head + body. The outer container
   provides only the vertical gap; individual cards keep all the
   normal pane chrome. */

:where(.lp-pane--stacked) {
    display: flex;
    flex-direction: column;
    gap: 12px;
    /* Outer doesn't render its own card chrome — children carry it. */
    background: transparent;
    border: 0;
}

:where(.lp-stacked-card) {
    background: var(--lp-pane-bg);
    border: 1px solid var(--lp-pane-border);
    border-radius: var(--lp-pane-radius, 10px);
    overflow: hidden;
}

:where(.lp-stacked-card-banner) {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px var(--lp-pane-pad-x);
    background: var(--lp-pane-amber-100, #fef6e0);
    border-bottom: 1px solid var(--lp-pane-border, rgba(0,0,0,0.08));
    font-size: 11px;
    color: var(--lp-pane-amber-900, #5a4500);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

:where(.lp-stacked-card-alias) {
    font-weight: 600;
}

:where(.lp-stacked-card-region) {
    color: var(--lp-pane-head-meta-ink, #7a6730);
    text-transform: none;
    letter-spacing: 0;
    margin-left: auto;
}

/* Strip the inner pane chrome inside a stacked card so the card's
   outer frame is the only visible chrome. Three things to override:
     1. The INNER `.lp-pane` (innerHost wrapper that renderTable
        mutates) loses border + radius + shadow.
     2. The OUTER `.lp-pane.lp-pane--stacked` (the original
        container that renderStacked targets) also loses radius
        + shadow — without this, the outer wrapper's rounded
        box-shadow halos around the entire stack and reads as a
        second rounded frame.
     3. The inner pane head, just in case any future change adds
        explicit corner rounding to it.
   Real selectors (not :where()) — the dynamic class swap done by
   renderTable means we've been fighting equal-specificity battles
   with :where(). Bumping to plain class selectors guarantees we
   win the cascade. */
.lp-pane.lp-pane--stacked {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}
.lp-stacked-card-body .lp-pane,
.lp-stacked-card-body.lp-pane {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}
.lp-stacked-card-body .lp-pane-head,
.lp-stacked-card-body.lp-pane > .lp-pane-head {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

/* ============================================================
   lp-pane composition primitives (window.lpPane, 2026-05-26)
   ============================================================
   Styles for the canonical primitive elements emitted by
   embed/lp-pane.js. Compound panes built on the new pane builder
   share these — drift between providers (different paddings,
   different colours per cloud) is impossible because there's one
   stylesheet driving every primitive.

   See embed/lp-pane.js for the JS API. Keep the two in sync. */

/* Mock-data visual treatment used to live here: lp-pane--demo-security
   (loud amber stripe + DEMO DATA corner tag, applied automatically by
   lpPane.securityShell() in mock mode) and lp-pane--mock (lighter
   white+amber stripe, applied to data-lp-source="mock" hosts).
   Both removed 2026-05-29 — the stripe was unintuitive for catalog
   visitors and the pane-head "Sample" badge already communicates mock
   state clearly. securityShell() is now an alias of shell();
   mockStripeModifier() in sd-embed.js was removed entirely. */

/* ----- demo-when-empty + unmonitored banners (three-state empty
   contract; see doc 37 §states) ----------------------------------
   demo-banner: live mode returned zero of the resource the pane
   shows, so the pane swapped to mock data. Amber wash, amber-700
   rail — "you're looking at demo data" — informational.
   unmonitored-warning: live mode returned zero AND we have evidence
   the AWS feature isn't wired (no detector, no analyzer, no alarms).
   Critical-red rail — "this isn't reassuring; nothing is watching."
   ---------------------------------------------------------------- */
:where(.lp-pane-demo-banner) {
    margin: 0 0 12px;
    padding: 10px 12px;
    background: var(--lp-pane-amber-50, #fffbeb);
    border: 1px solid var(--lp-pane-amber-300, #fde9a6);
    border-left: 3px solid var(--lp-pane-amber-700, #b45309);
    border-radius: 4px;
    font-size: 12px;
    color: var(--lp-pane-ink, #2b2a1e);
    line-height: 1.55;
}
:where(.lp-pane-demo-banner strong) {
    color: var(--lp-pane-amber-900, #3d2100);
}
:where(.lp-pane-demo-banner-hint) {
    margin-top: 4px;
    color: var(--lp-pane-meta-ink, #6b645a);
    font-size: 11px;
}

:where(.lp-pane-unmonitored-warning) {
    margin: 0 0 12px;
    padding: 10px 12px;
    background: var(--lp-pane-status-critical-bg, #fee2e2);
    border: 1px solid var(--lp-pane-status-critical-border, #fca5a5);
    border-left: 3px solid var(--lp-pane-status-critical-ink, #991b1b);
    border-radius: 4px;
    font-size: 12px;
    color: var(--lp-pane-ink, #2b2a1e);
    line-height: 1.55;
}
:where(.lp-pane-unmonitored-warning strong) {
    color: var(--lp-pane-status-critical-ink, #991b1b);
}
:where(.lp-pane-unmonitored-warning-action) {
    color: var(--lp-pane-status-critical-ink, #991b1b);
    font-weight: 600;
    text-decoration: underline;
    white-space: nowrap;
}

/* ----- header boxes (summary card row) ----- */
:where(.lp-pane-header-boxes) {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 8px;
    margin-bottom: 14px;   /* section rhythm: even the gap above/below the stat boxes */
}
/* Scorecard: a radial scoreRing beside a row of tally header-boxes (security-
   posture, app-health). Wraps to stack the ring above the tallies on narrow
   panes; the tallies flex to fill the remaining width. */
:where(.lp-pane-scorecard) {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 12px 18px; margin-bottom: 18px;
}
:where(.lp-pane-scorecard > .lp-pane-header-boxes) { flex: 1 1 240px; margin: 0; }
:where(.lp-pane-header-box) {
    display: flex;
    flex-direction: column;
    /* Pale amber wash so the box has presence on a white pane bg
       without competing with the value/label typography. */
    background: var(--lp-pane-amber-50, #fffbeb);
    border: 1px solid var(--lp-pane-header-box-border, #8f8b82);
    border-radius: 8px;
}
:where(.lp-pane-header-box-body) {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    padding: 10px 14px;
}
/* Delta-variant (G3): a coloured RAG footer band across the rounded bottom
   (bleeds 1px over the box border so the bottom edge takes the RAG colour). */
:where(.lp-pane-header-box-band) {
    padding: 5px 14px;
    font-size: 11.5px;
    font-weight: 600;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    margin: 0 -1px -1px;
    border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;
}
:where(.lp-pane-header-box-band--critical) { background: var(--lp-rag-critical-bg); color: var(--lp-rag-critical-ink); }
:where(.lp-pane-header-box-band--warn)     { background: var(--lp-rag-warn-bg);     color: var(--lp-rag-warn-ink); }
:where(.lp-pane-header-box-band--success)  { background: var(--lp-rag-success-bg);  color: var(--lp-rag-success-ink); }
:where(.lp-pane-header-box-band--info)     { background: var(--lp-rag-info-bg);     color: var(--lp-rag-info-ink); }
/* neutral band — no status, so no coloured strip: same wash as the box, the
   box's own border wraps it (unlike coloured bands, which bleed 1px over the
   border to take its colour to the edge), and NO internal top rule. Reads as the
   lower part of the box, present only for label placement / alignment with the
   RAG-banded siblings. Coloured bands keep today's bleed-to-edge behaviour. */
:where(.lp-pane-header-box-band--neutral) {
    background: var(--lp-pane-amber-50, #fffbeb);
    color: var(--lp-pane-meta-ink, #6b645a);
    margin: 0;
}
:where(.lp-pane-header-box-value) {
    font-size: 22px;
    font-weight: 600;
    line-height: 1.1;
    color: var(--lp-pane-ink, #1a1814);
    font-variant-numeric: tabular-nums;
}
:where(.lp-pane-header-box-label) {
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--lp-pane-meta-ink, #6b645a);
}
/* (Retired: tone-coloured header-box VALUES. Status is shown by a RAG band
   now — see headerBoxes() in lp-pane.js. Coloured numbers didn't stand out on
   the amber pane and failed as a sole signal.) */

/* ----- tierLocked (G6): upgrade gate for above-tier features ----- */
:where(.lp-pane-tierlock) {
    display: flex; gap: 12px; align-items: flex-start;
    margin: 10px 0 4px; padding: 16px;
    border: 1px dashed var(--lp-pane-amber-700, #a5620e);
    border-radius: var(--lp-pane-radius-sm, 6px);
    background: var(--lp-pane-amber-50, #fffbeb);
}
:where(.lp-pane-tierlock-icon) { font-size: 18px; line-height: 1.2; }
:where(.lp-pane-tierlock-title) {
    font-weight: 650; font-size: 13px; color: var(--lp-pane-ink, #1a1814); margin-bottom: 3px;
}
:where(.lp-pane-tierlock-body) {
    font-size: 12.5px; line-height: 1.5; color: var(--lp-pane-ink, #1a1814);
}
:where(.lp-pane-tierlock-cta) {
    display: inline-block; margin-top: 8px; font-size: 12.5px; font-weight: 600;
    color: var(--lp-pane-amber-900, #3d2100);
}

/* ----- pill selector (segmented filter) ----- */
:where(.lp-pane-pills) {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 12px 0 18px;
}
/* Grouped layout (opt-in): inline groups share a row (flow + wrap as inline
   boxes); a divided group leads with a vertical bar so two sets on one row read
   as distinct. */
:where(.lp-pane-pills--inline) {
    display: inline-flex;
    margin: 12px 8px 18px 0;
    vertical-align: top;
}
:where(.lp-pane-pills-divider) {
    align-self: stretch;
    width: 1px;
    background: var(--lp-pane-border-strong, #ddd6c9);
    margin: 3px 8px 3px 2px;
}

/* chipGrid — responsive grid of compact single-value cells (auto-fill reflow). */
:where(.lp-pane-chip-grid) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--lp-chip-min, 130px), 1fr));
    gap: 6px;
    margin: 4px 0 6px;
}
:where(.lp-pane-chip) {
    min-width: 0;
    padding: 6px 10px;
    /* Light-blue wash (the headline-bar palette) rather than amber — the amber
       is over-used, and blue sets these value chips apart. */
    background: var(--lp-pane-blue-100, #dbeafe);
    border: 1px solid var(--lp-pane-blue-300, #93c5fd);
    border-radius: 5px;
    overflow: hidden;
}
:where(.lp-pane-chip-label) {
    display: block;
    font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase;
    color: var(--lp-pane-ink-dim, #8a8378);
    margin-bottom: 1px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:where(.lp-pane-chip-value) {
    display: block;
    font-size: 12.5px; color: var(--lp-pane-ink, #1a1814);
    font-variant-numeric: tabular-nums;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* truncate:false — show unique values in full (wrap instead of ellipsis). */
:where(.lp-pane-chip-grid--wrap .lp-pane-chip-value) {
    white-space: normal; overflow: visible; overflow-wrap: anywhere; word-break: break-word;
}
:where(.lp-pane-chip-grid-more) {
    margin-top: 8px;
    font-size: var(--lp-pane-fs-meta, 11px);
    color: var(--lp-pane-ink-dim, #8a8378);
    font-style: italic;
}
:where(.lp-pane-pill) {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    background: var(--lp-pane-paper, #fff);
    /* Unselected border darkened from the pale border-strong (#ddd6c9) so the
       resting strip reads as a real control, not a ghost. Neutral (not toned) —
       the pill's colour only appears on hover / selection. */
    border: 1px solid var(--lp-pane-pill-border, #c2b39a);
    border-radius: 999px;
    color: var(--lp-pane-ink-2, #3a352e);
    font: inherit;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease, color .12s ease;
}

/* Hover (only while NOT selected): a LIGHT tint of the pill's own colour + a
   darker, tone-matched border — a preview of what selecting commits to (light
   red -> full red on select), matching the global-search selector feel. Standard
   across EVERY pill selector. Default (no tone) previews the sunlit amber. Listed
   general-first so the per-tone rules win (all :where(), so source order decides).
   Scoped to :not([aria-pressed]) so hovering the selected pill keeps it solid. */
:where(.lp-pane-pill:not([aria-pressed="true"]):hover) {
    background: var(--lp-pane-amber-100, #fef3c7);
    border-color: var(--lp-pane-amber-700, #a5620e);
    color: var(--lp-pane-amber-900, #3d2100);
}
:where(.lp-pane-pill[data-tone="critical"]:not([aria-pressed="true"]):hover) {
    background: var(--lp-rag-critical-soft-bg); border-color: var(--lp-rag-critical-soft-ink); color: var(--lp-rag-critical-soft-ink);
}
:where(.lp-pane-pill[data-tone="warn"]:not([aria-pressed="true"]):hover) {
    background: var(--lp-rag-warn-soft-bg); border-color: var(--lp-rag-warn-soft-ink); color: var(--lp-rag-warn-soft-ink);
}
:where(.lp-pane-pill[data-tone="info"]:not([aria-pressed="true"]):hover) {
    background: var(--lp-rag-info-soft-bg); border-color: var(--lp-rag-info-soft-ink); color: var(--lp-rag-info-soft-ink);
}
:where(.lp-pane-pill[data-tone="success"]:not([aria-pressed="true"]):hover) {
    background: var(--lp-rag-success-soft-bg); border-color: var(--lp-rag-success-soft-ink); color: var(--lp-rag-success-soft-ink);
}
:where(.lp-pane-pill[data-tone="neutral"]:not([aria-pressed="true"]):hover) {
    background: var(--lp-pane-sunk-bg, #f5f2ec); border-color: var(--lp-pane-ink-3, #6b645a);
}

/* Selected: the FULL / solid colour. Default = sunlit amber. Per-tone pills
   commit to their solid RAG colour — was a soft tint for warn/info/success,
   which now reads identically to the hover tint; solid gives a clear
   hover -> select escalation and matches "full red on selection". */
:where(.lp-pane-pill[aria-pressed="true"]) {
    background: var(--lp-pane-pill-active-bg);
    color: var(--lp-pane-pill-active-ink);
    border-color: var(--lp-pane-pill-active-border);
    font-weight: 600;
}
:where(.lp-pane-pill[aria-pressed="true"][data-tone="critical"]) {
    background: var(--lp-rag-critical-bg); color: var(--lp-rag-critical-ink); border-color: var(--lp-rag-critical-bg);
}
:where(.lp-pane-pill[aria-pressed="true"][data-tone="warn"]) {
    background: var(--lp-rag-warn-bg); color: var(--lp-rag-warn-ink); border-color: var(--lp-rag-warn-bg);
}
:where(.lp-pane-pill[aria-pressed="true"][data-tone="info"]) {
    background: var(--lp-rag-info-bg); color: var(--lp-rag-info-ink); border-color: var(--lp-rag-info-bg);
}
:where(.lp-pane-pill[aria-pressed="true"][data-tone="success"]) {
    background: var(--lp-rag-success-bg); color: var(--lp-rag-success-ink); border-color: var(--lp-rag-success-bg);
}
:where(.lp-pane-pill[aria-pressed="true"][data-tone="neutral"]) {
    background: var(--lp-pane-sunk-bg, #f5f2ec);
    color: var(--lp-pane-ink, #1a1814);
    border-color: var(--lp-pane-border-strong, #ddd6c9);
}

:where(.lp-pane-pill-count) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 18px;
    padding: 0 6px;
    background: rgba(0, 0, 0, 0.06);
    color: inherit;
    border-radius: 999px;
    font-family: var(--lp-pane-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 10px;
    font-weight: 600;
}
:where(.lp-pane-pill[aria-pressed="true"] .lp-pane-pill-count) {
    background: rgba(0, 0, 0, 0.1);
}

/* ----- searchBox (free-text filter) — shared primitive (lpPane.searchBox) -----
   One styling for every pane's search input (was three bespoke copies:
   aws red/amber unfocused + black focused; azure/gcp a pale-bordered flex row).
   Consistent blue-900 border in BOTH states, a soft blue focus glow, and a blue
   magnifying-glass icon nudged 6px below centre. Plain class selectors (not
   :where) so a host page's bare `input {}` rule can't repaint the box. */
.lp-pane-search-row { position: relative; }
.lp-pane-search {
    width: 100%; box-sizing: border-box;
    border: 2px solid var(--lp-pane-blue-900, #1e3a8a);
    border-radius: var(--lp-pane-radius-sm);
    background: var(--lp-pane-bg, #fff); color: var(--lp-pane-ink, #1a1814);
    padding: 12px 16px 12px 40px;
    font-family: inherit; font-size: 14px;
    transition: box-shadow .15s ease, border-color .15s ease;
}
.lp-pane-search:focus {
    outline: none;
    border-color: var(--lp-pane-blue-900, #1e3a8a);
    /* slight blue glow — doubles as a visible AA focus indicator */
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.28);
}
.lp-pane-search::placeholder { color: var(--lp-pane-ink-dim, #8a8378); }
.lp-pane-search-icon {
    /* +2px (not dead-centre) optically aligns the magnifier with the input text:
       the glass's mass sits slightly above the icon's geometric centre. Measured
       — text/input centre coincide; icon centre was 6px low at +6px. */
    position: absolute; left: 12px; top: calc(50% + 2px);
    transform: translateY(-50%);
    color: var(--lp-pane-blue-900, #1e3a8a);
    pointer-events: none;
}
.lp-pane-search-meta {
    /* 12px top = the old 6px + the requested 6px nudge below the box. */
    margin: 12px 0 12px; font-size: 11px; color: var(--lp-pane-ink-dim, #8a8378);
}
.lp-pane-search-meta strong { color: var(--lp-pane-blue-900, #1e3a8a); font-weight: 600; }
@container (max-width: 520px) {
    .lp-pane-search { font-size: 13px; padding: 10px 12px 10px 34px; }
}

/* ===== filterBar =================================================
   Search-first faceted filter: a search box + a "Filters" button that
   opens a framed dropdown of grouped, multi-select checkboxes. The
   dropdown clamps to the pane's bottom edge and scrolls internally
   (content lives within the frame); group legends + footer are sticky.
   Markup from lpPane.filterBar(); behaviour from lpPane.wireFilterBar().
   ------------------------------------------------------------------ */
.lp-filterbar { display: flex; align-items: center; gap: 10px; margin: 10px 0 14px; position: relative; }
.lp-filter-search { flex: 1 1 auto; min-width: 0; }
.lp-filter-search .lp-pane-search-row { margin: 0; }

.lp-filter-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    align-self: stretch;                 /* match the search box height */
    gap: 7px;
    padding: 0 14px;
    font: inherit; font-size: 14px; font-weight: 600;
    color: var(--lp-pane-ink, #1a1814);
    background: var(--lp-pane-bg, #fff);
    border: 2px solid var(--lp-pane-blue-900, #1e3a8a);   /* same as the search box */
    border-radius: var(--lp-pane-radius-sm, 7px);
    cursor: pointer;
    white-space: nowrap;
}
.lp-filter-btn:hover { background: var(--lp-pane-blue-100, #e6edf8); }
.lp-filter-btn:focus-visible { outline: 2px solid var(--lp-pane-blue-900, #1e3a8a); outline-offset: 1px; }
.lp-filter-btn--open { background: var(--lp-pane-blue-100, #e6edf8); }
.lp-filter-btn .lp-filter-caret { transition: transform .12s ease; }
.lp-filter-btn--open .lp-filter-caret { transform: rotate(180deg); }
.lp-filter-badge {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    font-size: 11px; font-weight: 700; color: #fff;
    background: var(--lp-pane-blue-900, #1e3a8a); border-radius: 9px;
}
.lp-filter-badge[hidden] { display: none; }

.lp-filtermenu {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
    width: 300px; max-width: calc(100vw - 40px);
    max-height: 60vh;                    /* JS overrides with the frame-clamped value */
    overflow: auto;
    padding: 0 14px;                     /* sticky legend/footer supply top/bottom space */
    background: var(--lp-pane-bg, #fff);
    border: 1px solid var(--lp-pane-header-box-border, #8f8b82);
    border-radius: 9px;
    box-shadow: 0 8px 28px rgba(26, 24, 20, .18);
}
.lp-filtermenu[hidden] { display: none; }
.lp-filter-group { border: 0; margin: 0 0 10px; padding: 0; }
.lp-filter-group legend {
    position: sticky; top: 0; z-index: 2;
    display: block; box-sizing: border-box; width: 100%;
    padding: 10px 0 6px; margin: 0;
    background: var(--lp-pane-bg, #fff);
    font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--lp-pane-meta-ink, #6b645a);
}
.lp-filter-check {
    display: flex; align-items: center; gap: 9px;
    padding: 5px 6px; border-radius: 6px; cursor: pointer;
    font-size: 13px; color: var(--lp-pane-ink, #1a1814);
}
.lp-filter-check:hover { background: var(--lp-pane-blue-100, #e6edf8); }
.lp-filter-check input {           /* native checkbox drives state; styled box over it */
    position: absolute; opacity: 0; width: 16px; height: 16px; margin: 0; cursor: pointer;
}
.lp-filter-checkbox {
    flex: 0 0 auto; width: 16px; height: 16px;
    border: 1.5px solid var(--lp-pane-header-box-border, #8f8b82);
    border-radius: 4px; background: #fff; position: relative;
}
.lp-filter-check input:checked + .lp-filter-checkbox {
    background: var(--lp-pane-blue-900, #1e3a8a); border-color: var(--lp-pane-blue-900, #1e3a8a);
}
.lp-filter-check input:checked + .lp-filter-checkbox::after {
    content: ""; position: absolute; left: 4.5px; top: 1px;
    width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.lp-filter-check input:focus-visible + .lp-filter-checkbox {
    outline: 2px solid var(--lp-pane-blue-900, #1e3a8a); outline-offset: 1px;
}
.lp-filter-check-label { flex: 1 1 auto; }
.lp-filter-check-count { flex: 0 0 auto; font-size: 11px; font-weight: 600; color: var(--lp-pane-meta-ink, #6b645a); }
.lp-filter-foot {
    position: sticky; bottom: 0; z-index: 2;
    display: flex; justify-content: flex-end;
    border-top: 1px solid var(--lp-pane-border, #d9d2c4);
    background: var(--lp-pane-bg, #fff);
    padding: 8px 0 10px; margin-top: 6px;
}
.lp-filter-clear {
    font: inherit; font-size: 12px; font-weight: 600;
    color: var(--lp-pane-blue-900, #1e3a8a);
    background: none; border: 0; padding: 3px 4px; cursor: pointer;
}
.lp-filter-clear:disabled { color: var(--lp-pane-meta-ink, #6b645a); cursor: default; opacity: .6; }
.lp-filter-clear:not(:disabled):hover { text-decoration: underline; }

@media (prefers-reduced-motion: reduce) {
    .lp-filter-btn .lp-filter-caret { transition: none; }
}

/* ----- alternate "cool" palette ----------------------------------
   Some panes (cost-by-tag, data-transfer, chart-heavy renders) need
   variety so series don't all melt into the sunlit amber. The blue
   shades below sit in the same lightness band as the amber tokens
   so they don't fight the brand visually — they're an *alternate*
   accent, not a replacement.

   Token convention:
     --lp-pane-blue-50  / -100  pale tints (chart fills, badge bg)
     --lp-pane-blue-500          mid (primary stroke / pill active bg)
     --lp-pane-blue-700          deep (text on light bg)
     --lp-pane-blue-900          ink-dark (display values)

   Authoring rule (see doc 35 §1.4): a single pane MAY use blue
   tokens for its chart series + supporting controls; the head bar +
   refresh button + status tags stay on the canonical palette. */
:root {
    --lp-pane-blue-50:  #eff6ff;
    --lp-pane-blue-100: #dbeafe;
    --lp-pane-blue-300: #93c5fd;
    /* Shared border for headline banners — the blue section bands
       (.lp-xtable-section) AND the full-bleed subhead (.lp-pane-subhead), so both
       read as one "headline banner" style from a single source. */
    --lp-pane-headline-border: #93c5fd;   /* blue-300 */
    --lp-pane-blue-500: #3b82f6;
    --lp-pane-blue-700: #1d4ed8;
    --lp-pane-blue-900: #1e3a8a;
}

/* ----- tag (inline status pill) ----- */
:where(.lp-pane-tag) {
    display: inline-flex;
    align-items: center;
    /* Raise the pill ~2px so its bottom sits on the following text's baseline
       (reads cleaner, and keeps it anchored to line 1 when the text wraps),
       and give it 4px of breathing room before the following text. */
    vertical-align: 2px;
    margin-right: 4px;
    padding: 1px 8px;
    border-radius: 999px;
    font-family: var(--lp-pane-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    border: 1px solid transparent;
    background: var(--lp-pane-sunk-bg, #f5f2ec);
    color: var(--lp-pane-ink-2, #3a352e);
    border-color: var(--lp-pane-border, #ebe6dd);
}
/* Status pills — driven by the --lp-pane-tag-* tokens (customer-overridable;
   inks are WCAG-AA). Success/critical are the more saturated + legible green /
   soft red from the discovery renderer (user pref 2026-07-14); warn/info map to
   the RAG soft set. Universal: applies to every lpPane.tag across the panes. */
:where(.lp-pane-tag--success)  { background: var(--lp-pane-tag-success-bg);  color: var(--lp-pane-tag-success-ink);  border-color: var(--lp-pane-tag-success-border); }
:where(.lp-pane-tag--warn)     { background: var(--lp-pane-tag-warn-bg);     color: var(--lp-pane-tag-warn-ink);     border-color: var(--lp-pane-tag-warn-border); }
:where(.lp-pane-tag--critical) { background: var(--lp-pane-tag-critical-bg); color: var(--lp-pane-tag-critical-ink); border-color: var(--lp-pane-tag-critical-border); }
:where(.lp-pane-tag--info)     { background: var(--lp-pane-tag-info-bg);     color: var(--lp-pane-tag-info-ink);     border-color: var(--lp-pane-tag-info-border); }

/* ----- simple / expandable list ----- */
:where(.lp-pane-list) {
    display: flex;
    flex-direction: column;
    gap: 0;
    /* Top breathing room separates the list from whatever comes
       above (header boxes, pill selector, subhead). Bottom margin
       gives the list space before the next section. */
    margin: 12px 0 14px;
    border-top: 1px solid var(--lp-pane-border, #ebe6dd);
}
/* When a list immediately follows a subhead inside a compound list,
   the subhead already provides the horizontal rule that delimits the
   section — the list's own top border would double up against the
   subhead's border-bottom. Drop it. */
:where(.lp-pane-subhead + .lp-pane-list) {
    border-top: none;
}
/* Don't draw a trailing line under the last row of a list — the
   row border-bottom is a between-rows separator, not a list edge.
   NOT wrapped in :where(): the base `.lp-pane-list-row` border-bottom
   rule below is also zero-specificity and comes later in source order,
   so a :where() version here would be defeated by it and every last row
   (incl. a single-row headline) would keep its rule — reading as a
   doubled hairline against the next subhead/list. Real specificity wins. */
.lp-pane-list-row:last-child {
    border-bottom: none;
}
:where(.lp-pane-list-row) {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    padding: 10px 0;
    border-bottom: 1px solid var(--lp-pane-border, #ebe6dd);
}
:where(.lp-pane-list-row--expandable) {
    flex-direction: column;
    cursor: pointer;
}
:where(.lp-pane-list-row--expandable .lp-pane-list-row-head) {
    display: flex;
    align-items: center;
    gap: 12px;
    /* no explicit width: the column-flex row stretches the head to full width,
       so the hover negative-margin EXPANDS (a fixed width would translate). */
}
:where(.lp-pane-list-row-left) {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--lp-pane-ink, #1a1814);
}
:where(.lp-pane-list-row-right) {
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--lp-pane-meta-ink, #6b645a);
    font-family: var(--lp-pane-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}
:where(.lp-pane-list-chevron) {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    color: var(--lp-pane-meta-ink, #6b645a);
    transition: transform .15s ease;
}
:where(.lp-pane-list-row--expandable[aria-expanded="true"] .lp-pane-list-chevron) {
    transform: rotate(90deg);
}
:where(.lp-pane-list-row-detail) {
    width: 100%;
    margin-top: 8px;
    padding: 8px 0 4px;
    border-top: 1px dashed var(--lp-pane-border, #ebe6dd);
    font-size: 12px;
    line-height: 1.55;
    color: var(--lp-pane-ink-2, #3a352e);
}
:where(.lp-pane-list-row[data-row-id]) {
    /* clickable rows get a subtle hover. */
    cursor: pointer;
}
:where(.lp-pane-list-row[data-row-id]:hover) {
    background: var(--lp-pane-sunk-bg, #f5f2ec);
}
/* Expandable rows use the same hover language as expandableTable: amber wash +
   0.75px ring on the row-head, bleeding 6px into the padding (cancel the plain
   whole-row sunk hover so only the head lights up). */
:where(.lp-pane-list-row--expandable[data-row-id]:hover) { background: transparent; }
:where(.lp-pane-list-row--expandable .lp-pane-list-row-head:hover) {
    background: var(--lp-pane-row-hover-bg);
    box-shadow: 0 0 0 0.75px var(--lp-pane-border);
    border-radius: var(--lp-pane-radius-sm);
    margin: 0 -6px;
    padding: 0 6px;
}

/* ----- compound list (sections + subheadings) ----- */
:where(.lp-pane-section) {
    margin-bottom: 18px;
}
:where(.lp-pane-section:last-child) {
    margin-bottom: 0;
}
:where(.lp-pane-subhead) {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    /* Negative horizontal margin + matching internal padding pulls
       the subhead's edges out to the pane's edge while keeping its
       text aligned with the body content (which sits inside the
       body's --lp-pane-pad-x padding). The border-bottom therefore
       reads as a full-pane horizontal rule, the way the legacy
       per-pane subheads used to render. */
    padding: 8px var(--lp-pane-pad-x) 6px;
    margin: 6px calc(-1 * var(--lp-pane-pad-x)) 10px;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.005em;
    text-transform: none;
    /* A light-blue section band (the alternate palette) so section headlines
       read as distinct headers rather than melting into a white list. Full-bleed
       tinted bar with a darker blue-300 hairline top AND bottom — the shared
       headline-banner border (matches the grouped-list section bands); on-theme
       with the amber body via the shared neutral ink. */
    color: var(--lp-pane-blue-900, #1e3a5f);
    background: var(--lp-pane-blue-50, #eff6ff);
    border-top: 1px solid var(--lp-pane-headline-border, #93c5fd);
    border-bottom: 1px solid var(--lp-pane-headline-border, #93c5fd);
}
/* card — a white box for a titled sub-list inside a drawer (so lists with
   headlines read as discrete blocks, not bare content on the amber band). */
:where(.lp-pane-card) {
    background: var(--lp-pane-bg, #ffffff);
    border: 1px solid var(--lp-pane-amber-300, #f2d199);
    border-radius: 6px;
    padding: 8px 10px;
    margin: 8px 0;
}
:where(.lp-pane-card + .lp-pane-card) { margin-top: 10px; }
:where(.lp-pane-card-head) {
    display: flex; justify-content: space-between; align-items: baseline;
    font-size: 12.5px; font-weight: 600; color: var(--lp-pane-ink, #1a1814);
    margin-bottom: 6px;
}
:where(.lp-pane-card-count) {
    font-weight: 400; color: var(--lp-pane-ink-dim, #8a8378);
    font-variant-numeric: tabular-nums;
}
:where(.lp-pane-subhead-count) {
    /* Match the subhead label's font + size (NOT the mono meta/time style — the
       count read as a timestamp), just a lighter weight so it's clearly the
       section count rather than a value. */
    font-size: 14px;
    font-weight: 400;
    letter-spacing: -0.005em;
    color: var(--lp-pane-ink-dim, #8a8378);
    font-variant-numeric: tabular-nums;
}

/* ----- map graphic (region-footprint container) ----- */
:where(.lp-pane-map) {
    width: 100%;
    margin: 0 0 14px;
    min-height: 280px;
}

/* ============================================================
   lp-chart visualisation primitives (window.lpChart, 2026-05-27)
   ============================================================
   Styles for the SVG-based chart primitives in embed/lp-chart.js.
   Charts are pure SVG inside a thin .lp-chart wrapper; fills /
   strokes come from the palette tokens above and from inline
   style="..." attributes so per-series colours work without per-
   class CSS rules.

   Design spec: docs/service-discovery-v2/39-chart-primitive-library.md */
:where(.lp-chart) {
    width: 100%;
    margin: 10px 0 18px;   /* section rhythm: breathing room around the chart */
    font-family: var(--lp-pane-font-sans, ui-sans-serif, system-ui, sans-serif);
}
/* Sankey: 1px top/bottom rules demarcate the diagram's vertical bounds. */
:where(.lp-chart--sankey) {
    border-top: 1px solid var(--lp-pane-border);
    border-bottom: 1px solid var(--lp-pane-border);
    padding: 10px 0;
}
/* Bar-chart readout (lpChart.bars {readout:true}) — a one-line label+value
   strip that tracks the hovered/focused chart element (bar or pie slice).
   Opt-in (readout:true) and always shown when present — keeps values/legends
   visible on a label-less pie or when a paired list scrolls out of view.
   Wired by lpRender.wireChartReadouts (via markRendered / lpPane.wire). */
:where(.lp-chart-readout) {
    display: flex;
    justify-content: space-between; align-items: center; gap: 14px;
    margin-bottom: 8px; padding: 7px 12px;
    background: var(--lp-pane-bg); border: 1px solid var(--lp-pane-divider);
    border-radius: var(--lp-pane-radius-sm); font-size: 12.5px;
}
:where(.lp-chart-readout-label) {
    flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; color: var(--lp-pane-ink); font-weight: 500;
}
:where(.lp-chart-readout-value) {
    flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 600;
    color: var(--lp-pane-ink); white-space: nowrap;
}
:where(.lp-chart-svg) {
    width: 100%;
    height: auto;
    display: block;
}
:where(.lp-chart-empty) {
    padding: 24px 18px;
    text-align: center;
    color: var(--lp-pane-meta-ink, #6b645a);
    font-size: 12px;
    font-style: italic;
    background: var(--lp-pane-sunk-bg, #f5f2ec);
    border: 1px dashed var(--lp-pane-border-strong, #ddd6c9);
    border-radius: 8px;
}

/* ----- bars ----- */
:where(.lp-chart-bar-track) {
    fill: var(--lp-pane-sunk-bg, #f5f2ec);
    rx: 3;
}
:where(.lp-chart-bar-fill) {
    rx: 3;
    transition: opacity .15s ease;
}
:where(.lp-chart-bar-row:hover .lp-chart-bar-fill),
:where(.lp-chart-bar-col:hover .lp-chart-bar-fill) {
    opacity: 0.78;
}
:where(.lp-chart-bar-label) {
    font-size: 11px;
    fill: var(--lp-pane-ink-2, #3a352e);
}
:where(.lp-chart-bar-value) {
    font-family: var(--lp-pane-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    fill: var(--lp-pane-ink-2, #3a352e);
}

/* ----- line ----- */
:where(.lp-chart-line) {
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
:where(.lp-chart-line-band) {
    stroke: none;
}
/* Per-point hover targets — invisible until hovered/focused (then a ringed
   marker appears), driving the readout. r=4 gives an easy hit area. */
:where(.lp-chart-line-point) {
    fill-opacity: 0;
    stroke: var(--lp-pane-bg, #fff);
    stroke-width: 1.5;
    cursor: pointer;
    transition: fill-opacity .1s ease;
}
:where(.lp-chart-line-point:hover),
:where(.lp-chart-line-point:focus),
:where(.lp-chart-line-point--active) { fill-opacity: 1; outline: none; }
:where(.lp-chart-grid) {
    stroke: var(--lp-pane-border, #ebe6dd);
    stroke-width: 1;
    stroke-dasharray: 2 3;
}
:where(.lp-chart-grid-label) {
    font-family: var(--lp-pane-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 9px;
    fill: var(--lp-pane-meta-ink, #6b645a);
    font-variant-numeric: tabular-nums;
}

/* ----- legend ----- */
:where(.lp-chart-legend) {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin: 6px 0 0;
    font-size: 12px;
    color: var(--lp-pane-ink-2, #3a352e);
}
:where(.lp-chart-legend[data-align="center"]) { justify-content: center; }
:where(.lp-chart-legend[data-align="right"])  { justify-content: flex-end; }
:where(.lp-chart-legend-item) {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
:where(.lp-chart-legend-swatch) {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    flex-shrink: 0;
}

/* ----- pie ----- */
:where(.lp-chart--pie) {
    display: flex;
    flex-direction: column;   /* pie-body row, then the readout as a block below */
    gap: 10px;
}
:where(.lp-chart-pie-body) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 18px;
}
:where(.lp-chart--pie[data-legend-position="bottom"]) .lp-chart-pie-body {
    flex-direction: column;
    align-items: flex-start;
}
:where(.lp-chart-pie-svg) {
    flex: 0 0 auto;
}
:where(.lp-chart-pie-slice) {
    transition: opacity .15s ease;
    stroke: var(--lp-pane-bg, #faf6ec);
    stroke-width: 1.5;
}
/* Dim the other slices ONLY when a slice itself is hovered (via :has on the
   svg) — the old `.lp-chart--pie:hover` fired on any hover inside the chart
   (legend, hole, padding), lightening the whole pie. */
:where(.lp-chart-pie-svg:has(.lp-chart-pie-slice:hover) .lp-chart-pie-slice) {
    opacity: 0.55;
}
:where(.lp-chart-pie-slice:hover) {
    opacity: 1;
}
:where(.lp-chart-pie-hole) {
    fill: var(--lp-pane-bg, #faf6ec);
    stroke: none;
}
:where(.lp-chart-pie-centre-value) {
    font-family: var(--lp-pane-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 18px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    fill: var(--lp-pane-ink, #2b2a1e);
}
:where(.lp-chart-pie-centre-label) {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    fill: var(--lp-pane-meta-ink, #6b645a);
}
:where(.lp-chart-pie-legend) {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    flex: 1 1 200px;
    min-width: 160px;
}
:where(.lp-chart-pie-legend .lp-chart-legend-item) {
    width: 100%;
    align-items: baseline;
    gap: 8px;
}
:where(.lp-chart-pie-legend-label) {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
:where(.lp-chart-pie-legend-value) {
    font-family: var(--lp-pane-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-variant-numeric: tabular-nums;
    color: var(--lp-pane-meta-ink, #6b645a);
    font-size: 11px;
    flex-shrink: 0;
}

/* ----- chartList (composite: chart stacked above a linked list) ----- */
:where(.lp-chart-list) {
    padding: var(--lp-pane-pad-y) var(--lp-pane-pad-x);
}
/* Chart renders full-width on top; the list is its own section below, so each
   element stands on its own. Width-scaling charts (bars/line, SVG width:100%)
   fill the column; only the fixed-size pie is centred (a flex parent would
   otherwise shrink the bars to their intrinsic viewBox width → dead space). */
:where(.lp-chart-list-chart) .lp-chart-pie-body { justify-content: center; }
:where(.lp-chart-list-list) { margin-top: 14px; }
/* Colour swatch that ties a list row to its chart element (the list = key). */
:where(.lp-chart-list-swatch) {
    display: inline-block;
    width: 10px; height: 10px;
    border-radius: 3px;
    margin-right: 8px;
    vertical-align: -1px;
    flex-shrink: 0;
}
/* keyed elements are the interactive link targets */
:where(.lp-chart-list [data-lp-key]) { cursor: pointer; }
/* Linked hover/focus: dim the non-active chart elements, brighten the active
   one, and wash the matching list row (the existing row-hover look). Additive
   to labels/values, so meaning is never colour-alone. */
:where(.lp-chart-list.lp-linked-active .lp-chart-bar-fill),
:where(.lp-chart-list.lp-linked-active .lp-chart-pie-slice) { opacity: 0.4; }
:where(.lp-chart-list .lp-chart-bar-row.lp-linked-on .lp-chart-bar-fill),
:where(.lp-chart-list .lp-chart-pie-slice.lp-linked-on) { opacity: 1; }
:where(.lp-chart-list .lp-pane-list-row.lp-linked-on),
:where(.lp-chart-list .lp-xtable-row.lp-linked-on) {
    background: var(--lp-pane-amber-50, #fdf6e3);
}

/* ----- sankey ----- */
:where(.lp-chart-sankey-ribbon) {
    opacity: var(--lp-ribbon-opacity, 0.55);
    transition: opacity .15s ease;
}
:where(.lp-chart--sankey:hover .lp-chart-sankey-ribbon) {
    opacity: 0.25;
}
:where(.lp-chart--sankey .lp-chart-sankey-ribbon:hover) {
    opacity: 0.85;
}
:where(.lp-chart-sankey-node) {
    rx: 1;
}
:where(.lp-chart-sankey-label) {
    font-size: 11px;
    fill: var(--lp-pane-ink-2, #3a352e);
}

/* ----- empty table row (lpPane.emptyTableRow) ----- */
:where(.lp-pane-empty-row td) {
    padding: 14px 12px;
    text-align: center;
    font-style: italic;
    color: var(--lp-pane-meta-ink, #6b645a);
    font-size: 12px;
    background: var(--lp-pane-sunk-bg, #f5f2ec);
}

/* ----- state blocks (empty / loading / error) -----
   IMPORTANT: scope to the explicit modifier variants, NOT the bare
   .lp-pane-state class. The bare class is already in use by the
   service-discovery status pill (line ~623) for column values like
   "running" / "stopped" / "available". A bare-class rule here wins
   by document order and turned every status pill into a big dashed
   block. Always list every modifier explicitly when you add a new
   block-style state (don't be tempted to refactor back to a bare
   .lp-pane-state base — you'll re-break the SD pills). */
:where(.lp-pane-state--empty),
:where(.lp-pane-state--loading),
:where(.lp-pane-state--error),
:where(.lp-pane-state--clear),
:where(.lp-pane-state--discovering),
:where(.lp-pane-state--inactive) {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 24px 18px;
    /* Even 10px on ALL sides — a flush box read as edge-to-edge in an otherwise
       empty pane (was margin-top only, so it butted the left/right pane edges). */
    margin: 10px;
    text-align: center;
    /* Preserve the author's case — the base .lp-pane-state (an SD status PILL)
       force-lowercases, which wrongly lowercased these full-sentence messages. */
    text-transform: none;
    letter-spacing: normal;
    /* The base .lp-pane-state pill sets white-space:nowrap (single-line chips);
       these block states carry BOTH classes, so without this reset a multi-
       sentence message/hint renders on one line and overflows the pane into a
       horizontal scroll (seen on free-tier's empty state, 2026-07-09). Wrap
       normally, and break over-long unbreakable tokens (ARNs in error hints). */
    white-space: normal;
    overflow-wrap: break-word;
    background: var(--lp-pane-sunk-bg, #f5f2ec);
    border: 1px dashed var(--lp-pane-border-strong, #ddd6c9);
    border-radius: 8px;
}
:where(.lp-pane-state-msg) {
    font-size: 13px;
    color: var(--lp-pane-meta-ink, #6b645a);
    font-style: italic;
}
:where(.lp-pane-state-hint) {
    font-size: 11px;
    color: var(--lp-pane-meta-ink, #6b645a);
}
:where(.lp-pane-state--error) {
    background: var(--lp-rag-critical-soft-bg);
    border-color: var(--lp-rag-critical-soft-border);
}
:where(.lp-pane-state--error .lp-pane-state-msg) {
    color: var(--lp-rag-critical-soft-ink);
    font-style: normal;
}
/* okState — a positive "all clear" confirmation: SOLID light-green box (not
   the grey dashed "no data" absence). Uses the AA soft-success RAG tokens
   (dark-green ink on light-green wash); the wording carries the state too. */
:where(.lp-pane-state--clear) {
    background: var(--lp-rag-success-soft-bg, #ecfdf5);
    border-style: solid;
    border-color: var(--lp-rag-success-soft-border, #a7f3d0);
}
:where(.lp-pane-state--clear .lp-pane-state-msg) {
    color: var(--lp-rag-success-soft-ink, #065f46);
    font-style: normal;
}
:where(.lp-pane-state--clear .lp-pane-state-hint) {
    color: var(--lp-rag-success-soft-ink, #065f46);
}
:where(.lp-pane-state--loading) {
    background: transparent;
    border: none;
}
/* discovering — a soft-BLUE "we're actively refreshing" block shown INSTEAD of
   the neutral grey empty state while a stale/building response is being
   re-polled. Solid border (not dashed) so it reads as active, not absent — a
   categorical "nothing here" would be misleading mid-rebuild. */
:where(.lp-pane-state--discovering),
:where(.lp-pane-state--inactive) {
    background: var(--lp-rag-info-soft-bg, #eff6ff);
    border-style: solid;
    border-color: var(--lp-rag-info-soft-border, #bfdbfe);
}
:where(.lp-pane-state--discovering .lp-pane-state-msg),
:where(.lp-pane-state--inactive .lp-pane-state-msg) {
    color: var(--lp-rag-info-soft-ink, #1e40af);
    font-style: normal;
}
:where(.lp-pane-state--discovering .lp-pane-state-hint),
:where(.lp-pane-state--inactive .lp-pane-state-hint) {
    color: var(--lp-rag-info-soft-ink, #1e40af);
}
:where(.lp-sd-countdown) { font-variant-numeric: tabular-nums; font-weight: 650; }
/* The stale-while-revalidate banner reuses the .lp-pane-subhead PRIMITIVE (an
   already-full-width soft-blue band) — no bespoke banner CSS. `--stale` is a
   marker hook (scheduleStaleUpgrade retargets its text on give-up) AND flushes
   it to the pane chrome: zero the subhead's vertical gaps + top hairline so it
   sits directly under the head as one continuous band. The base's negative
   horizontal margin (full-bleed) + bottom rule are kept, so it stays full-width,
   its text stays aligned with the head title, and it separates cleanly from the
   rows below. */
:where(.lp-pane-subhead--stale) {
    margin-top: 0;
    margin-bottom: 0;
    border-top: 0;
    /* Nudge the status text a touch inside the head-title alignment. The base
       padding-left is --lp-pane-pad-x (16px) and this REPLACES it (padding is
       not additive) → net +8px; the full-bleed lives on the negative MARGIN,
       not a negative padding. */
    padding-left: calc(var(--lp-pane-pad-x) + 8px);
}
/* Keep the status on ONE line as the pane narrows: scale the font with the
   pane's own inline size (the same cqi mechanism the inventory table uses —
   the pane is a `container-type: inline-size` query container), nowrap to
   forbid the wrap, and ellipsis as the extreme-narrow safety. */
:where(.lp-pane-subhead--stale > span) {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: clamp(11px, 3.2cqi, 14px);
}
:where(.lp-pane-state-spinner) {
    width: 18px;
    height: 18px;
    border: 2px solid var(--lp-pane-border-strong, #ddd6c9);
    border-top-color: var(--lp-pane-ink-3, #6b645a);
    border-radius: 50%;
    animation: lp-pane-spin .8s linear infinite;
}
@keyframes lp-pane-spin {
    to { transform: rotate(360deg); }
}


/* ==================================================================
   .lp-r-* helper classes (scoreRing / gauge / sparkline / statusPill /
   pager / paged-rows viewport). Folded in from lp-render.js's runtime
   injectCss() on 2026-07-14 — no more JS-injected <style>. Colours use
   the --lp-fn-* functional token layer, resolved via the @imports at
   the top of this file. Appended last to preserve the prior cascade
   order (the injected <style> was the last stylesheet in <head>).
   ================================================================== */
.lp-r-body {
    padding: var(--lp-pane-gap-lg) var(--lp-pane-pad-x);
}
.lp-r-body[data-max-rows] {
    overflow-y: auto;
    max-height: var(--lp-r-body-max-h);
}

/* ---- Section ------------------------------------------------- */
.lp-r-section { margin: 0 0 var(--lp-pane-section-gap); }
.lp-r-section:last-child { margin-bottom: 0; }
.lp-r-section-head {
    display: flex; align-items: center; gap: var(--lp-pane-gap);
    margin: 0 0 var(--lp-pane-gap-sm);
}
.lp-r-section-label {
    font-size: var(--lp-pane-fs-cell);
    font-weight: var(--lp-pane-fw-emphasis);
    color: var(--lp-fn-section-head-ink);
    text-transform: uppercase;
    letter-spacing: var(--lp-pane-letter-spacing-uppercase);
}
.lp-r-section-tag {
    font-size: var(--lp-pane-fs-pill);
    color: var(--lp-fn-section-tag-ink);
}

/* ---- Subhead (lighter than section-head) -------------------- */
.lp-r-subhead {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--lp-pane-gap);
    margin: 0 0 var(--lp-pane-gap-sm);
    font-size: var(--lp-pane-fs-cell);
    color: var(--lp-fn-ink);
}

/* ---- Canonical .lp-pane-subhead + .lp-pane-section-end ---------
   REMOVED 2026-05-27. These declarations are now in
   embed/sd-embed.css under :where(.lp-pane-subhead) so a single
   source owns the styling. The previous plain-selector rules here
   (specificity 0,0,1,0) were overriding the newer :where() rules
   in sd-embed.css (specificity 0,0,0,0), preventing the full-pane-
   width fix from taking effect on panes that loaded lp-render.js
   alongside sd-embed.css. */

/* ---- Embedded table ----------------------------------------
   Both class names are styled identically: .lp-r-table-frame is the
   new lp-render output; .lp-pane-embedded-table-frame is the legacy
   class many pre-Phase-9 panes still use. Aliasing means we can drop
   the per-pane redeclarations in pane-tests-v2 even before each pane
   migrates its JS. */
.lp-r-table-frame,
.lp-pane-embedded-table-frame {
    border: var(--lp-pane-table-frame-w) solid var(--lp-fn-table-frame-color);
    border-radius: var(--lp-pane-radius-sm);
    overflow: hidden;
    background: var(--lp-fn-bg);
}
/* Legacy class — restore the top/bottom margin that per-pane CSS
   redeclarations used to provide before the Phase 9 cleanup. The new
   .lp-r-table-frame stays at margin:0 because lpRender.section() and
   adjacent helpers manage their own spacing. */
.lp-pane-embedded-table-frame { margin: 12px 0; }
.lp-r-table-frame             { margin: 0; }
/* Paginated rows viewport — a FIXED height in CSS (header + N nominal rows), so
   the pane is the same height on every page and across browsers. Deliberately
   NOT measured from real row heights: rows wrap differently per page (and
   Firefox/Chromium measure differently), which made the pane resize per page.
   Rows that fit show with a little slack; rows that wrap taller overflow and
   scroll (with the "scroll for more" hint). Heights are tunable vars, overridden
   at narrow widths where the font shrinks. thead is position:sticky. */
.lp-r-scroll-wrap { position: relative; }
.lp-r-table-scroll {
    --lp-r-head-h: 34px;
    --lp-r-row-h: 43px;   /* a hair of slack so a full page doesn't trip the hint */
    overflow-y: auto;
    overflow-x: hidden;
    height: calc(var(--lp-r-head-h) + var(--lp-r-page-rows, 6) * var(--lp-r-row-h));
    /* NO scrollbar-gutter here (removed 2026-07-15). `scrollbar-gutter: stable`
       ALWAYS reserves a right-side gutter, but on machines with overlay
       scrollbars (e.g. macOS "show scrollbars: when scrolling") the overlay bar
       never paints into it — leaving a permanent white strip to the right of the
       full-bleed sticky thead, and appearing to "overlap" it on scroll. That
       machine-dependence (same browser, different box) was the tell. It was
       originally added to stop a row-expand from toggling the scrollbar and
       reflowing the table-layout:auto columns — but this box is FIXED-HEIGHT, so
       when it overflows the scrollbar is stably present (a styled classic bar via
       scrollbar-width:thin / ::-webkit-scrollbar below), and expanding a row
       inside an already-overflowing list doesn't toggle it. So the gutter earned
       us nothing and cost us the strip. Default `auto` = the bar takes its own
       space when present, thead fills the content beside it, no strip. */
    scrollbar-width: thin;   /* Firefox: slim, visible while scrollable */
    scrollbar-color: var(--lp-fn-pager-border, #cbbfa8) transparent;
    /* Blend the scrollbar COLUMN with the pane on machines whose OS is set to
       "Show scroll bars: Always" (or that have a mouse connected) — there the bar
       occupies a real ~11px column with a transparent track, which would show the
       white pane bg *behind* the amber sticky thead as a strip. Paint the
       container bg amber for exactly the header height, pane-bg below: the table
       covers this everywhere except that scrollbar column, so the column reads
       amber-at-the-header / white-in-the-body and the strip disappears. Fully
       hidden under overlay scrollbars (the column doesn't exist there). Aligned
       to the sticky thead via the same --lp-r-head-h that sizes the box.
       The final band also carries the thead's 1px amber-700 border-bottom (see
       `:where(.lp-xtable > thead > tr > th)`) across the column, so the divider
       line under the header doesn't stop short of the scrollbar either.
       Aligned to --lp-r-head-real (the ACTUAL rendered thead height, measured by
       wirePagedTables) so the band matches the cells' border to the pixel — the
       fixed --lp-r-head-h is a row-count estimate and ran ~4px short of the real
       header. Falls back to --lp-r-head-h before JS runs / if unmeasured. */
    background: linear-gradient(
        to bottom,
        var(--lp-pane-thead-bg) 0,
        var(--lp-pane-thead-bg) calc(var(--lp-r-head-real, var(--lp-r-head-h)) - 1px),
        var(--lp-pane-amber-700) calc(var(--lp-r-head-real, var(--lp-r-head-h)) - 1px),
        var(--lp-pane-amber-700) var(--lp-r-head-real, var(--lp-r-head-h)),
        var(--lp-pane-bg) var(--lp-r-head-real, var(--lp-r-head-h)),
        var(--lp-pane-bg) 100%);
}
/* WebKit: a slim classic (non-overlay) scrollbar — stays visible while the rows
   overflow (so it's clear there's more), and absent when they fit. */
.lp-r-table-scroll::-webkit-scrollbar { width: 11px; }
.lp-r-table-scroll::-webkit-scrollbar-track { background: transparent; }
.lp-r-table-scroll::-webkit-scrollbar-thumb {
    background: var(--lp-fn-pager-border, #cbbfa8);
    border-radius: 6px;
    border: 3px solid transparent;
    background-clip: content-box;
}
/* "Scroll for more" — a fade + amber pill over the bottom of the viewport,
   toggled by wirePagedTables when the rows overflow and aren't at the bottom.
   A reliable cue that doesn't depend on the OS showing the scrollbar. */
.lp-r-scroll-fade {
    position: absolute; left: 0; right: 0; bottom: 0; height: 34px;
    display: flex; align-items: flex-end; justify-content: center;
    padding-bottom: 4px; pointer-events: none;
    background: linear-gradient(180deg, transparent 0, var(--lp-fn-bg, #fff) 92%);
    transition: opacity 160ms ease;
}
.lp-r-scroll-fade[hidden] { display: none; }
.lp-r-scroll-fade span {
    background: var(--lp-pane-amber-100, #fdecc8);
    color: var(--lp-pane-amber-900, #7a5a17);
    font-size: 10.5px; font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; padding: 2px 10px; border-radius: 999px;
}
/* Narrow-pane (@container <520px): tighter nominal row heights so the paged/
   scroll viewport (.lp-r-table-scroll, used by expandableTable's paged mode)
   shows a sensible page height. */
@container (max-width: 520px) {
    .lp-r-table-scroll { --lp-r-head-h: 30px; --lp-r-row-h: 40px; }
}
/* ---- Status pill ------------------------------------------- */
.lp-r-pill {
    display: inline-flex; align-items: center;
    padding: 1px 8px;
    border-radius: 10px;
    font-size: var(--lp-pane-fs-pill);
    font-weight: var(--lp-pane-fw-head);
    letter-spacing: 0.02em;
    line-height: 1.55;
    white-space: nowrap;
    border: 1px solid transparent;
    text-transform: lowercase;
}
.lp-r-pill--ok      { background: var(--lp-status-ok-bg);      color: var(--lp-status-ok-ink);      border-color: var(--lp-status-ok-border); }
.lp-r-pill--warn    { background: var(--lp-status-warning-bg); color: var(--lp-status-warning-ink); border-color: var(--lp-status-warning-border); }
.lp-r-pill--danger  { background: var(--lp-status-danger-bg);  color: var(--lp-status-danger-ink);  border-color: var(--lp-status-danger-border); }
.lp-r-pill--info    { background: var(--lp-status-info-bg);    color: var(--lp-status-info-ink);    border-color: var(--lp-status-info-border); }
/* neutral = white background (distinct from info's slate tint), slate ink +
   border. 7.0:1 (#475569 on #ffffff) — WCAG AA. */
.lp-r-pill--neutral { background: var(--lp-pane-bg);           color: var(--lp-status-info-ink);    border-color: var(--lp-status-info-border); }

/* ---- Chips / filters --------------------------------------- */
.lp-r-chips {
    display: flex; flex-wrap: wrap;
    gap: var(--lp-pane-gap-sm);
    margin: 0 0 var(--lp-pane-gap-lg);
}
.lp-r-chip {
    display: inline-flex; align-items: center;
    gap: var(--lp-pane-gap-sm);
    background: var(--lp-fn-chip-bg);
    color: var(--lp-fn-chip-ink);
    border: 1px solid var(--lp-fn-chip-border);
    padding: 4px 12px;
    border-radius: 999px;
    font: inherit;
    font-size: var(--lp-pane-fs-meta);
    font-weight: var(--lp-pane-fw-meta);
    cursor: pointer;
}
.lp-r-chip:hover {
    background: var(--lp-fn-chip-hover-bg);
    border-color: var(--lp-fn-chip-hover-border);
}
.lp-r-chip[aria-pressed="true"] {
    background: var(--lp-fn-chip-pressed-bg);
    color: var(--lp-fn-chip-pressed-ink);
    border-color: var(--lp-fn-chip-pressed-border);
    font-weight: var(--lp-pane-fw-head);
}
.lp-r-chip-count {
    background: var(--lp-fn-chip-count-bg);
    color: var(--lp-fn-chip-count-ink);
    padding: 1px 6px;
    border-radius: 999px;
    font-size: var(--lp-pane-fs-pill);
    font-weight: var(--lp-pane-fw-head);
}
.lp-r-chip[aria-pressed="true"] .lp-r-chip-count {
    background: var(--lp-fn-chip-count-pressed-bg);
    color: var(--lp-fn-chip-count-pressed-ink);
}

/* ---- Counters ---------------------------------------------- */
.lp-r-counters {
    display: flex; flex-wrap: wrap;
    justify-content: center;
    gap: 18px;
    padding: 0 0 var(--lp-pane-gap-lg);
}
.lp-r-counter {
    display: inline-flex; align-items: center;
    gap: 8px;
    font-size: var(--lp-pane-fs-cell);
    color: var(--lp-fn-ink);
}
.lp-r-counter-num {
    font-size: var(--lp-pane-fs-counter-num);
    font-weight: var(--lp-pane-fw-emphasis);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--lp-fn-counter-num-ink);
}
.lp-r-counter--ok     .lp-r-counter-num { color: var(--lp-status-ok-ink); }
.lp-r-counter--warn   .lp-r-counter-num { color: var(--lp-status-warning-ink); }
.lp-r-counter--danger .lp-r-counter-num { color: var(--lp-status-danger-ink); }
.lp-r-counter--info   .lp-r-counter-num { color: var(--lp-status-info-ink); }
.lp-r-counter-label {
    text-transform: uppercase;
    letter-spacing: var(--lp-pane-letter-spacing-uppercase);
    font-size: var(--lp-pane-fs-pill);
    color: var(--lp-fn-counter-label-ink);
}

/* ---- Drawer (per-row expand body) -------------------------- */
.lp-r-drawer {
    background: var(--lp-fn-drawer-bg);
    padding: var(--lp-pane-gap-lg) var(--lp-pane-pad-x);
}
.lp-r-drawer-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 12px; row-gap: 4px;
    margin: 0 0 8px;
    font-size: var(--lp-pane-fs-meta);
}
.lp-r-drawer-grid dt {
    color: var(--lp-fn-ink-dim);
    text-transform: uppercase;
    letter-spacing: var(--lp-pane-letter-spacing-uppercase);
    font-size: var(--lp-pane-fs-pill);
    font-weight: var(--lp-pane-fw-head);
}
.lp-r-drawer-grid dd {
    margin: 0;
    color: var(--lp-fn-ink);
    font-variant-numeric: tabular-nums;
}
.lp-r-drawer-link {
    display: inline-block;
    margin-top: 4px;
    font-size: var(--lp-pane-fs-meta);
    color: var(--lp-fn-link-ink);
    text-decoration: none;
    border-bottom: 1px dotted var(--lp-fn-link-underline);
}
.lp-r-drawer-link:hover {
    background: var(--lp-fn-link-hover-bg);
    border-bottom-style: solid;
}

/* ---- Loading / empty / error ------------------------------- */
.lp-r-loading {
    display: flex; align-items: center;
    gap: var(--lp-pane-gap);
    padding: var(--lp-pane-pad-y) var(--lp-pane-pad-x);
    color: var(--lp-fn-ink-dim);
    font-size: var(--lp-pane-fs-cell);
}
.lp-r-spinner {
    width: 14px; height: 14px;
    border-radius: 50%;
    border: 2px solid var(--lp-fn-spinner-track);
    border-top-color: var(--lp-fn-spinner-accent);
    animation: lp-r-spin 0.8s linear infinite;
    flex-shrink: 0;
}
@keyframes lp-r-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .lp-r-spinner { animation-duration: 2.4s; }
}

/* Compound-refresh progress card.
   Used by panes that fan out 50+ discovery calls (region-map,
   global-search) where the round-trip can take 20-30s on cache
   miss. Replaces the bare spinner with a richer "we're scanning,
   here's progress" affordance so users don't smash F5 at the
   10-second mark. */
.lp-r-cprogress {
    display: flex; flex-direction: column;
    gap: 10px;
    padding: 22px var(--lp-pane-pad-x) 24px;
}
.lp-r-cprogress-head {
    display: flex; align-items: center; gap: 10px;
    color: var(--lp-fn-ink);
    font-size: var(--lp-pane-fs-cell);
    font-weight: 600;
}
.lp-r-cprogress-sub {
    color: var(--lp-fn-ink-dim);
    font-size: 12px;
    line-height: 1.5;
}
.lp-r-cprogress-bar {
    position: relative;
    height: 6px;
    background: var(--lp-fn-spinner-track);
    border-radius: 999px;
    overflow: hidden;
}
.lp-r-cprogress-bar-fill {
    position: absolute; inset: 0 auto 0 0;
    width: 0;
    background: var(--lp-fn-spinner-accent);
    border-radius: 999px;
    transition: width 0.25s linear;
}
.lp-r-cprogress-status {
    display: flex; justify-content: space-between;
    color: var(--lp-fn-ink-dim);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

.lp-r-empty {
    margin: 0;
    padding: 18px 16px;
    background: var(--lp-fn-empty-bg);
    border: 1px dashed var(--lp-fn-empty-border);
    border-radius: var(--lp-pane-radius-sm);
    color: var(--lp-fn-empty-ink);
    font-size: var(--lp-pane-fs-cell);
    line-height: 1.55;
}
.lp-r-empty-title {
    color: var(--lp-fn-empty-emphasis-ink);
    font-weight: var(--lp-pane-fw-head);
    margin: 0 0 6px;
}
.lp-r-empty-cta {
    margin-top: 10px;
}

.lp-r-error {
    margin: 0;
    padding: 12px var(--lp-pane-pad-x);
    background: var(--lp-fn-error-bg);
    border-left: 3px solid var(--lp-fn-error-border);
    color: var(--lp-fn-error-ink);
    font-size: var(--lp-pane-fs-cell);
    line-height: 1.55;
}
.lp-r-error-title {
    font-weight: var(--lp-pane-fw-head);
    margin: 0 0 4px;
}

/* ---- Foot bar (optional bottom strip) ---------------------- */
.lp-r-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--lp-pane-foot-gap);
    padding: var(--lp-pane-foot-pad-y) var(--lp-pane-foot-pad-x);
    background: var(--lp-fn-foot-bg);
    color: var(--lp-fn-foot-ink);
    border-top: 1px solid var(--lp-fn-foot-border-top);
    font-size: var(--lp-pane-fs-meta);
}
.lp-r-foot-status {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    flex: 1;
}
.lp-r-foot-actions {
    display: flex;
    gap: var(--lp-pane-gap-sm);
    flex-shrink: 0;
}
.lp-r-foot-action {
    appearance: none;
    background: var(--lp-fn-foot-action-bg);
    color: var(--lp-fn-foot-action-ink);
    border: 1px solid var(--lp-fn-foot-action-border);
    padding: 3px 10px;
    border-radius: 4px;
    font: inherit;
    font-size: var(--lp-pane-fs-meta);
    text-decoration: none;
    cursor: pointer;
    transition: background-color 120ms ease;
}
.lp-r-foot-action:hover { background: var(--lp-fn-foot-action-bg-hover); }
@container (max-width: 480px) {
    .lp-r-foot {
        flex-direction: column;
        align-items: flex-start;
    }
    .lp-r-foot-actions { flex-wrap: wrap; }
}

/* ---- Pager -------------------------------------------------
   Three-column grid keeps the prev/next buttons pinned at the
   left/right edges of the pager so the user can repeatedly click
   them without the cursor needing to chase. The page-number group
   centres in the middle column and may shift slightly within it as
   the visible-pages set changes (1, 2 ... 5  →  1 ... 4, 5), but
   prev and next stay anchored. */
.lp-r-pager {
    /* Centred, fixed-width button group — NOT the old 3-col grid that pushed
       prev/next to the edges. Every button is a fixed size, so the bar keeps a
       constant width and the numbers never shift as you page. */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 9px var(--lp-pane-gap);
    /* Amber head wash + a TOP divider so the nav bar reads as a distinct footer
       control. NOT a full border — at a flush pane's edge the side/bottom sides
       clashed with the pane's own border (2026-07-14); the head-bg + top rule
       carry the "distinct control" intent without the conflict. */
    background: var(--lp-pane-head-bg);
    border-top: 1px solid var(--lp-pane-amber-900);
    font-size: var(--lp-pane-fs-meta);
}
.lp-r-pager-pages { display: flex; align-items: center; gap: 4px; }
.lp-r-pager-btn {
    appearance: none;
    box-sizing: border-box;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* White chip on the cream bar so buttons read as controls (the old
       transparent-on-amber merged into the background). */
    background: var(--lp-fn-bg, #ffffff);
    color: var(--lp-fn-pager-ink);
    border: 1px solid var(--lp-fn-pager-btn-border, var(--lp-amber-300, #fcd34d));
    padding: 0 6px;
    border-radius: 5px;
    font: inherit;
    font-size: var(--lp-pane-fs-meta);
    font-variant-numeric: tabular-nums;   /* equal-width digits */
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease;
}
/* Number buttons: one fixed width sized for up to 3 digits (≤999 pages), so
   1 / 10 / 100 all occupy the same slot and the window can't jitter. Beyond
   999 the slot grows uniformly rather than clipping. */
.lp-r-pager-num { min-width: 38px; }
/* Nav arrows: their own fixed width, slightly larger glyph. */
.lp-r-pager-nav { min-width: 30px; font-size: 15px; line-height: 1; }
.lp-r-pager-btn:hover:not([disabled]):not([aria-current="page"]) {
    background: var(--lp-fn-pager-hover-bg);
    border-color: var(--lp-fn-pager-active-bg);
}
.lp-r-pager-btn[aria-current="page"] {
    /* Strong fill so the current page is unmistakable (was pale amber-300). */
    background: var(--lp-fn-pager-active-bg);
    color: var(--lp-fn-pager-active-ink);
    border-color: var(--lp-fn-pager-active-bg);
    cursor: default;
    font-weight: var(--lp-pane-fw-head);
}
.lp-r-pager-btn[disabled] {
    color: var(--lp-fn-pager-disabled-ink);
    background: var(--lp-fn-pager-bg);
    border-color: var(--lp-fn-pager-border);
    cursor: default;
}

/* ---- Static-mode neutralisations ---------------------------- */
[data-lp-mode="static"] .lp-r-body {
    overflow: visible !important;
    max-height: none !important;
}
[data-lp-mode="static"] .lp-xtable[data-sticky-header="true"] thead th {
    position: static !important;
}
[data-lp-mode="static"] .lp-r-pager,
[data-lp-mode="static"] .lp-pane-refresh,
[data-lp-mode="static"] [data-lp-interactive] {
    display: none !important;
}
[data-lp-mode="static"] *,
[data-lp-mode="static"] *::before,
[data-lp-mode="static"] *::after {
    transition: none !important;
    animation: none !important;
}

/* ---- gauge (determinate value bar; single or stacked) ------- */
.lp-r-gauge {
    display: inline-flex; align-items: center; gap: 8px;
    min-width: 96px; width: 100%; max-width: 220px; vertical-align: middle;
}
.lp-r-gauge-track {
    flex: 1 1 auto; display: flex; min-width: 44px; height: 8px;
    border-radius: 999px; overflow: hidden;
    background: var(--lp-pane-amber-100, #fdecc8);
    /* EXPERIMENT (2026-07-16): a 1px mid-grey outline (the header-box border) so
       the pale amber track — the washed-out "yellow" behind a low fill — reads as
       a defined bar instead of melting into the row. box-sizing keeps it 8px.
       Shared gauge primitive (quota panes, rightsize, tag-coverage, free-tier,
       ecs-services, …) — refine or scope if it doesn't read cleanly. */
    box-sizing: border-box;
    border: 1px solid var(--lp-pane-header-box-border, #8f8b82);
}
.lp-r-gauge-fill { height: 100%; }
.lp-r-gauge-label {
    flex: 0 0 auto; font-size: 12px; color: var(--lp-pane-ink-dim, #8a8378);
    white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* Radial score ring — a circular 0–max gauge with the score in the centre.
   Static (no motion → respects prefers-reduced-motion by construction). */
.lp-r-ring { display: inline-block; vertical-align: middle; line-height: 0; }
.lp-r-ring svg { width: 100%; height: auto; display: block; }
.lp-r-ring-track { stroke: var(--lp-pane-amber-100, #fdecc8); }
.lp-r-ring-value { font-weight: 700; fill: var(--lp-pane-ink, #1a1814);
    font-variant-numeric: tabular-nums; }
.lp-r-ring-label { fill: var(--lp-pane-ink-dim, #8a8378); }
