/* ==========================================================================
   FL510 — PORTAL SHELL (shared chrome, extracted 2026-07-22)
   --------------------------------------------------------------------------
   The generic two-tier portal chrome, promoted out of the accepted /Portal/Billing
   visual (billing-modules.css) into a SHARED, multi-consumer stylesheet: the desktop
   persistent rail + mobile :target nav sheet, the appbar + KPI strip, module-card
   grids, the badge family, sheet chrome, and the legacy-harmonization nudges.

   FIRST CONSUMER: the external member portal (_ExternalPortalLayout.cshtml, scope
   `.external-shell.portal-shell.ds`). The accepted /Portal/Billing surface is
   DELIBERATELY LEFT UNTOUCHED — it keeps its own `.billing-shell .bl-*` rules so
   the live page is guaranteed regression-clean. Migrating billing onto these shared
   `.ps-*` classes is the remaining half of internal item #40 (a separately-reviewed
   change); this file pre-pays it by proving the shared foundation on the external side.

   TOKEN RECONCILIATION (translation discipline, same as the billing shell): every
   color/space/radius derives from the EXISTING three-tier design system
   (wwwroot/css/tokens.css). No forked palette. The `--ps-*` locals below are
   COMPONENT-tier locals referencing SEMANTIC `--ds-*` tokens — so light/dark (and the
   manual [data-theme] toggle) still swap in ONE place. PER-TAIL HUES DO NOT COME FROM
   `--ds-aircraft-N` (corrected 2026-07-30) — that eight-slot palette is superseded
   reference only. A tail's hue is the operator's PICKED colour, else the colour DERIVED
   from its registration, resolved by Services/DesignSystemPalette.Resolve and emitted
   inline as `--ds-ac-l`/`--ds-ac-d` alongside the `.ds-tail` marker class. The
   `--ps-accent` local named here has NO producer today (grep-verified).

   SCOPE: everything is under `.portal-shell` (which also carries `.ds`), so the opt-in
   contract holds — no bare element is styled, no non-portal page is touched.
   ========================================================================== */

.portal-shell {
    /* --- surfaces / ink (semantic tier) --- */
    --ps-bg: var(--ds-surface);
    --ps-surface: var(--ds-surface-raised);
    --ps-surface-2: color-mix(in srgb, var(--ds-surface-raised) 88%, var(--ds-line));
    --ps-line: var(--ds-line);
    --ps-line-strong: color-mix(in srgb, var(--ds-line) 55%, var(--ds-text-secondary));
    --ps-ink: var(--ds-text-primary);
    --ps-ink-2: var(--ds-text-secondary);
    --ps-ink-3: color-mix(in srgb, var(--ds-text-secondary) 78%, var(--ds-surface));
    --ps-ink-faint: color-mix(in srgb, var(--ds-text-secondary) 52%, var(--ds-surface));
    /* Control boundary. NOT --ps-line / --ps-line-strong: both are card hairlines and measured
       1.35:1 and 2.478:1 against the light pane, under the SC 1.4.11 3:1 floor for the boundary
       that identifies a form control. Measured in a browser at 390px: 3.87:1 light, 5.80:1 dark. */
    --ps-field-border: var(--ps-ink-3);

    /* --- primary (accent) --- */
    --ps-primary: var(--ds-accent);
    --ps-primary-on: var(--ds-accent-on);
    --ps-primary-wash: color-mix(in srgb, var(--ds-accent) 15%, var(--ds-surface));

    /* --- semantic status + derived washes --- */
    --ps-good: var(--ds-ok);
    --ps-good-wash: color-mix(in srgb, var(--ds-ok) 16%, var(--ds-surface));
    --ps-warn: var(--ds-warn);
    --ps-warn-wash: color-mix(in srgb, var(--ds-warn) 18%, var(--ds-surface));
    --ps-crit: var(--ds-danger);
    --ps-crit-wash: color-mix(in srgb, var(--ds-danger) 16%, var(--ds-surface));
    --ps-info: var(--ds-info);
    --ps-info-wash: color-mix(in srgb, var(--ds-info) 14%, var(--ds-surface));

    /* --- geometry / elevation / motion --- */
    --ps-radius: var(--ds-radius-3);
    --ps-radius-sm: var(--ds-radius-2);
    --ps-shadow-card: var(--ds-shadow-1);
    --ps-shadow-pop: var(--ds-shadow-2);
    --ps-shadow-sheet: 0 -8px 40px color-mix(in srgb, var(--ds-text-primary) 22%, transparent);
    --ps-ease: var(--ds-ease);
    --ps-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

    background: var(--ps-bg);
    min-height: 100vh;
    min-height: 100svh;
    color: var(--ps-ink);
}

/* App frame: rail (desktop) + main column. */
.portal-shell .ps-app { min-height: 100vh; min-height: 100svh; }
.portal-shell .ps-main { min-width: 0; display: flex; flex-direction: column; }
.portal-shell .ps-rail { display: none; }
.portal-shell .tnum { font-variant-numeric: tabular-nums; }
.portal-shell .ps-mono { font-family: var(--ps-mono); font-variant-numeric: tabular-nums; }

/* ============ appbar: topbar + KPI strip ============ */
.portal-shell .ps-appbar {
    position: sticky; top: 0; z-index: 30;
    background: color-mix(in srgb, var(--ps-bg) 84%, transparent);
    backdrop-filter: saturate(180%) blur(14px);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    border-bottom: 1px solid var(--ps-line);
}
.portal-shell .ps-topbar {
    display: flex; align-items: center; gap: 12px;
    padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 10px;
}
.portal-shell .ps-brand { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.portal-shell .ps-eyebrow {
    font-size: 10.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ps-ink-3);
}
.portal-shell .ps-brand h1 {
    margin: 0; font-size: 17px; font-weight: 650; letter-spacing: -.01em;
    display: flex; align-items: center; gap: 8px; line-height: 1.2;
}
.portal-shell .ps-livedot {
    width: 7px; height: 7px; border-radius: 50%; background: var(--ps-good);
    box-shadow: 0 0 0 3px var(--ps-good-wash); flex: none;
}
.portal-shell .ps-topbar .ps-spacer { flex: 1; }
.portal-shell .ps-iconbtn {
    width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--ps-line);
    background: var(--ps-surface); display: grid; place-items: center; color: var(--ps-ink-2);
    transition: background .15s var(--ps-ease), border-color .15s; flex: none; text-decoration: none;
}
.portal-shell .ps-iconbtn:hover { background: var(--ps-surface-2); border-color: var(--ps-line-strong); }
.portal-shell .ps-iconbtn svg { width: 18px; height: 18px; }
.portal-shell .ps-avatar {
    width: 38px; height: 38px; border-radius: 50%; flex: none; display: grid; place-items: center;
    background: var(--ps-primary-wash); color: var(--ps-primary); font-size: 12.5px; font-weight: 750;
    border: 1px solid color-mix(in srgb, var(--ps-primary) 22%, transparent);
}

