/* rp-responsive.css — RESPONSIVE-01 PHASE 1: THE SHARED RESPONSIVE LAYER (Michael, 2026-10-15 19:46; Terminal A owns the shared layout system).
 * ONE sheet, loaded LAST by all four shells (admin/_layout.php · corp-portal/_layout.php · platform/_layout.php · portal/_layout.php); its behaviour half is
 * assets/js/rp-shell.js. Nothing here repaints a desktop page: every rule is inside a breakpoint, so ≥ 1024 is unchanged except the opt-in utilities.
 *
 * RULE OF RECORD (docs/UI_STANDARDS.md §Responsive): every page renders correctly at 390 (phone) · 768 (tablet) · 1024 · 1366 · 1920 — no horizontal page scroll,
 * no clipped text, no overlapping controls, 44-px minimum touch targets, 16-px side gutters on phones, scroll-to-top on navigation (UI-SCROLL-01).
 *
 * THE BREAKPOINT SET (the only five; a media query anywhere else needs a reason in its comment):
 *     phone   ≤ 767.98   cards stack · tables become cards · modals are full-screen sheets · 16-px gutters · 44-px targets
 *     tablet  ≤ 1023.98  the sidebar is an off-canvas drawer behind a hamburger (the same drawer as the phone) · two-column grids stay
 *     laptop  ≥ 1024     the docked sidebar (220 px, collapsible to 56) · 1366 and 1920 only widen the content
 * (JS reads the same numbers: rp-shell.js MQ_PHONE / MQ_DRAWER.)
 *
 * ★ Every selector below is written to LOSE to nothing it should not: geometry guarantees (the drawer, the gutters, the 44-px floor, the sheet) use the
 *   `html body` prefix (specificity 0,0,2+) so they beat the per-plane sheets (admin.css · platform.css · resident-theme.css) without !important; the few
 *   !important are the ones that must beat an INLINE style (grid/flex rows authored inline).
 */
:root {
  --rp-gutter: 16px;              /* the side gutter on a phone */
  --rp-tap: 44px;                 /* the minimum touch target */
  --rp-topbar-h: 52px;
  --rp-drawer-w: min(86vw, 300px);
  --rp-sheet-bg: #fff;
  --rp-fade: 18px;                /* the edge fade of a scrolling pill band */
}
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
img, video, canvas, iframe, svg { max-width: 100%; }
select, textarea, input:not([type=checkbox]):not([type=radio]) { max-width: 100%; }   /* a <select> is as wide as its longest option unless told otherwise */
img, video, canvas { height: auto; }
/* long unbroken strings (an e-mail, a URL, a serial) wrap instead of pushing the page sideways */
p, li, dd, dt, label, summary, .card-sh h2, .card-sh h3, .rpm-body { overflow-wrap: anywhere; }
td, th { overflow-wrap: break-word; }   /* NOT anywhere: that shrinks an auto-layout column's minimum to one character and wraps an amount letter by letter */
pre, code, kbd, samp { overflow-wrap: anywhere; word-break: break-word; }
pre { white-space: pre-wrap; }
.rp-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
.rp-trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; max-width: 100%; }
.rp-only-phone { display: none !important; }
@media (max-width: 767.98px) { .rp-only-phone { display: revert !important; } .rp-hide-phone { display: none !important; } }
@media (min-width: 768px) { .rp-only-tablet-down { display: none !important; } }
html.rp-noscroll, html.rp-noscroll body { overflow: hidden; }

/* ═══ 1. THE SHELL — drawer below 1024, the docked sidebar from 1024 ═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1023.98px) {
  html body .sidebar { width: var(--rp-drawer-w); transform: translateX(-102%); transition: transform .22s ease, visibility 0s linear .22s; visibility: hidden;
    box-shadow: 4px 0 40px rgba(0,0,0,.35); z-index: 400; overscroll-behavior: contain; }   /* above the topbar (100), the modal layer (320) and the Co-Pilot widget (200); below the open panel (9600) */
  html body.sb-open .sidebar { transform: none; visibility: visible; transition: transform .22s ease, visibility 0s; }
  html body.sb-open .sb-backdrop { opacity: 1; visibility: visible; }
  html body .sb-backdrop { z-index: 390; }
  html body .main { margin-left: 0; }
  html body .topbar { left: 0; }
  html body.sb-collapsed .sidebar { width: var(--rp-drawer-w); }
  html body.sb-collapsed .sidebar .nav-label { display: initial; }
  html body.sb-collapsed .main { margin-left: 0; }
  html body .topbar-context { display: none; }
  html body .topbar-hamburger { min-width: var(--rp-tap); min-height: var(--rp-tap); display: inline-flex; align-items: center; justify-content: center; padding: 0; margin-left: -8px; }
  html body .sidebar a, html body .sidebar .sb-group-toggle { min-height: var(--rp-tap); display: flex; align-items: center; }
}
.rp-search-btn { display: none; }   /* shown on a phone only (§2); from 768 the search form is the topbar's own */

/* ═══ 1b. COMPACT DESKTOP (≤ 1279.98) — the docked sidebar leaves ~740 px at 1024 and ~1000 at 1280: a page-local two-up grid stacks here, not at 900 ═════════════════════ */
@media (max-width: 1279.98px) {
  html body .plat-grid, html body .pp-grid, html body .promo-grid, html body .backup-grid, html body .wiz-layout, html body .reg-layout:not(.rr-shell) { grid-template-columns: minmax(0, 1fr); }
}