.portal-shell .ps-kpis {
    display: flex; align-items: center; gap: 8px;
    padding: 2px 16px 12px; overflow-x: auto; scrollbar-width: none; margin: 0;
}
.portal-shell .ps-kpis::-webkit-scrollbar { display: none; }
.portal-shell .ps-kpis .ps-plabel { flex: none; font-size: 12px; font-weight: 650; color: var(--ps-ink-2); }
.portal-shell .ps-kpi {
    flex: none; display: inline-flex; align-items: center; gap: 8px; text-decoration: none;
    padding: 7px 11px; border-radius: 10px; background: var(--ps-surface); border: 1px solid var(--ps-line);
    min-height: 40px; transition: border-color .15s, background .15s;
}
.portal-shell .ps-kpi:hover { border-color: var(--ps-line-strong); background: var(--ps-surface-2); }
.portal-shell .ps-kpi .ps-kl { font-size: 11px; font-weight: 600; color: var(--ps-ink-3); white-space: nowrap; }
.portal-shell .ps-kpi .ps-kv {
    font-size: 13px; font-weight: 750; letter-spacing: -.01em;
    font-variant-numeric: tabular-nums; color: var(--ps-ink); white-space: nowrap;
}
.portal-shell .ps-kpi.is-warn .ps-kv { color: var(--ps-warn); }
.portal-shell .ps-kpi.is-good .ps-kv { color: var(--ps-good); }
.portal-shell .ps-kpi.is-crit .ps-kv { color: var(--ps-crit); }

/* ============ mobile module-card menu ============ */
.portal-shell .ps-menu { padding: 16px; }
.portal-shell .ps-grid { display: grid; gap: 12px; }
.portal-shell .ps-card {
    position: relative; text-align: left; width: 100%; text-decoration: none; color: inherit;
    background: var(--ps-surface); border: 1px solid var(--ps-line); border-radius: var(--ps-radius);
    box-shadow: var(--ps-shadow-card); padding: 16px; display: flex; flex-direction: column; gap: 12px;
    transition: transform .16s var(--ps-ease), border-color .16s, box-shadow .16s;
}
.portal-shell .ps-card:hover { transform: translateY(-2px); border-color: var(--ps-line-strong); box-shadow: var(--ps-shadow-pop); }
.portal-shell .ps-card:active { transform: translateY(0) scale(.995); }
.portal-shell .ps-card.is-primary::before {
    content: ""; position: absolute; left: 0; top: 14px; bottom: 14px; width: 3px; border-radius: 3px; background: var(--ps-primary);
}
.portal-shell .ps-chead { display: flex; align-items: flex-start; gap: 12px; }
.portal-shell .ps-glyph {
    width: 36px; height: 36px; border-radius: 10px; flex: none; display: grid; place-items: center;
    background: var(--ps-primary-wash); color: var(--ps-primary);
}
.portal-shell .ps-glyph svg { width: 19px; height: 19px; }
.portal-shell .ps-ctitle { flex: 1; min-width: 0; }
.portal-shell .ps-kicker {
    font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ps-ink-3);
}
.portal-shell .ps-card h2, .portal-shell .ps-card h3 { margin: 1px 0 0; font-size: 17px; font-weight: 640; letter-spacing: -.01em; }
.portal-shell .ps-go { color: var(--ps-ink-faint); flex: none; margin-top: 3px; transition: transform .16s var(--ps-ease), color .16s; }
.portal-shell .ps-card:hover .ps-go { transform: translateX(3px); color: var(--ps-primary); }
.portal-shell .ps-go svg { width: 18px; height: 18px; }

/* ============ chips / tail chips / badges (shared family) ============ */
.portal-shell .ps-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.portal-shell .ps-chip {
    display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; line-height: 1;
    padding: 6px 10px; border-radius: 999px; background: var(--ps-surface-2); color: var(--ps-ink-2); border: 1px solid var(--ps-line);
}
.portal-shell .ps-chip .n { font-variant-numeric: tabular-nums; color: var(--ps-ink); font-weight: 700; }
.portal-shell .ps-chip.is-good { background: var(--ps-good-wash); border-color: transparent; color: var(--ps-good); }
.portal-shell .ps-chip.is-good .n { color: var(--ps-good); }
.portal-shell .ps-chip.is-warn { background: var(--ps-warn-wash); border-color: transparent; color: var(--ps-warn); }
.portal-shell .ps-chip.is-warn .n { color: var(--ps-warn); }
.portal-shell .ps-chip.is-crit { background: var(--ps-crit-wash); border-color: transparent; color: var(--ps-crit); }
.portal-shell .ps-chip.is-crit .n { color: var(--ps-crit); }
.portal-shell .ps-chip.is-info { background: var(--ps-info-wash); border-color: transparent; color: var(--ps-info); }
.portal-shell .ps-chip.dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .85; }
.portal-shell .ps-tailchip {
    display: inline-flex; align-items: center; gap: 7px; padding: 5px 10px 5px 6px; border-radius: 999px;
    background: var(--ps-surface-2); border: 1px solid var(--ps-line); font-size: 12px; font-weight: 700; font-family: var(--ps-mono);
}
.portal-shell .ps-tswatch { width: 12px; height: 12px; border-radius: 4px; flex: none; }
.portal-shell .ps-badge { font-size: 10px; font-weight: 750; letter-spacing: .04em; padding: 3px 7px; border-radius: 6px; flex: none; background: var(--ps-surface-2); color: var(--ps-ink-2); }
.portal-shell .ps-badge.is-good { background: var(--ps-good-wash); color: var(--ps-good); }
.portal-shell .ps-badge.is-warn { background: var(--ps-warn-wash); color: var(--ps-warn); }
.portal-shell .ps-badge.is-crit { background: var(--ps-crit-wash); color: var(--ps-crit); }
.portal-shell .ps-badge.is-info { background: var(--ps-info-wash); color: var(--ps-info); }
@media (min-width: 720px) {
    .portal-shell .ps-grid.is-two { grid-template-columns: 1fr 1fr; }
    .portal-shell .ps-grid .ps-span { grid-column: 1 / -1; }
}