/* ═══ 1c. THE TOP BAR AT 1024-1279 — the docked sidebar leaves 804 px: the long account line goes (as it does below 1024) and the search narrows, so Co-Pilot and Logout stay on screen ═════ */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  html body .topbar-context { display: none; }
  html body .topbar-search { max-width: 320px; }
}

/* ═══ 2. THE TOP BAR ON A PHONE — search collapses to an icon, the bar never wraps ═════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
  html body .topbar { padding: 0 8px 0 8px; gap: 4px; height: var(--rp-topbar-h); }
  html body .topbar-search { display: none; }
  .rp-search-btn { display: inline-flex; align-items: center; justify-content: center; min-width: var(--rp-tap); min-height: var(--rp-tap); border: 0; background: none; color: var(--muted, #64748b); cursor: pointer; border-radius: 8px; }
  .rp-search-btn:hover, .rp-search-btn:focus-visible { color: var(--text, #0f172a); background: var(--surface-2, #f1f5f9); }
  html body.rp-search-open .topbar-search { display: flex; position: fixed; z-index: calc(var(--z-sticky-header, 100) + 30); left: 0; right: 0; top: 0; height: var(--rp-topbar-h); max-width: none; border-radius: 0; margin: 0;
    padding: 0 8px 0 14px; background: #fff; border: 0; border-bottom: 1px solid var(--border, #e2e8f0); }
  html body.rp-search-open .topbar-search input { font-size: 16px; min-height: var(--rp-tap); }
  html body.rp-search-open .topbar-search-kbd { display: none; }
  html body.rp-search-open .topbar-search-results { position: fixed; left: 0; right: 0; top: var(--rp-topbar-h); max-height: calc(100dvh - var(--rp-topbar-h)); border-radius: 0; }
  html body .topbar-right { gap: 2px; min-width: 0; }
  html body .topbar-right > * { flex-shrink: 0; }
  /* the Co-Pilot button lives on as the bottom-right widget (§7); the labelled button in the bar is an icon only */
  html body .btn-topbar-help, html body .btn-topbar-logout { min-width: var(--rp-tap); min-height: var(--rp-tap); padding: 0; justify-content: center; font-size: 0; gap: 0; }
  html body .btn-topbar-help svg, html body .btn-topbar-logout svg { width: 18px; height: 18px; }
  html body .topbar-avatar { flex-shrink: 0; }
}