/* ============ portal nav (shared two-tier chrome) ============ */
.portal-shell .ps-pnav { display: flex; flex-direction: column; gap: 2px; padding: 6px 10px 2px; margin: 0; }
.portal-shell .ps-pitem {
    display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 10px; min-height: 44px;
    color: var(--ps-ink-2); text-decoration: none; font-size: 13px; font-weight: 600;
    transition: background .13s, color .13s;
}
.portal-shell .ps-pitem svg { width: 17px; height: 17px; flex: none; opacity: .8; }
.portal-shell .ps-pitem .ps-lbl { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.portal-shell .ps-pitem .ps-cnt {
    margin-left: auto; font-size: 10.5px; font-weight: 700; font-variant-numeric: tabular-nums;
    padding: 2px 7px; border-radius: 99px; background: var(--ps-surface-2); border: 1px solid var(--ps-line); color: var(--ps-ink-3);
}
.portal-shell .ps-pitem .ps-cnt.is-attn { color: var(--ps-warn); background: var(--ps-warn-wash); border-color: transparent; }
.portal-shell .ps-pitem:hover { background: var(--ps-surface-2); color: var(--ps-ink); }
.portal-shell .ps-pitem[aria-current="page"] { background: var(--ps-primary-wash); color: var(--ps-primary); }
.portal-shell .ps-pitem[aria-current="page"] svg { opacity: 1; }
.portal-shell .ps-pitem.is-soon .ps-lbl { color: var(--ps-ink-faint); }

/* Sign out — POST form, rendered in the desktop rail foot AND the mobile #ext-portal sheet. It exists
   because suppressing _Layout's site-header (internal-design-build-spec.md §4 step 3) removed the only
   sign-out control the external shell had. Styled to match .ps-pitem so it reads as part of the nav;
   44px min-height is the repo touch standard (WCAG 2.2 SC 2.5.8 floor is 24px). Declared OUTSIDE the
   900px block because the mobile sheet is where it matters most on a mobile-first product. */
.portal-shell .ps-signout { margin: 0; padding: 6px 10px 2px; }
.portal-shell .ps-signout__btn {
    display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px; border-radius: 10px;
    min-height: 44px; background: none; border: 0; cursor: pointer; text-align: left;
    color: var(--ps-ink-2); font: inherit; font-size: 13px; font-weight: 600;
    transition: background .13s, color .13s;
}
.portal-shell .ps-signout__btn svg { width: 17px; height: 17px; flex: none; opacity: .8; }
.portal-shell .ps-signout__btn:hover { background: var(--ps-surface-2); color: var(--ps-ink); }

/* ============ scrim + sheets ============ */
.portal-shell .ps-scrim {
    position: fixed; inset: 0; z-index: 40; background: color-mix(in srgb, var(--ds-c-ink-900) 42%, transparent);
    opacity: 0; visibility: hidden; transition: opacity .3s var(--ps-ease), visibility .3s;
}
.portal-shell .ps-scrim.is-show { opacity: 1; visibility: visible; }

/* The mobile portal-nav sheet: pure-CSS :target no-JS fallback, JS-enhanced. */
.portal-shell .ps-sheet { background: var(--ps-surface); display: flex; flex-direction: column; }
.portal-shell #ext-portal.ps-sheet {
    position: fixed; z-index: 50; left: 0; right: 0; bottom: 0; max-height: 90vh;
    border-radius: 20px 20px 0 0; box-shadow: var(--ps-shadow-sheet);
    transform: translateY(100%); visibility: hidden;
    transition: transform .34s var(--ps-ease), visibility 0s linear .34s;
    will-change: transform;
}
.portal-shell #ext-portal.ps-sheet.is-open,
.portal-shell #ext-portal.ps-sheet:target {
    transform: translateY(0); visibility: visible;
    transition: transform .34s var(--ps-ease), visibility 0s;
}
body:has(.portal-shell #ext-portal:target) .portal-shell .ps-scrim { opacity: 1; visibility: visible; }
.portal-shell .ps-grip { width: 38px; height: 4px; border-radius: 99px; background: var(--ps-line-strong); margin: 9px auto 0; flex: none; }
.portal-shell .ps-sheet-head {
    display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--ps-line); flex: none;
}
.portal-shell .ps-sheet-head .ps-glyph { width: 32px; height: 32px; }
.portal-shell .ps-sheet-head .ps-glyph svg { width: 17px; height: 17px; }
.portal-shell .ps-sheet-title { flex: 1; min-width: 0; }
.portal-shell .ps-sheet-title .ps-kicker { font-size: 10px; letter-spacing: .12em; }
.portal-shell .ps-sheet-title h2 { margin: 1px 0 0; font-size: 17px; font-weight: 660; letter-spacing: -.01em; }
.portal-shell .ps-closebtn {
    width: 40px; height: 40px; border-radius: 9px; border: 1px solid var(--ps-line); background: var(--ps-surface);
    display: grid; place-items: center; color: var(--ps-ink-2); flex: none; text-decoration: none;
}
.portal-shell .ps-closebtn:hover { background: var(--ps-surface-2); }
.portal-shell .ps-closebtn svg { width: 16px; height: 16px; }
.portal-shell #ext-portal .ps-sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 6px 2px calc(20px + env(safe-area-inset-bottom, 0px)); }

/* ============ module content pane (each route's body) ============ */
.portal-shell .ps-panes { padding: 8px 12px 60px; }
.portal-shell .ps-modulehead { display: flex; align-items: center; gap: 12px; padding: 14px 6px 8px; }
.portal-shell .ps-modulehead .ps-glyph { width: 34px; height: 34px; }
.portal-shell .ps-modulehead .ps-kicker { font-size: 10px; letter-spacing: .12em; }
.portal-shell .ps-modulehead h1, .portal-shell .ps-modulehead h2 { margin: 1px 0 0; font-size: 19px; font-weight: 660; letter-spacing: -.01em; }
.portal-shell .ps-backlink {
    display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600;
    color: var(--ps-ink-2); text-decoration: none; padding: 8px 4px; min-height: 44px;
}
.portal-shell .ps-backlink:hover { color: var(--ps-primary); }
.portal-shell .ps-foot { padding: 28px 0 14px; text-align: center; font-size: 11px; color: var(--ps-ink-faint); }

/* ==========================================================================
   LEGACY CONTENT HARMONIZATION (added 2026-07-31)
   --------------------------------------------------------------------------
   `site.css` HAS NO DARK THEME. Every colour in it is a light-theme literal
   (`--fl510-text: #3F4B51`, `.contact-heading { color:#0b3d91 }`,
   `.account-status-box { background: var(--fl510-surface) }` = `#F7FAFA`,
   `.btn-primary` = white on the `#4FA5A9→#67B8BB` teal gradient). That was
   harmless while those classes only ever rendered on `_Layout`, which is also
   light-only.

   `/ExternalPortal/InstructorVerification` and `/ExternalPortal/OwnerVerification`
   were re-parented onto `_ExternalPortalLayout` on 2026-07-31. They are the FIRST
   `contact-*`/`account-*` views to render inside this shell, and in dark mode the
   result was measured, in a browser, against the real composited backgrounds:

     .contact-heading  #0b3d91 on #12181D ......... 1.83:1   (SC 1.4.3 needs 4.5)
     .account-status-box body text ................ 1.06:1   light box, dark ink
     .text-danger      #b91c1c on #12181D ......... 2.85:1
     .account-field-help #66757C on #12181D ....... 3.86:1
     .btn-primary label white on #67B8BB .......... 2.29:1   (fails in BOTH themes)
     .ds input border  #2C363D on #12181D ......... 1.50:1   (SC 1.4.11 needs 3.0)

   Everything below re-points those literals at the shell's SEMANTIC tokens, which
   already carry both themes, so the fix cannot drift when a token changes. Scoped
   to `.ps-panes` so it touches only content rendered inside the shell's pane —
   the same class on a `_Layout` page is byte-for-byte unaffected. Post-fix
   measured ratios are in `docs/AREAS/mobile-design-standards.md`.
   ========================================================================== */
.portal-shell .ps-panes .contact-heading,
.portal-shell .ps-panes .schedule-heading,
.portal-shell .ps-panes .account-card h2,
.portal-shell .ps-panes .account-card h3,
.portal-shell .ps-panes .account-section h2 { color: var(--ps-ink); }

.portal-shell .ps-panes .contact-copy,
.portal-shell .ps-panes .account-field-help,
.portal-shell .ps-panes .account-step-meta { color: var(--ps-ink-2); }

.portal-shell .ps-panes .account-status-box,
.portal-shell .ps-panes .account-card {
    background: var(--ps-surface); border-color: var(--ps-line); color: var(--ps-ink);
}

.portal-shell .ps-panes a { color: var(--ps-primary); }
.portal-shell .ps-panes a:hover { color: var(--ps-ink); }
.portal-shell .ps-panes .text-danger { color: var(--ps-crit); }
.portal-shell .ps-panes .contact-email,
.portal-shell .ps-panes .schedule-link { color: var(--ps-crit); }

/* Fields: the `.ds` base layer already gives 16px + a 44px min-height, but its
   background is `--ds-surface` — identical to the pane — so in dark the hairline
   border was the only cue that a control was there, at 1.50:1. The raised surface
   plus `--ps-field-border` puts the boundary at 3.87:1 light / 5.80:1 dark
   (SC 1.4.11 floor is 3:1), both measured in a browser at 390px. */
.portal-shell .ps-panes .contact-input,
.portal-shell .ps-panes input:not([type="checkbox"]):not([type="radio"]),
.portal-shell .ps-panes select,
.portal-shell .ps-panes textarea {
    background: var(--ps-surface); color: var(--ps-ink); border: 1px solid var(--ps-field-border);
    border-radius: var(--ps-radius-sm);
}
/* `.ds input` sets width:100% + a 44px min-height, which is right for text fields
   and wrong for a checkbox sitting inline inside its own label. */
.portal-shell .ps-panes input[type="checkbox"],
.portal-shell .ps-panes input[type="radio"] {
    width: auto; min-height: 0; margin-right: 8px; accent-color: var(--ps-primary);
}
.portal-shell .ps-panes .contact-check {
    display: flex; align-items: center; min-height: 44px; gap: 4px;
}

/* Buttons. `.btn` in site.css is `display:inline-block` with rem padding, which
   measured 42.09px tall (BACKLOG 122) — under the repo's standing 44px
   (REVIEW_CHECKLIST §6a). inline-flex + an explicit min-height fixes the box, and
   the shell accent fixes the label contrast in both themes. */
.portal-shell .ps-panes .btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: var(--ds-touch-min); min-width: var(--ds-touch-min);
}
.portal-shell .ps-panes .btn-primary {
    background: var(--ps-primary); color: var(--ps-primary-on);
    border-color: var(--ps-primary); box-shadow: none;
}
.portal-shell .ps-panes .btn-primary:hover { color: var(--ps-primary-on); filter: brightness(1.06); }
.portal-shell .ps-panes .btn-secondary {
    background: var(--ps-surface); color: var(--ps-ink); border-color: var(--ps-field-border);
}
.portal-shell .ps-panes .btn-secondary:hover {
    background: var(--ps-surface-2); color: var(--ps-ink); border-color: var(--ps-primary);
}

/* Breadcrumb: the current-location marker for a shell page that deliberately has
   NO nav row (the two verification routes). `aria-current="page"` is emitted here,
   on the member's real location, rather than on a nav item that does not exist. */
.portal-shell .ps-crumbs {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    padding: 4px 0 10px; font-size: 12.5px; font-weight: 600; color: var(--ps-ink-2);
}
.portal-shell .ps-crumbs a {
    color: var(--ps-ink-2); text-decoration: none; display: inline-flex; align-items: center;
    min-height: 44px; padding-right: 2px;
}
.portal-shell .ps-crumbs a:hover { color: var(--ps-primary); text-decoration: underline; }
/* The separator is `aria-hidden` but it is still rendered TEXT, so SC 1.4.3 reaches it.
   `--ps-ink-faint` measured 2.286:1 on the light pane; `--ps-ink-2` measures 6.45:1 light /
   8.45:1 dark (browser-measured at 390px). */
.portal-shell .ps-crumbs .ps-crumbsep { color: var(--ps-ink-2); }
.portal-shell .ps-crumbs [aria-current="page"] { color: var(--ps-ink); }