/* ═══ 3. CONTENT ON A PHONE — 16-px gutters, one column, no sideways push ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
  html body .main, html body .portal-main { padding: 16px var(--rp-gutter) calc(24px + 76px + env(safe-area-inset-bottom)); margin-left: 0; max-width: 100vw; min-width: 0; }   /* the bottom padding lets the last row scroll clear of the Co-Pilot widget (52 px + 12) */
  html body .main > *, html body .portal-main > * { max-width: 100%; }
  html body .card, html body .card-sh { margin-left: 0; margin-right: 0; max-width: 100%; }
  html body .main h1, html body .portal-main h1 { font-size: clamp(20px, 6vw, 26px); line-height: 1.2; overflow-wrap: anywhere; }   /* a page title in the app shells; a marketing hero keeps its own size */
  html body h1 { overflow-wrap: anywhere; }
  html body h2 { overflow-wrap: anywhere; }
  /* grids and flex rows authored inline collapse to one column */
  html body main [style*="grid-template-columns"], html body .main [style*="grid-template-columns"] { grid-template-columns: minmax(0, 1fr) !important; }
  html body main [style*="grid-template-columns"] > *, html body .main [style*="grid-template-columns"] > * { min-width: 0; }   /* a grid item never forces its track wider than the screen */
  html body main [style*="display:flex"][style*="gap"]:not(.rp-keep-row):not([role=tablist]), html body .main [style*="display: flex"][style*="gap"]:not(.rp-keep-row):not([role=tablist]) { flex-wrap: wrap; }
  /* the named multi-column containers of the platform (cards stack below 768; stat tiles go two-up) */
  html body .grid-2, html body .grid-3, html body .grid-4, html body .col-2, html body .cols-2, html body .two-col, html body .detail-grid, html body .sp-grid,
  html body .vr-grid, html body .vr-cols, html body .cs-settings-cols, html body .wz-site-grid, html body .wz-dates-grid, html body .rrnav-grid, html body .rr-cform-perms-grid { grid-template-columns: minmax(0, 1fr); }
  html body .stats-grid, html body .kpi-grid, html body .stat-row, html body .stats, html body .kpi-row, html body .tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  html body .stat-card, html body .kpi-card, html body .stat { min-width: 0; }
  /* a card header is a wrapping row: the title, the starter button and the actions fold under each other instead of pushing the page */
  html body .card-header, html body .card-sh, html body .card-hd { flex-wrap: wrap; gap: 8px; min-width: 0; }
  html body .card-header > *, html body .card-sh > *, html body .card-hd > * { min-width: 0; max-width: 100%; }
  /* the phone safety net: rows of actions wrap, a badge never out-grows the screen, an <header> inside a card is a wrapping row */
  html body .ph-right, html body .ph-actions, html body .page-actions, html body .actions, html body .btn-row, html body .btn-group, html body .toolbar, html body .vo-hd-act, html body .vi-row { flex-wrap: wrap; min-width: 0; max-width: 100%; }
  html body main header, html body .main header { flex-wrap: wrap; gap: 8px; min-width: 0; }
  html body .v-badge, html body .badge, html body .pill, html body .chip, html body .tag, html body .rp-badge { max-width: 100% !important; white-space: normal !important; overflow-wrap: anywhere; height: auto !important; min-height: var(--v-badge-h, 22px); line-height: 1.3; }   /* a badge that may wrap keeps no fixed pill height */
  html body .btn, html body a.btn, html body button.btn, html body .v-btn, html body .rp-btn { white-space: normal !important; height: auto !important; max-width: 100%; text-align: center; }   /* a long button label wraps; the button grows (min 44 px) */
  html body .starter-btn { max-width: 100%; flex-wrap: wrap; white-space: normal; }
  html body .starter-btn__pill { white-space: normal; }
  html body .kpi-val, html body .stat-val, html body .stat-value, html body .stat-num, html body .big-num { font-size: clamp(22px, 7.4vw, 30px); white-space: nowrap; overflow-wrap: normal; font-variant-numeric: tabular-nums; }
  /* forms: one column, labels above, 16-px text (iOS never zooms a 16-px field), the 44-px floor */
  html body input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=range]):not([type=file]):not([type=color]), html body select, html body textarea { font-size: 16px; }
  html body .form-row, html body .fld-pair, html body .fld-grid, html body .form-grid, html body .field-row, html body .frm-row, html body .row-2, html body .two-up { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }   /* minmax(0, …): a plain 1fr column cannot shrink below its widest item's min-content — Bot & Abuse Protection's key rows (an input + a Show button in a flex row) forced a 449-px column and 96 px of sideways scroll at 390 (D's finding 2026-10-21; measured, the topbar was only the victim) */
  html body form label { display: block; max-width: 100%; }
  html body .input-dollar { position: relative; }
  html body .input-dollar > input, html body .input-dollar .form-control { padding-left: 28px; }   /* the 16-px phone digits clear the $ prefix */
  html body .input-dollar-prefix { font-size: 16px; top: 50%; transform: translateY(-50%); }
  html body form > div, html body form > p, html body form > fieldset { max-width: 100%; min-width: 0; }   /* a flex-wrapped filter row: each field is at most the screen wide (a <select> sizes to its longest option) */
  html body .field-help, html body .help, html body .hint, html body .fld-help { white-space: normal; overflow-wrap: anywhere; }
  /* Save bars stick to the bottom (the opt-in class and the platform's own form-actions rows) */
  html body .rp-savebar, html body form > .form-actions:last-child, html body form > .card-ft:last-child { position: sticky; bottom: 0; z-index: 30; background: var(--rp-sheet-bg); border-top: 1px solid var(--border, #e2e8f0);
    padding: 10px var(--rp-gutter); margin-left: calc(-1 * var(--rp-gutter)); margin-right: calc(-1 * var(--rp-gutter)); display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
  /* breadcrumbs: first and last stay, the middle collapses to an ellipsis */
  html body nav.breadcrumb, html body .breadcrumb { min-width: 0; max-width: 100%; overflow: hidden; }
  html body nav.breadcrumb ol, html body nav.breadcrumb ul, html body .breadcrumb ol { display: flex; flex-wrap: nowrap; min-width: 0; align-items: center; }
  html body nav.breadcrumb li:not(:first-child):not(:last-child), html body .breadcrumb li:not(:first-child):not(:last-child) { display: none; }
  html body nav.breadcrumb li:first-child::after, html body .breadcrumb li:first-child::after { content: " … ›"; opacity: .6; margin-left: 4px; }
  html body nav.breadcrumb li:last-child, html body .breadcrumb li:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* pagers fit one line: Prev · Page 1 of 6 · Next (the numbered buttons and the gaps rest; the range and the page-size select go on a second line) */
  html body .rp-pager { flex-wrap: wrap; gap: 8px 12px; }
  html body .rp-pager .rp-pager-btns { order: 1; flex: 1 1 100%; display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center; gap: 8px; }
  html body .rp-pager .rp-pg-n, html body .rp-pager .rp-pager-gap { display: none !important; }
  html body .rp-pager .rp-pg-of { display: inline-block; font-weight: 700; font-size: 13px; color: var(--text, #0f172a); white-space: nowrap; }
  html body .rp-pager .rp-pager-range { order: 2; flex: 1 1 auto; }
  html body .rp-pager .rp-pager-per { order: 3; }
  html body .rp-pager .rp-pg-prev, html body .rp-pager .rp-pg-next { min-width: 88px; justify-content: center; }
}

/* ═══ 3b. THE 44-PX FLOOR — touch targets on a phone AND a tablet (≤ 1023.98): everything pressable, every form control, every label that wraps a check or a radio ══════════ */
/* the page-family sheets that size controls from a token get the 44-px floor through the token (VoIP admin: --v-btn-h) */
@media (max-width: 1023.98px) { :root, html body .v-ui, html body.v-ui { --v-btn-h: var(--rp-tap); } }
@media (max-width: 1023.98px) {
  html body input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=range]), html body select { min-height: var(--rp-tap); }
  html body input[type=color] { min-width: var(--rp-tap); padding: 2px; }
  html body [role=button] { min-width: var(--rp-tap); }
  html body label:has(input[type=checkbox]), html body label:has(input[type=radio]) { display: flex; align-items: center; gap: 10px; min-height: var(--rp-tap); min-width: var(--rp-tap); }
  html body input[type=checkbox], html body input[type=radio] { width: 20px; height: 20px; flex: 0 0 auto; }
  /* the 44-px floor for everything pressable */
  html body button, html body [role=button], html body .btn, html body .rp-btn, html body a.btn, html body a.rp-btn, html body .btn-sm, html body input[type=submit], html body input[type=button] { min-height: var(--rp-tap); }
  html body button:not(.rpm-x):not(.rp-rowmenu-btn):not(.topbar-hamburger):not(.rp-search-btn), html body .btn, html body .rp-btn, html body a.btn, html body a.rp-btn { min-width: var(--rp-tap); }
  html body li > a:only-child, html body .links > a, html body .link-list > a, html body nav a, html body [role=tab], html body .stnav-r1 a, html body .stnav-r2 a, html body .stnav-r3 a { min-height: var(--rp-tap); min-width: var(--rp-tap); display: inline-flex; align-items: center; }
  html body summary { min-height: var(--rp-tap); min-width: var(--rp-tap); display: list-item; padding-block: 10px; box-sizing: border-box; }   /* list-item keeps the disclosure triangle (display:flex hides it) */
  /* every button-like control, whatever its page-local class (.hm-btn, .ac-btn, .xp-btn, .pm-btn …) — a text link that is only named *-link stays inline */
  html body a[class*="btn"]:not([class*="link"]):not([class*="text"]), html body button[class*="btn"]:not([class*="link"]):not(.rp-rowmenu-btn):not(.rpm-x):not(.topbar-hamburger):not(.rp-search-btn) { min-height: var(--rp-tap); box-sizing: border-box; }   /* no display override: a button with mixed inline content (A · . · text · (recommended)) must stay ONE run, never a row of flex items */
  html body table[data-rp-cards] td a:not([class*="btn"]) { min-height: var(--rp-tap); display: inline-flex; align-items: center; }
  /* pills, chips and tabs that are links or buttons (the page-local families: .cs-tab, .mig-chip, .vc-pill, the auto bands) */
  html body button.rp-badge, html body button[class*="badge"] { min-height: var(--rp-tap); min-width: var(--rp-tap); }   /* a status badge that is a toggle button */
  html body a[class*="tab"], html body a[class*="chip"], html body a[class*="pill"], html body button[class*="tab"]:not([role=tab]), html body button[class*="chip"], html body button[class*="pill"], html body .rp-pillband > a, html body .rp-pillband > button, html body .rp-pillband > label { min-height: var(--rp-tap); min-width: var(--rp-tap); display: inline-flex; align-items: center; box-sizing: border-box; }
  html body .rp-tapnode { display: inline-flex; align-items: center; min-height: var(--rp-tap); min-width: var(--rp-tap); }
  html body .rp-tapbox { display: inline-flex; align-items: center; justify-content: center; min-width: var(--rp-tap); min-height: var(--rp-tap); }
  html body #rp-copilot .rp-cp-msg a, html body #rp-copilot .rp-cp-msg button { min-height: var(--rp-tap); display: inline-flex; align-items: center; }   /* the quick-link chips inside the panel */
}

@media (max-width: 1023.98px) { .rp-hit { position: relative; } .rp-hit::after { content: ""; position: absolute; top: calc(-1 * var(--rp-hit-y, 14px)); bottom: calc(-1 * var(--rp-hit-y, 14px)); left: calc(-1 * var(--rp-hit-x, 8px)); right: calc(-1 * var(--rp-hit-x, 8px)); } }   /* an invisible 44-px hit area for a small stand-alone link (rp-shell.js adds it; a template may add the class by hand) */

/* the phone-only 'Page 1 of 6' of the shared pager is hidden everywhere else */
.rp-pg-of { display: none; }

/* ═══ 4. PILL BANDS AND TAB ROWS — one scrolling row with an edge fade; a pill never shrinks and never wraps (NAV-03) ═════════════════════════════════════ */
.rp-pillband, .stnav-r1, .stnav-r2, .stnav-r3, [data-rp-pills] { display: flex; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; scrollbar-width: none; overscroll-behavior-x: contain; }
@media (max-width: 1023.98px) { .rp-pillband.rp-autoband { contain: inline-size; max-width: 100%; flex: 1 1 auto; min-width: 0; display: flex !important; grid-template-columns: none !important; flex-wrap: nowrap !important; overflow-x: auto !important; justify-content: flex-start !important; } .rp-pillband.rp-autoband > * { flex: 0 0 auto !important; white-space: nowrap; width: auto !important; } }
/* an ACTION row that must not band (.rp-stack, opt-in; NETCTL-UI 2026-10-17): on a phone one button per line, full width, so no action hides off the right edge; rp-shell.js never makes it a band */
@media (max-width: 767.98px) { html body .rp-stack { display: flex; flex-direction: column; align-items: stretch; flex-wrap: nowrap; } html body .rp-stack > :empty { display: none; } html body .rp-stack > .btn, html body .rp-stack > button { width: 100%; justify-content: center; } }
/* ★ UI-POLISH-01 (2026-10-17, D; audited by A): the ONE inline result mark — rp-autosave.js ("✓ Saved 21:26" / "✕ Not saved — …") and rp-inplace.js
   (a press's result). Opt-in by markup ([data-rp-saved]); at every width. It reserves its line from the moment it is placed, so a save never moves the
   page; it wraps inside its control's row (never wider than it at 390); the bad state is text + ✕, never colour alone; tokens only. */
html body .rp-saved { display: block; min-height: 1.35em; margin-top: 2px; font-size: 12px; line-height: 1.35; font-weight: 500; color: var(--success, #15803d); overflow-wrap: anywhere; white-space: normal; max-width: 100%; }
html body .rp-saved:empty::before { content: '\00a0'; }
html body .rp-saved.rp-saved--busy { color: var(--muted, #6b7280); }
html body .rp-saved.rp-saved--bad { color: var(--danger, #b91c1c); }
html body button + .rp-saved, html body .btn + .rp-saved { display: inline-block; vertical-align: middle; margin: 0 0 0 8px; min-width: 9em; }
html body [data-rp-page-scope], html body [data-netctl-gw], html body [data-netctl-park-ops] { overflow-anchor: none; }   /* a node replaced in place is never the browser's scroll anchor (UI-POLISH-01: no jump) */
html body [aria-busy="true"][data-rp-page-scope], html body [aria-busy="true"][id] > .rp-pager { opacity: .6; transition: opacity .15s; }
.rp-pillband::-webkit-scrollbar, .stnav-r1::-webkit-scrollbar, .stnav-r2::-webkit-scrollbar, .stnav-r3::-webkit-scrollbar, [data-rp-pills]::-webkit-scrollbar { display: none; }
.rp-pillband > *, .stnav-r1 > *, .stnav-r2 > *, .stnav-r3 > *, [data-rp-pills] > * { flex: 0 0 auto; white-space: nowrap; }
.rp-pillband.rp-fade-r, .stnav-r1.rp-fade-r, .stnav-r2.rp-fade-r, .stnav-r3.rp-fade-r, [data-rp-pills].rp-fade-r { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--rp-fade)), transparent); mask-image: linear-gradient(to right, #000 calc(100% - var(--rp-fade)), transparent); }
.rp-pillband.rp-fade-l, .stnav-r1.rp-fade-l, .stnav-r2.rp-fade-l, .stnav-r3.rp-fade-l, [data-rp-pills].rp-fade-l { -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--rp-fade)); mask-image: linear-gradient(to right, transparent, #000 var(--rp-fade)); }
.rp-pillband.rp-fade-l.rp-fade-r, .stnav-r1.rp-fade-l.rp-fade-r, .stnav-r2.rp-fade-l.rp-fade-r, .stnav-r3.rp-fade-l.rp-fade-r, [data-rp-pills].rp-fade-l.rp-fade-r { -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--rp-fade), #000 calc(100% - var(--rp-fade)), transparent); mask-image: linear-gradient(to right, transparent, #000 var(--rp-fade), #000 calc(100% - var(--rp-fade)), transparent); }

/* ═══ 5. TABLES — below 768 each row becomes a card (label · value pairs from <th>); wider tables scroll inside their card with a sticky first column ═══════ */
@media (max-width: 767.98px) {
  table[data-rp-cards], table[data-rp-cards] tbody, table[data-rp-cards] tfoot, table[data-rp-cards] tr, table[data-rp-cards] td { display: block; width: 100%; max-width: 100%; }
  table[data-rp-cards] { border: 0; background: none; }
  table[data-rp-cards] thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }   /* kept for assistive tech */
  table[data-rp-cards] tr { border: 1px solid var(--border, #e2e8f0); border-radius: 12px; background: var(--card-bg, #fff); margin: 0 0 10px; padding: 6px 12px; box-shadow: 0 1px 2px rgba(15,23,42,.04); position: relative; }
  table[data-rp-cards] td { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; padding: 8px 0; border: 0; border-bottom: 1px solid var(--border, #eef2f7); text-align: right; min-height: 0; overflow-wrap: anywhere; }
  table[data-rp-cards] td:last-child { border-bottom: 0; }
  table[data-rp-cards] td[data-label]::before { content: attr(data-label); flex: 0 0 38%; max-width: 38%; text-align: left; font-weight: 700; font-size: 11.5px; letter-spacing: .02em; text-transform: uppercase; color: var(--muted, #64748b); padding-top: 2px; }
  table[data-rp-cards] td { white-space: normal !important; }
  table[data-rp-cards] td[data-nolabel]::before { display: none; }
  table[data-rp-cards] tr.rp-sumrow { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 8px 12px; }
  table[data-rp-cards] tr.rp-sumrow td { display: block; width: auto; border: 0; padding: 0; min-width: 0; }
  table[data-rp-cards] tr.rp-sumrow td:first-child { text-align: left; font-weight: 600; }
  table[data-rp-cards] td[data-span], table[data-rp-cards] td[colspan] { display: block; text-align: left; }
  table[data-rp-cards] td[data-span]::before { display: none; }
  table[data-rp-cards] td > .rp-cell { display: block; flex: 1 1 0; min-width: 0; text-align: inherit; overflow-wrap: break-word; }   /* one block for a cell's loose children (a label | value row, never a column per child) */
  table[data-rp-cards] td.rp-actions-cell, table[data-rp-cards] td.rp-cell-actions { justify-content: flex-end; border-bottom: 0; padding-top: 4px; }
  table[data-rp-cards] td.rp-actions-cell::before { display: none; }
  /* a page sheet that gives a td a fixed height (voip-admin: height: var(--v-row-h)) would cap a flex cell: a card cell is as tall as its content */
  html body table[data-rp-cards] td, html body table[data-rp-cards] tr, html body table[data-rp-cards] th { height: auto !important; min-height: 0 !important; }
  table[data-rp-cards] tr.rp-row-menu-open { z-index: 5; }
  .rp-rowmenu { position: relative; margin-left: auto; }
  .rp-rowmenu-btn { min-width: var(--rp-tap); min-height: var(--rp-tap); border: 1px solid var(--border, #e2e8f0); background: #fff; border-radius: 10px; font-size: 20px; line-height: 1; cursor: pointer; color: var(--text, #0f172a); }
  .rp-rowmenu-pop { position: absolute; right: 0; top: 100%; z-index: 40; min-width: 200px; max-width: calc(100vw - 2 * var(--rp-gutter)); background: #fff; border: 1px solid var(--border, #e2e8f0); border-radius: 12px; box-shadow: 0 10px 30px rgba(15,23,42,.2); padding: 6px; display: flex; flex-direction: column; gap: 2px; text-align: left; }
  .rp-rowmenu-pop[hidden] { display: none; }
  .rp-rowmenu-pop > a, .rp-rowmenu-pop > button, .rp-rowmenu-pop > form { display: flex; align-items: center; min-height: var(--rp-tap); width: 100%; justify-content: flex-start; padding: 8px 12px; border: 0; background: none; border-radius: 8px; text-align: left; font: inherit; cursor: pointer; color: inherit; text-decoration: none; }
  .rp-rowmenu-pop > form > button { min-height: var(--rp-tap); width: 100%; justify-content: flex-start; background: none; border: 0; text-align: left; font: inherit; cursor: pointer; }
  .rp-rowmenu-pop > a:hover, .rp-rowmenu-pop > button:hover { background: var(--surface-2, #f1f5f9); }
}
.rp-tablewrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; position: relative; }
.rp-tablewrap > table { min-width: 100%; }
@media (min-width: 768px) {
  .rp-tablewrap > table.rp-sticky-first th:first-child, .rp-tablewrap > table.rp-sticky-first td:first-child { position: sticky; left: 0; z-index: 2; background: var(--card-bg, #fff); box-shadow: 1px 0 0 var(--border, #e2e8f0); }
}

/* ═══ 6. MODALS AND DRAWERS — full-screen sheets below 768 with a sticky header (title + close) and a sticky footer (actions) ════════════════════════════ */
@media (max-width: 767.98px) {
  html body .rpm-overlay, html body .rpm-overlay:has(.rpm-full) { padding: 0 !important; align-items: stretch !important; }
  html body .rpm-card, html body .rpm-sm, html body .rpm-lg, html body .rpm-full { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; border-radius: 0; margin: 0; }
  html body .rpm-hd { position: sticky; top: 0; z-index: 2; background: var(--rp-sheet-bg); padding: 10px var(--rp-gutter); min-height: 56px; }
  html body .rpm-x { min-width: var(--rp-tap); min-height: var(--rp-tap); }
  html body .rpm-body { padding: 12px var(--rp-gutter) 0; flex: 1 1 auto; -webkit-overflow-scrolling: touch; }   /* no bottom padding: a sticky footer pins to the scrollport's padding edge, so padding here would float it above the sheet bottom */
  html body .rpm-actions { position: sticky; bottom: 0; z-index: 2; background: var(--rp-sheet-bg); margin: 14px calc(-1 * var(--rp-gutter)) 0; padding: 10px var(--rp-gutter) calc(10px + env(safe-area-inset-bottom)); border-top: 1px solid var(--border, #e2e8f0); flex-wrap: wrap; }
  html body .rpm-actions > * { flex: 1 1 auto; }
  html body .rpm-2col { grid-template-columns: 1fr; }
  /* the other modal families of the platform (wizards, the NetCTRL modal, the device panel, the pay modal, the VoIP admin's .v-ui .wz-modal) share the same sheet geometry; !important because the
     family sheets size their cards with higher-specificity rules (.v-ui .wz-modal { width: 520px; max-height: 90vh }) */
  html body .wz-overlay, html body .ng-modal-overlay, html body .pds-overlay, html body .v-modal-overlay, html body .v-ui .wz-overlay { padding: 0 !important; align-items: stretch !important; justify-content: stretch !important; }
  html body .wz-modal, html body .ng-modal, html body .pds-modal, html body .v-modal, html body .pay-modal, html body .wz-card, html body .v-ui .wz-modal, html body .v-ui .wz-modal.wz-modal--drawer { width: 100vw !important; max-width: 100vw !important; height: 100dvh !important; max-height: 100dvh !important; border-radius: 0 !important; margin: 0 !important; overflow-y: auto; box-sizing: border-box; padding-left: var(--rp-gutter); padding-right: var(--rp-gutter); }
  html body .wz-modal-hd, html body .v-ui .wz-modal-hd { position: sticky; top: 0; z-index: 2; background: var(--rp-sheet-bg); padding: 10px 0; margin-bottom: 8px; }
  html body .wz-modal-ft, html body .wz-actions, html body .v-ui .wz-modal .wz-actions, html body .v-ui .wz-modal .wz-modal-ft { position: sticky; bottom: 0; z-index: 2; background: var(--rp-sheet-bg); padding: 10px 0 calc(10px + env(safe-area-inset-bottom)); border-top: 1px solid var(--border, #e2e8f0); }
  html body [role=dialog]:not(#rp-copilot):not(.rp-copilot) .modal-hd, html body [role=dialog]:not(#rp-copilot) header { position: sticky; top: 0; z-index: 2; background: var(--rp-sheet-bg); }
  /* the resident dialog family (.rp-modal), the VoIP port modal (.vp-modal) and the framed panel (.rpm-full iframe) */
  html body .rp-modal-ov, html body .vp-overlay { padding: 0 !important; align-items: stretch !important; justify-content: stretch !important; }
  html body .rp-modal, html body .vp-modal { width: 100vw !important; max-width: 100vw !important; height: 100dvh !important; max-height: 100dvh !important; border-radius: 0 !important; margin: 0 !important; display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box; }
  html body .rp-modal-hd { position: sticky; top: 0; z-index: 2; background: var(--rp-sheet-bg); }
  html body .rp-modal-bd { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; }
  html body .rp-modal-ft { position: sticky; bottom: 0; z-index: 2; background: var(--rp-sheet-bg); padding-bottom: calc(12px + env(safe-area-inset-bottom)); flex-wrap: wrap; }
  html body .rp-modal-ft > * { flex: 1 1 auto; }
  html body .vp-modal > * { min-width: 0; }
  html body .rpm-full iframe, html body .rpm-full .rpm-body > * { width: 100% !important; max-width: 100% !important; box-sizing: border-box; }
  html body .rpm-full .rpm-body { overflow-x: hidden; }
}

/* ═══ 7. CO-PILOT ON A PHONE — a bottom-right widget; the open panel is a full-screen sheet; a walk docks it as a bottom sheet with the field kept above ═══ */
@media (max-width: 767.98px) {
  html.rp-cp-settling #rp-copilot:not(.rp-cp-minimized) { opacity: 0 !important; pointer-events: none !important; }   /* the auto-open is held invisible for the first second (rp-shell.js decides) */
  /* a sheet is open: the Co-Pilot widget would sit on its footer */
  html body:has(.rpm-overlay) #rp-cp-widget, html body:has(.wz-overlay:not([hidden])) #rp-cp-widget, html body:has(.vp-overlay:not([hidden])) #rp-cp-widget, html body:has(.rp-modal-ov) #rp-cp-widget, html body:has(.ng-modal:not([hidden])) #rp-cp-widget { display: none !important; }
  /* ★ A, 2026-10-22 (found driving the console's change flow at 390): with a sheet open the widget is hidden above — but its opaque DOCK BAND (z 9590, 76 px) stayed, ABOVE the modal (z 321), covering the bottom 76 px of every modal: a button at the end of a modal could not be tapped or scrolled clear of it. No widget, no band. */
  html body:has(.rpm-overlay)::before, html body:has(.wz-overlay:not([hidden]))::before, html body:has(.vp-overlay:not([hidden]))::before, html body:has(.rp-modal-ov)::before, html body:has(.ng-modal:not([hidden]))::before { display: none !important; }
  html body #rp-cp-widget { right: 12px; bottom: calc(12px + env(safe-area-inset-bottom)); transform: none !important; }   /* a saved drag offset from a wider screen must never carry the widget off a phone */
  /* ★ MOBILE-SHELL-01 REV 1 (Michael 2026-10-21 18:55, from production at 390: "the Co-Pilot panel opens as an overlay on top of the page content, covering the Ruling line card and its 5 waiting questions").
     THE RULE: EXPANDED = a full-height BOTTOM SHEET that takes the viewport and has its OWN scroll (the messages), the PAGE SCROLL LOCKED behind it (html/body overflow hidden + rp-shell.js's touch guard), the chevron / X returns;
     MINIMIZED = ONE docked pill bottom-right above the safe area, standing on an opaque dock band with a reserved gutter so no page control is ever under it (below). Never a half-cover (the 46 % sheet of 17:47 was exactly that).
     A running WALK is the one exception: the panel docks at the bottom (42 %) and the page keeps scrolling above it, because the guided ring points at fields on the page. */
  html { --rp-cp-sheet: min(42dvh, 360px); }   /* the walk's dock height (the expanded sheet is the whole viewport) */
  html body #rp-copilot.rp-copilot:not(.rp-cp-minimized):not([hidden]) { left: 0 !important; right: 0 !important; top: 0 !important; bottom: 0 !important; width: 100vw !important; max-width: 100vw !important; height: 100dvh !important; max-height: 100dvh !important;
    border-radius: 0 !important; transform: none !important; margin: 0 !important; overscroll-behavior: contain; padding-bottom: env(safe-area-inset-bottom, 0px); box-sizing: border-box; }
  html body #rp-copilot .rp-cp-msgs, html body #rp-copilot [data-rp-msgs] { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  html:not(.rp-walking-phone):not(.rp-cp-settling):has(#rp-copilot.rp-copilot:not(.rp-cp-minimized):not([hidden])), html:not(.rp-walking-phone):not(.rp-cp-settling):has(#rp-copilot.rp-copilot:not(.rp-cp-minimized):not([hidden])) body { overflow: hidden !important; overscroll-behavior: none; height: 100%; }   /* the page behind the sheet does not scroll (the 16:45 trap: swipes moved a page nobody could see); not while the auto-open is still being settled (html.rp-cp-settling holds it invisible for ~1 s, then folds it into the pill) */
  /* the folded widget gets its own DOCK: an opaque band the width of the screen, so page text scrolls UNDER a footer and never behind a floating pill; the page ends 88 px lower (+ the safe area) */
  html body.rp-cp-widget-on::before { content: ''; position: fixed; left: 0; right: 0; bottom: 0; height: calc(76px + env(safe-area-inset-bottom, 0px)); z-index: 9590; background: var(--rp-sheet-bg, #fff); border-top: 1px solid rgba(15,23,42,.12); box-shadow: 0 -6px 18px rgba(15,23,42,.06); }
  html body.rp-cp-widget-on::after { height: calc(88px + env(safe-area-inset-bottom, 0px)); }
  /* a walk docks the panel at the bottom (42 %): the page ends that much lower and scroll-padding keeps the field above it */
  html body.rp-walking #rp-copilot.rp-copilot:not(.rp-cp-minimized):not([hidden]) { top: auto !important; height: var(--rp-cp-sheet) !important; max-height: var(--rp-cp-sheet) !important; border-radius: 16px 16px 0 0 !important; box-shadow: 0 -10px 30px rgba(15,23,42,.25); }
  html body.rp-walking:has(#rp-copilot.rp-copilot:not(.rp-cp-minimized):not([hidden]))::after { content: ''; display: block; height: calc(var(--rp-cp-sheet) + 16px); }
  html.rp-walking-phone { scroll-padding-bottom: calc(var(--rp-cp-sheet) + 24px); }
  /* the environment pill lives in the top bar next to the avatar (rp-shell.js adds .rp-env-chip); the floating corner badge is not drawn on a phone */
  html body:has(.topbar-right .rp-env-chip) #rp-env-banner { display: none !important; }   /* only where the chip exists: a page with no top bar (sign-in, forgot, affiliates) keeps the corner badge so it never loses its environment mark */
  html body .rp-env-chip { display: inline-flex; }
  /* the guided highlight and the pointer glyph never leave the screen */
  html body #rp-guide-ring, html body .rp-guide-ring { max-width: calc(100vw - 8px); }
  html body .rp-guide-pointer { max-width: 48px; max-height: 48px; }
}
@media (max-width: 1023.98px) { #rp-cp-widget { max-width: calc(100vw - 16px); } }

/* ★ CORP-VOICE-ADDON-01 §4.2 (A, 2026-10-19): the public header gained a ninth link ("Add-on services"); the bar's links stay on ONE line each (a wrapped "Find a Park" / "Get Started" read as broken) — a tighter gap, and nowrap. Phones and tablets keep their scroll strip. */
@media (min-width: 1024px) { html body .top-nav__links { gap: 18px; } html body .top-nav__links a { white-space: nowrap; } }
@media (min-width: 1280px) { html body .top-nav__links { gap: 22px; } }

/* ★ RULING-LINE-03 (Michael 2026-10-21 16:17, the Agents page at 390): a control or message scrolled into view (focus, scrollIntoView, an #anchor) landed at y = 0, UNDER the sticky top bar, or at the bottom edge, UNDER the Co-Pilot dock —
   "Send Suggestion does nothing" (it had posted; its confirmation sat under the bar). The browser keeps scroll-padding clear of fixed chrome, so every page does it once, here: the top bar (all widths) and, on a phone, the dock
   (the widget is 58 px + 16 px + the iOS safe area; body.rp-cp-widget-on::after already reserves 96 px at the END of the page — this is the same band for everything scrolled to). */
html { scroll-padding-top: var(--rp-scroll-top, 72px); }   /* rp-shell.js measures the sticky/fixed stack at the top (top bar + the Settings tab strip = 159 px at 390) into --rp-scroll-top */
@media (max-width: 767.98px) { html { scroll-padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); } }
.rp-env-chip { display: none; align-items: center; margin-right: 6px; padding: 2px 8px; border-radius: 999px; font: 700 11px/1.5 system-ui, sans-serif; letter-spacing: .05em; color: #fff; background: #475467; white-space: nowrap; flex-shrink: 0; }
.rp-env-chip[data-env="prod"] { background: #b42318; }

/* ★ MOBILE-SHELL-01 REV 1 (A, 2026-10-21) — text that ran out of its OWN box at 390 (tests/responsive_audit.js 'spill'): the VoIP header tiles' status word ("not reported", 8 px over) and a reconcile row's [Release] button (21 px over) wrap instead of spilling. */
@media (max-width: 767.98px) {
  html body .vsw-tile-txt, html body .vsw-tile-txt b { min-width: 0; white-space: normal; overflow-wrap: anywhere; }
  html body .vd-recon-row { flex-wrap: wrap; row-gap: 8px; }
}

/* ★ MOBILE-SHELL-01 REV 1 (A, gate on the park plane): the camp store POS's filter row (category select + search + grid/list toggle) was a nowrap flex row 522 px wide inside a 320 px card whose overflow is hidden — the search box and the grid/list buttons sat off-screen and could not be tapped at 390. It wraps. */
@media (max-width: 767.98px) {
  html body .pos-filters { flex-wrap: wrap; row-gap: 8px; }
  html body .pos-filters #posCatSelect { max-width: none; flex: 1 1 150px; min-width: 0; }
  html body .pos-filters > input, html body .pos-filters > .form-control:not(select) { flex: 1 1 150px; min-width: 0; }
  html body .pos-pagefoot { flex-wrap: wrap; row-gap: 8px; justify-content: center; }   /* the pager (1 2 3 … 8) and its "Showing 1–8 of 62" line were one nowrap row 542 px wide: pages 6–8 were off the card */
  html body .pos-pager { flex-wrap: wrap; justify-content: center; max-width: 100%; }
}