/* ============ desktop >= 900px — persistent rail + content pane ============ */
@media (min-width: 900px) {
    .portal-shell .ps-app { display: flex; align-items: stretch; }
    .portal-shell .ps-rail {
        display: flex; flex-direction: column; width: 248px; flex: none;
        position: sticky; top: 0; align-self: flex-start; height: 100vh; height: 100svh;
        background: var(--ps-surface); border-right: 1px solid var(--ps-line); z-index: 35; overflow-y: auto;
    }
    .portal-shell .ps-rail__brand { display: flex; align-items: center; gap: 11px; padding: 20px 18px 14px; }
    .portal-shell .ps-rail__brand .ps-rlogo {
        width: 34px; height: 34px; border-radius: 10px; background: var(--ps-primary-wash); color: var(--ps-primary);
        display: grid; place-items: center; flex: none;
    }
    .portal-shell .ps-rail__brand .ps-rlogo svg { width: 19px; height: 19px; }
    .portal-shell .ps-rail__brand .ps-rb { min-width: 0; }
    .portal-shell .ps-rail__brand .ps-rb .ps-eyebrow { font-size: 9.5px; }
    .portal-shell .ps-rail__brand .ps-rb .ps-rn { font-size: 15px; font-weight: 650; letter-spacing: -.01em; }
    .portal-shell .ps-rail__label {
        font-size: 10px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; color: var(--ps-ink-3); padding: 14px 20px 8px;
    }
    .portal-shell .ps-rail__label.is-tight { padding-top: 6px; }
    .portal-shell .ps-rail__nav { display: flex; flex-direction: column; gap: 3px; padding: 0 10px 10px; }
    .portal-shell .ps-navitem {
        display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 10px; min-height: 42px;
        border: 1px solid transparent; color: var(--ps-ink-2); text-decoration: none; font-size: 13px; font-weight: 600;
        transition: background .13s, color .13s;
    }
    .portal-shell .ps-navitem svg { width: 17px; height: 17px; flex: none; opacity: .85; }
    .portal-shell .ps-navitem .ps-lbl { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .portal-shell .ps-navitem .ps-cnt {
        margin-left: auto; flex: none; font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
        padding: 2px 8px; border-radius: 99px; background: var(--ps-surface-2); border: 1px solid var(--ps-line); color: var(--ps-ink-2);
    }
    .portal-shell .ps-navitem .ps-cnt.is-attn { color: var(--ps-warn); background: var(--ps-warn-wash); border-color: transparent; }
    .portal-shell .ps-navitem .ps-cnt.is-ok { color: var(--ps-good); background: var(--ps-good-wash); border-color: transparent; }
    .portal-shell .ps-navitem:hover { background: var(--ps-surface-2); color: var(--ps-ink); }
    .portal-shell .ps-navitem[aria-current="page"] { background: var(--ps-primary-wash); color: var(--ps-primary); }
    .portal-shell .ps-navitem[aria-current="page"] svg { opacity: 1; }
    .portal-shell .ps-navitem.is-soon .ps-lbl { color: var(--ps-ink-faint); }
    .portal-shell .ps-rail .ps-pnav { padding: 6px 10px 2px; }
    .portal-shell .ps-rail__spacer { flex: 1; }
    .portal-shell .ps-rail__foot { border-top: 1px solid var(--ps-line); padding: 13px 16px; display: flex; flex-direction: column; gap: 11px; }
    .portal-shell .ps-synced { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--ps-ink-2); }
    .portal-shell .ps-synced .ps-ldot { width: 6px; height: 6px; border-radius: 50%; background: var(--ps-good); flex: none; }
    .portal-shell .ps-user { display: flex; align-items: center; gap: 10px; }
    .portal-shell .ps-user .ps-uav {
        width: 30px; height: 30px; border-radius: 50%; background: var(--ps-primary-wash); color: var(--ps-primary);
        display: grid; place-items: center; font-size: 11px; font-weight: 750; flex: none;
        border: 1px solid color-mix(in srgb, var(--ps-primary) 22%, transparent);
    }
    .portal-shell .ps-user .ps-un { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
    .portal-shell .ps-user .ps-un b { font-size: 12.5px; font-weight: 650; }
    .portal-shell .ps-user .ps-un span { font-size: 11px; color: var(--ps-ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* In the rail foot the padding comes from .ps-rail__foot, so the nav-list gutter is dropped. */
    .portal-shell .ps-rail__foot .ps-signout { padding: 0; }
    .portal-shell .ps-rail__foot .ps-signout__btn { padding: 8px 10px; margin-left: -10px; }

    .portal-shell .ps-main { flex: 1; min-width: 0; }
    .portal-shell .ps-topbar { padding: 18px 30px 12px; }
    .portal-shell .ps-kpis { padding: 2px 30px 14px; }
    .portal-shell #ext-menuBtn { display: none; }
    .portal-shell .ps-panes { padding: 22px 30px 60px; max-width: 1040px; width: 100%; }
    .portal-shell .ps-scrim { display: none !important; }
    .portal-shell #ext-portal { display: none !important; }
    .portal-shell .ps-grid.is-two { grid-template-columns: 1fr 1fr; }
}

@media (prefers-reduced-motion: reduce) {
    .portal-shell * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* ==========================================================================
   INTERNAL SHELL COMPONENT LAYER (FW6c, 2026-08-06)
   --------------------------------------------------------------------------
   SECOND CONSUMER of this stylesheet: Views/Shared/_InternalPortalLayout.cshtml,
   scope `.internal-shell.portal-shell.ds`. Everything above is shared chrome and
   is reused as-is; this block adds only the components the accepted INTERNAL
   design uses and the external portal never needed — the tier-3 record bar, the
   in-record section jump strip, the record identity header, the data-table-at-
   scale pattern, stat grids, the audit trail, the empty/denied states and the
   internal mobile sheet id.
   TRANSLATION, NOT INVENTION: every rule below is the corresponding rule from
   docs/prototype/internal-design-final.html's EXTENSION LAYER, re-prefixed
   `ps-` and re-pointed at the semantic --ps-* locals declared at the top of this
   file (the mock's unprefixed `.card`/`.section`/`.chip` names would collide with
   site.css, which is why every shell in this estate prefixes — `bl-` for billing,
   `ps-` here). No token is renamed, retuned or dropped.
   PARSE GUARD: PortalShellStylesheetTests asserts this file's comment and brace
   balance and that a sentinel rule from this block is present — the FW5c lesson
   (an unterminated comment silently swallowed six rules while 281 tests passed).
   ========================================================================== */

/* --- INTERNAL INK RAMP: the same four steps, retuned to clear WCAG AA ---------
   The shared ramp above was tuned for the EXTERNAL portal, where `--ps-ink-3` and
   `--ps-ink-faint` carry decoration and one-word meta. The internal estate uses
   them for staff FACTS — "Applied by {user} on {date} UTC", "Last hold lifted by
   {user}", instructor review notes, the credit-hold consequence copy — text that
   moved here off `.account-field-help`, which the harmonization layer above pins
   at `--ps-ink-2`. Shipping that copy on the untouched ramp was a measured
   REGRESSION, not an inherited condition:

     computed on the real palette (tokens.css), sRGB color-mix, both themes,
     against all three shell backgrounds (--ps-bg pane / --ps-surface card /
     --ps-surface-2), worst cell shown:

       BEFORE  --ps-ink-3     78% ... 3.58:1   (.ps-note, .ps-sectlbl, .ps-dsub)
       BEFORE  --ps-ink-faint 52% ... 2.12:1   (.ps-hint, the mobile column labels)
       control .account-field-help = --ps-ink-2 ... 5.97:1

       AFTER   --ps-ink-3     92% ... 4.92:1   AA body text
       AFTER   --ps-ink-faint 84% ... 4.09:1   >= 3:1, NON-TEXT only (SC 1.4.11)

   SCOPED TO `.internal-shell` ON PURPOSE. The external portal's ramp is an
   ACCEPTED, separately-measured visual; widening this retune to bare
   `.portal-shell` would restyle every /ExternalPortal route as a side effect of an
   internal conversion. Nothing here renames a token, so the light/dark swap happens
   in exactly one place. Pinned numerically — not as a string — by
   InternalAccountsShellIntegrationTests.InternalShellInkRamp_ClearsWcagAa_InBothThemes,
   which recomputes these ratios from tokens.css rather than trusting this comment. */
.internal-shell.portal-shell {
    --ps-ink-3: color-mix(in srgb, var(--ds-text-secondary) 92%, var(--ds-surface));
    --ps-ink-faint: color-mix(in srgb, var(--ds-text-secondary) 84%, var(--ds-surface));
}

/* The shared `.ps-rail__label` is declared ONLY inside the >=900px rail block, but
   `_InternalPortalNav` also renders it as the "More sections" divider INSIDE the
   mobile #int-portal sheet — which is where, below 900px, it had no rule at all and
   fell back to inline body text. Scoped to `.ps-sheet` so it can never fight the
   rail's own copy of the rule (which is the authority at desktop). */
.internal-shell.portal-shell .ps-sheet .ps-rail__label {
    display: block; padding: 14px 10px 6px;
    font-size: 10px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
    color: var(--ps-ink-3);
}

/* --- data table at scale: phone stacked card row -> desktop column grid ---
   One markup, two layouts. NEVER a desktop table squeezed onto a phone
   (mobile-design-standards.md rule 2). The column template is per-table and is
   passed in as an inline `--ps-cols`. */
.portal-shell .ps-dtable { display: flex; flex-direction: column; }
.portal-shell .ps-dhead { display: none; }
.portal-shell .ps-drow {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: 11px; row-gap: 6px; align-items: center;
    padding: 12px 4px; border-bottom: 1px solid var(--ps-line); border-radius: 10px;
}
.portal-shell .ps-drow:last-child { border-bottom: none; }
.portal-shell .ps-drow.is-attn { box-shadow: inset 3px 0 0 var(--ps-warn); }
.portal-shell .ps-drow.is-crit { box-shadow: inset 3px 0 0 var(--ps-crit); }
.portal-shell .ps-dc { min-width: 0; }
.portal-shell .ps-dc-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.portal-shell .ps-dt { font-size: 14px; font-weight: 650; letter-spacing: -.01em; color: var(--ps-ink); }
.portal-shell a.ps-dt { text-decoration: none; }
.portal-shell a.ps-dt:hover { color: var(--ps-primary); text-decoration: underline; }
.portal-shell .ps-dsub { font-size: 11.5px; color: var(--ps-ink-3); overflow-wrap: anywhere; }
.portal-shell .ps-dmeta { grid-column: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 5px 14px; min-width: 0; }
/* The phone-only column labels. These are the ONLY thing that says what a stacked value
   means once the desktop column head is gone, so they are text under SC 1.4.3, not chrome —
   `--ps-ink-3`, not `--ps-ink-faint`. */
.portal-shell .ps-dmeta .ps-dc[data-l]::before {
    content: attr(data-l); display: block;
    font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
    color: var(--ps-ink-3); margin-bottom: 1px;
}
.portal-shell .ps-dv { font-size: 12.5px; font-weight: 600; color: var(--ps-ink-2); font-variant-numeric: tabular-nums; }
.portal-shell .ps-dv.is-strong { color: var(--ps-ink); font-weight: 700; }
.portal-shell .ps-dv.is-pos { color: var(--ps-good); }
.portal-shell .ps-dv.is-neg { color: var(--ps-crit); }
/* The row-action glyph — the ONE deliberate `--ps-ink-faint` consumer left in this layer. It is a
   non-text UI component, so its floor is SC 1.4.11's 3:1, which the retuned step clears at 4.09:1. */
.portal-shell .ps-dgo {
    color: var(--ps-ink-faint); display: grid; place-items: center;
    width: 44px; height: 44px; text-decoration: none; flex: none;
    transition: transform .16s var(--ps-ease), color .16s;
}
.portal-shell .ps-dgo:hover { color: var(--ps-primary); transform: translateX(2px); }
.portal-shell .ps-dgo svg { width: 17px; height: 17px; }
.portal-shell .ps-drailtail { width: 4px; border-radius: 4px; align-self: stretch; min-height: 34px; flex: none; background: var(--ps-line-strong); }
.portal-shell .ps-hl { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ps-ink-3); }

/* --- generic module sections + cards --- */
.portal-shell .ps-section { display: flex; flex-direction: column; gap: 14px; padding: 16px 0; }
/* The mock renders a section label as a styled div. It ships here as a real <h2> carrying the same
   class, because these are <section> landmarks and an unnamed region is worse than no region — the
   class carries the whole visual treatment either way, so nothing about the design changes. */
.portal-shell .ps-sectlbl { margin: 0; font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ps-ink-3); }
/* `.ps-note` is where `.account-field-help` body copy landed — the credit-hold consequence
   paragraph, the document-slot guidance, the owner-claim explanations. It takes `--ps-ink-2`,
   the SAME token the harmonization layer pins `.account-field-help` to, so the conversion is
   colour-for-colour rather than a step down. `.ps-hint` keeps a softer step, but on the retuned
   `--ps-ink-3` (4.92:1 worst cell), never on `--ps-ink-faint`, because it carries audit facts
   ("Applied by {user} on {date} UTC"), not decoration. */
.portal-shell .ps-note { font-size: 12.5px; color: var(--ps-ink-2); line-height: 1.5; }
.portal-shell .ps-hint { font-size: 11.5px; color: var(--ps-ink-3); line-height: 1.5; }
.portal-shell .ps-stackcard {
    background: var(--ps-surface); border: 1px solid var(--ps-line); border-radius: var(--ps-radius);
    padding: 14px; display: flex; flex-direction: column; gap: 10px;
}
.portal-shell .ps-stackcard > h2, .portal-shell .ps-stackcard > h3 { margin: 0; font-size: 15px; font-weight: 660; letter-spacing: -.01em; color: var(--ps-ink); }
.portal-shell .ps-actrow { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.portal-shell .ps-mbtn {
    font-size: 13px; font-weight: 620; padding: 10px 14px; border-radius: 9px; text-decoration: none;
    border: 1px solid var(--ps-line-strong); background: var(--ps-surface); color: var(--ps-ink);
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    min-height: var(--ds-touch-min); min-width: var(--ds-touch-min);
    transition: background .15s, border-color .15s;
}
.portal-shell .ps-mbtn:hover { background: var(--ps-surface-2); color: var(--ps-ink); }
.portal-shell .ps-mbtn.is-solid { background: var(--ps-primary); color: var(--ps-primary-on); border-color: transparent; }
.portal-shell .ps-mbtn.is-solid:hover { background: var(--ps-primary); color: var(--ps-primary-on); filter: brightness(1.06); }

/* --- summary stat grid --- */
.portal-shell .ps-statgrid {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px;
    background: var(--ps-line); border: 1px solid var(--ps-line); border-radius: 12px; overflow: hidden;
}
.portal-shell .ps-statcell { background: var(--ps-surface); padding: 13px 14px; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.portal-shell .ps-statcell .ps-sl { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ps-ink-3); }
.portal-shell .ps-statcell .ps-sv { font-size: 20px; font-weight: 700; letter-spacing: -.02em; line-height: 1.15; font-variant-numeric: tabular-nums; color: var(--ps-ink); }
.portal-shell .ps-statcell.is-warn .ps-sv { color: var(--ps-warn); }
.portal-shell .ps-statcell.is-crit .ps-sv { color: var(--ps-crit); }
.portal-shell .ps-statcell.is-ok .ps-sv { color: var(--ps-good); }

/* --- tier-3 record context bar + in-record section jumps + identity header --- */
.portal-shell .ps-crumbrow {
    display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
    padding: 10px 0; margin-bottom: 4px;
}
.portal-shell .ps-upbtn {
    display: inline-flex; align-items: center; gap: 6px; text-decoration: none; flex: none;
    font-size: 13px; font-weight: 650; color: var(--ps-primary);
    padding: 8px 12px; border-radius: 9px; background: var(--ps-primary-wash); min-height: 44px;
}
.portal-shell .ps-upbtn:hover { color: var(--ps-primary); filter: brightness(1.04); }
.portal-shell .ps-upbtn svg { width: 15px; height: 15px; }
.portal-shell .ps-crumbrow .ps-ctx { font-size: 11.5px; color: var(--ps-ink-2); min-width: 0; }
.portal-shell .ps-secnav { display: flex; gap: 7px; overflow-x: auto; scrollbar-width: none; padding: 4px 0 12px; }
.portal-shell .ps-secnav::-webkit-scrollbar { display: none; }
.portal-shell .ps-secchip {
    flex: none; text-decoration: none; font-size: 12.5px; font-weight: 650;
    padding: 10px 14px; border-radius: 999px; color: var(--ps-ink-2); min-height: 44px;
    display: inline-flex; align-items: center;
    background: var(--ps-surface); border: 1px solid var(--ps-line);
}
.portal-shell .ps-secchip:hover { border-color: var(--ps-line-strong); color: var(--ps-ink); }
.portal-shell .ps-recsec { scroll-margin-top: 96px; }
.portal-shell .ps-rechead { display: flex; align-items: flex-start; gap: 13px; padding: 4px 0 12px; }
.portal-shell .ps-ravatar {
    width: 46px; height: 46px; border-radius: 13px; flex: none; display: grid; place-items: center;
    font-size: 15px; font-weight: 750; background: var(--ps-primary-wash); color: var(--ps-primary);
}
.portal-shell .ps-rid { flex: 1; min-width: 0; }
.portal-shell .ps-rid h2 { margin: 0; font-size: 19px; font-weight: 670; letter-spacing: -.015em; color: var(--ps-ink); }
.portal-shell .ps-rsub { font-size: 12.5px; color: var(--ps-ink-3); margin-top: 2px; overflow-wrap: anywhere; }

/* --- audit / history trail --- */
.portal-shell .ps-trail { display: flex; flex-direction: column; }
.portal-shell .ps-trailitem { display: flex; gap: 11px; padding: 9px 0; border-bottom: 1px solid var(--ps-line); }
.portal-shell .ps-trailitem:last-child { border-bottom: none; }
/* Audit-trail timestamps are the evidence half of an audit row — text, so `--ps-ink-3`. */
.portal-shell .ps-trailitem .ps-tw { font-size: 11px; color: var(--ps-ink-3); font-variant-numeric: tabular-nums; flex: none; width: 84px; padding-top: 1px; }
.portal-shell .ps-trailitem .ps-tb { font-size: 12.5px; color: var(--ps-ink-2); line-height: 1.45; min-width: 0; }

/* --- empty / all-clear / permission-denied states ---
   `.ps-empty` and `.ps-empty.is-good` are DIFFERENT FACTS: filtered-to-nothing
   versus a queue that is empty because everything is done. */
.portal-shell .ps-empty {
    padding: 24px 16px; text-align: center; color: var(--ps-ink-3); font-size: 12.5px;
    border: 1.5px dashed var(--ps-line-strong); border-radius: 12px;
}
.portal-shell .ps-empty strong { color: var(--ps-ink-2); display: block; font-size: 13.5px; font-weight: 650; margin-bottom: 4px; }
.portal-shell .ps-empty.is-good { border-color: color-mix(in srgb, var(--ps-good) 32%, transparent); color: var(--ps-good); }
.portal-shell .ps-empty.is-good strong { color: var(--ps-good); }
.portal-shell .ps-denied {
    padding: 18px; border-radius: 13px; display: flex; gap: 13px; align-items: flex-start;
    background: var(--ps-surface); border: 1px solid var(--ps-line);
}
.portal-shell .ps-denied .ps-dicon {
    width: 36px; height: 36px; border-radius: 10px; flex: none; display: grid; place-items: center;
    background: var(--ps-warn-wash); color: var(--ps-warn);
}
.portal-shell .ps-denied h3 { margin: 0 0 4px; font-size: 14px; font-weight: 660; color: var(--ps-ink); }
.portal-shell .ps-denied p { margin: 0; font-size: 12.5px; color: var(--ps-ink-2); line-height: 1.5; }

/* --- pill (state, never accent) --- */
.portal-shell .ps-pill {
    display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 700;
    padding: 4px 9px; border-radius: 999px; flex: none;
    background: var(--ps-surface-2); color: var(--ps-ink-2);
}
.portal-shell .ps-pill.is-ok { background: var(--ps-good-wash); color: var(--ps-good); }
.portal-shell .ps-pill.is-warn { background: var(--ps-warn-wash); color: var(--ps-warn); }
.portal-shell .ps-pill.is-crit { background: var(--ps-crit-wash); color: var(--ps-crit); }
.portal-shell .ps-pill.is-info { background: var(--ps-info-wash); color: var(--ps-info); }

/* --- the internal mobile portal sheet (mirrors the #ext-portal rules above) --- */
.portal-shell #int-portal.ps-sheet {
    position: fixed; z-index: 50; left: 0; right: 0; bottom: 0; max-height: 90vh;
    border-radius: 20px 20px 0 0; box-shadow: var(--ps-shadow-sheet);
    transform: translateY(100%); visibility: hidden;
    transition: transform .34s var(--ps-ease), visibility 0s linear .34s;
    will-change: transform;
}
.portal-shell #int-portal.ps-sheet.is-open,
.portal-shell #int-portal.ps-sheet:target {
    transform: translateY(0); visibility: visible;
    transition: transform .34s var(--ps-ease), visibility 0s;
}
body:has(.portal-shell #int-portal:target) .portal-shell .ps-scrim { opacity: 1; visibility: visible; }
.portal-shell #int-portal .ps-sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 6px 2px calc(20px + env(safe-area-inset-bottom, 0px)); }

/* --- LEGACY CONTENT HARMONIZATION, internal additions ---
   docs/AREAS/mobile-design-standards.md §8 RULE: before re-parenting any
   site.css-classed view into a themed shell, measure it in both themes. These
   are the classes the converted /Portal/Accounts views still carry that the
   external-side harmonization above did not reach. The owner-portal mirror's
   fold family is the important one: its markup is pinned byte-for-byte by
   AccountFlowIntegrationTests.OwnerPortalCollapsedSections (the FW5c
   collapsed-section contract), so the fold CANNOT be re-classed — the colours
   are re-pointed instead, which is exactly what a harmonization layer is for. */
.portal-shell .ps-panes .account-section-fold {
    background: var(--ps-surface); border-color: var(--ps-line);
}
.portal-shell .ps-panes .account-section-fold > summary { color: var(--ps-ink); }
.portal-shell .ps-panes .account-section-fold > summary:focus-visible { outline-color: var(--ps-primary); }
.portal-shell .ps-panes .account-section-fold-body { border-top-color: var(--ps-line); }
.portal-shell .ps-panes .account-section-chip {
    background: var(--ps-surface-2); color: var(--ps-ink-2); border-color: var(--ps-line);
}
.portal-shell .ps-panes .account-fold-chev { color: var(--ps-ink-2); }
.portal-shell .ps-panes .account-list-scroll { border-color: var(--ps-line); }
.portal-shell .ps-panes .account-field-value { color: var(--ps-ink); }
.portal-shell .ps-panes .contact-alert { border: 1px solid var(--ps-line); background: var(--ps-surface-2); color: var(--ps-ink); }
.portal-shell .ps-panes .contact-alert-success { background: var(--ps-good-wash); color: var(--ps-good); border-color: color-mix(in srgb, var(--ps-good) 26%, transparent); }
.portal-shell .ps-panes .contact-alert-error { background: var(--ps-crit-wash); color: var(--ps-crit); border-color: color-mix(in srgb, var(--ps-crit) 26%, transparent); }
.portal-shell .ps-panes .contact-alert-info { background: var(--ps-info-wash); color: var(--ps-info); border-color: color-mix(in srgb, var(--ps-info) 26%, transparent); }
.portal-shell .ps-panes .currency-badge { background: var(--ps-surface-2); color: var(--ps-ink-2); border-color: var(--ps-line); }
.portal-shell .ps-panes .currency-badge-current { background: var(--ps-good-wash); color: var(--ps-good); border-color: transparent; }
.portal-shell .ps-panes .currency-badge-duesoon { background: var(--ps-warn-wash); color: var(--ps-warn); border-color: transparent; }
.portal-shell .ps-panes .currency-badge-expired { background: var(--ps-crit-wash); color: var(--ps-crit); border-color: transparent; }

@media (min-width: 900px) {
    /* The internal sheet is mobile-only chrome; on desktop the rail is the navigation.
       The billing shell shipped without this rule and a desktop viewport landing on the
       sheet's hash painted it over the rail (mobile-design-standards.md §1). */
    .portal-shell #int-portal { display: none !important; }
    .portal-shell #int-menuBtn { display: none; }
    .portal-shell .ps-dtable { --ps-cols: 1fr; }
    .portal-shell .ps-dhead {
        display: grid; grid-template-columns: var(--ps-cols); column-gap: 14px; align-items: center;
        padding: 9px 4px; border-bottom: 1.5px solid var(--ps-line-strong);
    }
    .portal-shell .ps-drow { grid-template-columns: var(--ps-cols); column-gap: 14px; row-gap: 0; padding: 11px 4px; }
    .portal-shell .ps-dmeta { display: contents; }
    .portal-shell .ps-dmeta .ps-dc[data-l]::before { display: none; }
    .portal-shell .ps-statgrid.is-three { grid-template-columns: repeat(3, 1fr); }
    .portal-shell .ps-statgrid.is-four { grid-template-columns: repeat(4, 1fr); }
    .portal-shell .ps-section { padding: 18px 0; }
}

/* SENTINEL — the parse guard asserts this exact rule renders. If an unterminated
   comment or an unbalanced brace ever swallows the block above, this disappears
   with it and the guard fails instead of the estate silently losing its styling. */
.portal-shell .ps-internal-sentinel { color: var(--ps-ink); }
