.btn{font-family:var(--font-ui);font-size:13px;font-weight:500;min-height:33px;padding:8px 14px;border-radius:var(--r-sm);border:1px solid transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:7px;line-height:1;background:var(--bg-raised);color:var(--text-1);transition:background .15s,border-color .15s,color .15s}
.btn svg{width:15px;height:15px}
@keyframes icon-spin360{to{transform:rotate(360deg)}}
.icon-spin{animation:icon-spin360 .8s linear infinite;transform-origin:center}
.btn-primary{background:var(--accent-strong);color:#fff;border-color:transparent}
.btn-primary:hover{background:var(--accent)}
.btn-secondary{background:var(--bg-raised);color:var(--text-1);border-color:var(--border)}
.btn-secondary:hover{background:var(--bg-2);border-color:var(--border-strong)}
.btn-ghost{background:transparent;color:var(--text-2);border-color:transparent}
.btn-ghost:hover{background:var(--bg-raised);color:var(--text-1)}
.btn-icon{padding:7px;gap:0}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn[aria-busy="true"]{opacity:.75;cursor:progress}
.state{margin:auto;max-width:34ch;text-align:center;padding:var(--s5);color:var(--text-muted)}
.state h3{color:var(--text-1);font-size:14px;margin-bottom:4px}
.state .skel{height:13px;border-radius:5px;background:linear-gradient(90deg,#16202f 25%,#1d293b 50%,#16202f 75%);background-size:200% 100%;animation:sh 1.3s infinite;margin:9px auto;max-width:240px}
@keyframes sh{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* ── btn-danger (solid, confirmDialog) + danger-ghost (subtle destructive) ──── */
.btn-danger{background:var(--danger-strong);color:#fff;border-color:transparent}
.btn-danger:hover{background:var(--danger)}
.btn-danger-ghost{background:transparent;color:var(--danger);border-color:color-mix(in srgb,var(--danger) 30%,transparent)}
.btn-danger-ghost:hover{background:var(--danger-dim);border-color:var(--danger)}

/* ── Badge ───────────────────────────────────────────────────────────────────
   <span class="badge badge-<variant>"> */
.badge{display:inline-flex;align-items:center;gap:5px;font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;padding:2px 7px;border-radius:var(--r-pill);line-height:1.6;white-space:nowrap}
.badge-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
/* live — pulsing green */
.badge-live{background:color-mix(in srgb,var(--live) 14%,transparent);color:var(--live)}
.badge-live .badge-dot{background:var(--live);animation:pulse-dot 1.6s ease-in-out infinite}
@keyframes pulse-dot{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(.7)}}
/* up — blue */
.badge-up{background:var(--accent-soft);color:var(--accent)}
.badge-up .badge-dot{background:var(--accent)}
/* ok — green */
.badge-ok{background:var(--ok-dim);color:var(--ok)}
.badge-ok .badge-dot{background:var(--ok)}
/* warn — amber */
.badge-warn{background:var(--warn-dim);color:var(--warn)}
.badge-warn .badge-dot{background:var(--warn)}
/* danger — red */
.badge-danger{background:var(--danger-dim);color:var(--danger)}
.badge-danger .badge-dot{background:var(--danger)}
/* ended / neutral — grey */
.badge-ended,.badge-neutral{background:color-mix(in srgb,var(--border) 40%,transparent);color:var(--text-muted)}
.badge-ended .badge-dot,.badge-neutral .badge-dot{background:var(--text-muted)}

/* ── Chip ────────────────────────────────────────────────────────────────────
   <button class="chip [active]"> */
.chip{font-family:var(--font-ui);font-size:12px;font-weight:500;padding:4px 12px;border-radius:var(--r-pill);border:1px solid var(--border);background:transparent;color:var(--text-2);cursor:pointer;line-height:1.5;transition:background .15s,color .15s,border-color .15s}
.chip:hover{background:var(--bg-raised);color:var(--text-1);border-color:var(--border-strong)}
.chip.active{background:var(--accent-soft);color:var(--accent);border-color:var(--accent)}

/* ── Card ────────────────────────────────────────────────────────────────────
   <section class="card"> */
.card{background:var(--bg-card);border:1px solid var(--border-subtle);border-radius:var(--r-md);overflow:hidden}
.card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s3);padding:var(--s4);border-bottom:1px solid var(--border-subtle)}
.card-title-group{flex:1;min-width:0}
.card-title{font-family:var(--font-ui);font-size:13px;font-weight:600;color:var(--text-1);margin:0;line-height:1.3}
.card-subtitle{font-family:var(--font-ui);font-size:11px;color:var(--text-muted);margin:3px 0 0}
.card-actions{display:flex;align-items:center;gap:var(--s2);flex-shrink:0}
.card-body{padding:var(--s4)}

/* ── StatCard ────────────────────────────────────────────────────────────────
   <div class="stat-card"> */
.stat-card{background:var(--bg-card);border:1px solid var(--border-subtle);border-radius:var(--r-md);padding:var(--s4) var(--s4) var(--s3)}
.stat-label{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);margin-bottom:var(--s2)}
.stat-value{font-family:var(--font-mono);font-size:28px;font-weight:700;color:var(--text-1);font-variant-numeric:tabular-nums;line-height:1}
.stat-value.accent{color:var(--accent)}

/* ── DataTable ───────────────────────────────────────────────────────────────
   <div style="overflow-x:auto"><table class="dt"> */
.dt{width:100%;border-collapse:collapse;font-family:var(--font-ui);font-size:13px;color:var(--text-1)}
.dt thead{position:sticky;top:0;z-index:1;background:var(--bg-1)}
.dt th{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--text-muted);padding:8px 12px;text-align:left;border-bottom:1px solid var(--border);white-space:nowrap;user-select:none}
.dt th[style*="cursor: pointer"]:hover,.dt th[style*="cursor:pointer"]:hover{color:var(--text-1)}
.sort-indicator{color:var(--accent);margin-left:3px;font-size:9px}
.dt td{padding:9px 12px;border-bottom:1px solid var(--border-subtle);color:var(--text-1);vertical-align:middle}
.dt td.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.dt tbody tr:hover td{background:var(--bg-raised)}

/* ── Band library (org-bands) — collapsible region sections ────────────────────
   Ported from spectrum-platform-pages .band-section*, remapped to next tokens.
   <section class="band-section"><button class="band-section-header">… */
.band-section{margin-bottom:12px}
/* top offset: sticks below the (now also sticky) global topbar on nav-shell
   pages — --stick-under-nav is the topbar height there (base.css), 0 elsewhere. */
.band-section-header{display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:7px 12px;background:var(--bg-card);border:1px solid var(--border-subtle);border-radius:var(--r-sm);cursor:pointer;user-select:none;font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--text-2);position:sticky;top:var(--stick-under-nav,0px);z-index:2;transition:background .15s,border-color .15s}
.band-section-header:hover{background:var(--bg-raised);border-color:var(--border)}
.band-section-header .chevron{display:inline-block;transition:transform 120ms ease-out;color:var(--text-muted)}
.band-section.collapsed .band-section-header .chevron{transform:rotate(-90deg)}
.band-section-header .band-section-title{color:var(--text-1)}
.band-section-header .count{margin-left:auto;color:var(--text-muted);font-size:11px;letter-spacing:0;text-transform:none}
.band-section-body{overflow-x:auto}
.band-section.collapsed .band-section-body{display:none}
/* Inline-edit affordance: editable cells get a soft hover ring (custom bands). */
.band-section .dt td [data-field][style*="cursor:cell"]:hover,
.band-section .dt td [data-field][style*="cursor: cell"]:hover{box-shadow:inset 0 0 0 1px var(--accent-soft);border-radius:var(--r-sm)}

/* ── Segmented ───────────────────────────────────────────────────────────────
   <div class="segmented" role="tablist"> */
.segmented{display:inline-flex;background:var(--bg-0);border:1px solid var(--border);border-radius:var(--r-sm);padding:2px;gap:2px}
.seg-btn{font-family:var(--font-ui);font-size:12px;font-weight:500;color:var(--text-muted);background:transparent;border:none;border-radius:calc(var(--r-sm) - 2px);padding:5px 12px;cursor:pointer;line-height:1.4;display:inline-flex;align-items:center;gap:6px;transition:color .15s,background .15s}
.seg-btn:hover{color:var(--text-1)}
.seg-btn.active{background:var(--bg-raised);color:var(--text-1);box-shadow:0 1px 3px rgba(0,0,0,.4)}
.seg-count{font-family:var(--font-mono);font-size:10px;font-weight:600;background:var(--border);color:var(--text-muted);border-radius:var(--r-pill);padding:1px 6px;line-height:1.4}
.seg-btn.active .seg-count{background:var(--accent-soft);color:var(--accent)}
/* Status verdict dot (e.g. Channel Health tab worst-verdict) */
.seg-dot{width:7px;height:7px;border-radius:50%;background:var(--text-muted);flex-shrink:0}
.seg-dot.is-green{background:var(--ok)}
.seg-dot.is-amber{background:var(--warn)}
.seg-dot.is-red{background:var(--danger)}
/* UNKNOWN: recorded spectrum gone from storage — clearance not measured (amber ring, not a fill) */
.seg-dot.is-unknown{background:transparent;box-shadow:inset 0 0 0 1.5px var(--warn)}
/* Not measured, no conflict detected (PR 912 round 5b): "Not measured / no carriers on air" and
   "No IM conflict among carriers on air / not measured" (neutral grey ring, never green) */
.seg-dot.is-no_carriers_on_air,.seg-dot.is-on_air_checked{background:transparent;box-shadow:inset 0 0 0 1.5px var(--text-muted)}
/* No channels in the plan, or Channel Health stopped refreshing: a faint neutral ring */
.seg-dot.is-empty,.seg-dot.is-stale{background:transparent;box-shadow:inset 0 0 0 1.5px var(--border)}
/* Clear over PARTIAL coverage (#912 leftovers c): a half-filled ring whose tooltip says
   "Ready on measured data, N recorded chunks unavailable", never a bare green dot */
.seg-dot.is-partial{background:linear-gradient(90deg,var(--ok) 50%,transparent 50%);box-shadow:inset 0 0 0 1.5px var(--ok)}
/* A simulated board (#912 leftovers e): a dashed amber ring, never a verdict colour */
.seg-dot.is-simulated{background:transparent;border:1.5px dashed var(--warn);box-sizing:border-box}

/* ── Modal ───────────────────────────────────────────────────────────────────
   .modal-overlay > .modal */
.modal-overlay{position:fixed;inset:0;z-index:900;display:flex;align-items:center;justify-content:center;background:rgba(5,7,10,.72);backdrop-filter:blur(2px)}
/* max-height in dvh: on iPhone Safari 100vh includes the collapsed-toolbar
   zone, so a vh-sized modal ran under the browser chrome; dvh tracks the
   real visible height (identical to vh on desktop). */
.modal{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--r-lg);width:min(520px,calc(100vw - 32px));max-height:calc(100dvh - 64px);display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.6)}
.modal--wide{width:min(800px,calc(100vw - 32px))}
/* xwide: the Devices Grid view — wide enough for ~10 large tiles per row */
.modal--xwide{width:min(1760px,calc(100vw - 32px))}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:var(--s4) var(--s4) var(--s3);border-bottom:1px solid var(--border-subtle);flex-shrink:0}
.modal-title{font-family:var(--font-ui);font-size:14px;font-weight:600;color:var(--text-1);margin:0}
.modal-close{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:2px 6px;border-radius:var(--r-sm);line-height:1}
.modal-close:hover{color:var(--text-1);background:var(--bg-raised)}
.modal-body{padding:var(--s4);overflow-y:auto;flex:1;color:var(--text-1);font-size:13px;line-height:1.6}
.modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--s2);padding:var(--s3) var(--s4);border-top:1px solid var(--border-subtle);flex-shrink:0}
/* phone: full-screen sheet */
@media(max-width:640px){
  .modal,.modal--wide{width:100%;max-height:90vh;border-bottom-left-radius:0;border-bottom-right-radius:0;margin-top:auto}
  .modal-overlay{align-items:flex-end}
}

/* ── Dialog helpers ──────────────────────────────────────────────────────────*/
.dialog-message{font-size:13px;color:var(--text-2);margin:0;line-height:1.6}
.dialog-hint{font-size:12px;color:var(--text-muted);margin:8px 0 0;line-height:1.5}
.dialog-footer{display:flex;gap:var(--s2)}

/* ── Toast ───────────────────────────────────────────────────────────────────
   .toast-stack > .toast */
/* Safe-area aware: clears the iPhone home indicator (portrait) and the
   landscape notch side; --safe-* are 0px elsewhere. */
.toast-stack{position:fixed;bottom:calc(var(--s5) + var(--safe-bottom));right:calc(var(--s4) + var(--safe-right));z-index:1000;display:flex;flex-direction:column;gap:var(--s2);pointer-events:none}
.toast{pointer-events:all;display:flex;align-items:center;gap:var(--s3);background:var(--bg-raised);border:1px solid var(--border);border-left-width:3px;border-radius:var(--r-sm);padding:10px var(--s3);min-width:240px;max-width:360px;font-size:13px;color:var(--text-1);box-shadow:0 8px 24px rgba(0,0,0,.5);animation:toast-in .2s ease}
.toast span:first-child{flex:1}
.toast-close{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:12px;padding:0 2px;line-height:1;flex-shrink:0}
.toast-close:hover{color:var(--text-1)}
/* inline action (opts.action, e.g. Undo) — a text button before the × */
.toast-action{background:none;border:1px solid var(--border);border-radius:var(--r-sm);color:var(--accent,var(--text-1));cursor:pointer;font-size:12px;font-weight:600;padding:4px 10px;min-height:28px;flex-shrink:0}
.toast-action:hover{background:var(--bg-hover,rgba(255,255,255,.06))}
.toast-action:focus-visible{outline:2px solid var(--accent,#60a5fa);outline-offset:1px}
/* require_ack toasts: stay until clicked (whole toast is the dismiss target) */
.toast-sticky{cursor:pointer}
/* type tints — left border */
.toast-info{border-left-color:var(--accent)}
.toast-ok{border-left-color:var(--ok)}
.toast-warn{border-left-color:var(--warn)}
.toast-error{border-left-color:var(--danger)}
@keyframes toast-in{from{opacity:0;transform:translateX(16px)}to{opacity:1;transform:none}}
.toast-out{animation:toast-out .25s ease forwards}
@keyframes toast-out{to{opacity:0;transform:translateX(16px)}}
/* While a dialog is open (toast.js placeToasts): top-left, clear of the
   dialog's footer and close button; toast.js caps height/width from the
   measured rows. Scrolls past the cap, newest in view. */
.toast-stack.is-over-dialog{top:calc(var(--s3) + var(--safe-top));bottom:auto;left:calc(var(--s4) + var(--safe-left));right:auto;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain}
.toast-stack.is-over-dialog .toast{min-width:0;max-width:min(360px,calc(100vw - 2 * var(--s4) - var(--safe-left) - var(--safe-right)))}

/* ── Popover ─────────────────────────────────────────────────────────────────
   .popover */
/* max-width min(): a flat 320px cap is itself wider than viewports under
   336px (320 SE floor), so even a clamped card hung past the edge there.
   Identical at ≥336px viewports. */
.popover{position:fixed;z-index:800;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--r-md);box-shadow:0 12px 40px rgba(0,0,0,.55);padding:var(--s3);min-width:160px;max-width:min(320px,calc(100vw - 16px));font-size:13px;color:var(--text-1);animation:pop-in .12s ease}
@keyframes pop-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}

/* ── Touch targets (mobile foundation, Wave 4 batch 1) ────────────────────────
   Raise interactive controls to >=var(--tap-min) (44px, WCAG 2.5.5 / Apple HIG)
   on touch devices AND narrow viewports. `pointer:coarse` catches phones/tablets
   regardless of width; `max-width:760px` (our mobile breakpoint) also catches a
   narrow window/resized viewport — so the bump is verifiable in a narrow desktop
   preview and a small window gets usable targets too. Wide-window mouse/desktop
   is left EXACTLY as-is. Sizing uses min-height/min-width + padding + centering
   (NOT a fixed height) so text can still wrap without clipping. Some controls
   (.m-star, the usage pins, the monitor checkboxes/SSA range sliders) are themed
   in view-specific CSS that loads AFTER this file, so their selectors are doubled
   (.x.x) here to win on specificity from components.css without touching views. */
@media (pointer:coarse), (max-width:760px){
  /* Shared button kit */
  .btn{min-height:var(--tap-min);padding-top:0;padding-bottom:0;align-items:center}
  .btn-icon{min-width:var(--tap-min);min-height:var(--tap-min);padding:0}
  /* Chips + segmented controls — grow the hit area, keep the pill/visual look */
  .chip{min-height:var(--tap-min);padding-top:0;padding-bottom:0;display:inline-flex;align-items:center;justify-content:center}
  .seg-btn{min-height:calc(var(--tap-min) - 6px);padding-top:0;padding-bottom:0;align-items:center}
  /* Bottom tab bar links (responsive nav, base.css) */
  .tabbar a{min-height:var(--tap-min);justify-content:center}
  /* Pin/star toggle buttons (view CSS: monitor .m-star, usage row/legend/marker
     pins). Doubled selector beats the single-class view rules. */
  .m-star.m-star,
  .usage-row-pin.usage-row-pin,
  .usage-legend-pin.usage-legend-pin,
  .usage-marker-pin.usage-marker-pin{min-width:var(--tap-min);min-height:var(--tap-min)}
  /* Checkboxes / radios — natively ~14px; enlarge the box + give it a roomier
     hit area. accent-color (set in view CSS) is preserved. The 1.25 scale grows
     the RENDERED box past its layout box from the center — by box × (scale−1)/2
     per side (24px × .125 = 3px) — which visually eats the box↔label gap. An
     equal margin per side reserves that overflow, so the rendered edge lands
     exactly on the reserved edge and the box↔label gap matches the desktop gap.
     --tap-box (on :root above the selectors that need it) is the single source
     for the base size so the compensation math stays in sync. */
  :root{--tap-box:24px}
  input[type="checkbox"],
  input[type="radio"]{min-width:var(--tap-box);min-height:var(--tap-box);transform:scale(1.25);transform-origin:center;margin:calc(var(--tap-box)*.125)}
  /* Doubled to also win over the explicit 14px sizes in views-monitor.css. */
  .m-check.m-check,
  .m-checkrow input{min-width:24px;min-height:24px}
  /* Menu-row gutter: fixed 15px box (this file, later — its margin:0 also wins
     on specificity, harmlessly: inside a centered fixed gutter a symmetric
     margin cannot move the box). Grow the gutter to the RENDERED size
     (box × 1.25) so the visual edges land on the gutter's own edges and the
     10px row gap to the label survives. */
  .menu-row .gutter.gutter{width:calc(var(--tap-box)*1.25);height:calc(var(--tap-box)*1.25)}
  /* NDA agree row (views-nda.css margin:1px 0 0 wins on specificity) —
     re-assert the compensation with the .x.x trick, keeping its 1px top nudge. */
  .nda-agree.nda-agree input[type="checkbox"]{margin:calc(1px + var(--tap-box)*.125) calc(var(--tap-box)*.125) calc(var(--tap-box)*.125)}
  /* Range slider thumb — enlarge so it's draggable by finger. The track stays
     thin; we only fatten the thumb across the vendor pseudo-elements. */
  input[type="range"]{height:var(--tap-min)}
  input[type="range"]::-webkit-slider-thumb{width:24px;height:24px}
  input[type="range"]::-moz-range-thumb{width:24px;height:24px}
}

/* ── Responsive table → card reflow utility (.rtable) ─────────────────────────
   REUSABLE pattern; NOT applied to any table yet (a later batch converts the
   real tables). Contract:
     1. add class `rtable` to the <table>
     2. add data-label="ColumnName" to each <td>
   On desktop it is a normal table. Under <=760px (the codebase layout
   breakpoint) each <tr> becomes a bordered card and each <td> becomes a
   label/value row, with the column name injected from data-label via ::before.
   A <td> without a data-label (e.g. an actions/icon cell) renders no stray
   label. Pair with a wrapping `overflow-x:auto` only if you keep table mode on
   small screens — this utility replaces horizontal scroll with vertical cards. */
@media (max-width:760px){
  .rtable thead{display:none}
  .rtable tr{display:block;border:1px solid var(--border);border-radius:var(--r-sm);padding:var(--s3);margin-bottom:var(--s2)}
  .rtable td{display:flex;justify-content:space-between;gap:var(--s3);padding:4px 0;border:0;min-width:0;overflow-wrap:anywhere}
  .rtable td::before{content:attr(data-label);color:var(--text-2);font-weight:600;margin-right:var(--s3);flex:0 0 auto}
  /* No stray empty label for cells that opt out of a data-label. */
  .rtable td:not([data-label])::before{content:none}
  /* Action/icon cells (no data-label) hold several buttons — let them wrap
     left-aligned inside the card instead of forcing it wider than the viewport. */
  .rtable td:not([data-label]){flex-wrap:wrap;justify-content:flex-start;gap:var(--s2)}
}

/* === PWA install banner (js/install-prompt.js) ==========================
   Floating "Add to Home Screen" / [Install] card, restored from the v1
   spectrum-platform-pages port. Themed to next's dark tokens (v1 was a white
   card). Self-hides when installed/dismissed; on iOS shows A2HS instructions,
   on Chrome/Edge a real [Install] button. Sits above page content but below
   modal overlays (z 900+) so a modal cleanly covers it. Safe-area insets keep
   the card clear of the notch (landscape) and home indicator (portrait).
   base.css reserves matching bottom space on non-immersive routes via
   --install-banner-height (published by setupBannerSizing). */
.install-banner {
  position: fixed;
  left: calc(12px + var(--safe-left));
  right: calc(12px + var(--safe-right));
  bottom: calc(12px + var(--safe-bottom));
  z-index: 850;
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--bg-raised);
  color: var(--text-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  font-size: 13px;
  line-height: 1.35;
  /* Stacked shadows: soft wide depth + a tighter darker edge so the card reads
     as clearly "in front of" the chart/map behind it. */
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* Phone + logged-in dashboard routes: the bottom tab bar owns the bottom
   strip (sticky, z 100 < banner's 850), so lift the card above it instead of
   covering the tabs. body:has(.tabbar) scopes this to routes that actually
   render the NavShell (public/immersive routes keep the plain bottom slot).
   setupBannerSizing measures the card's top edge, so the body's reserved
   padding follows automatically. */
@media (max-width: 760px) {
  body:has(.tabbar) .install-banner {
    bottom: calc(12px + var(--tabbar-h) + var(--safe-bottom));
  }
}
.install-banner-text {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text-1);
}
.install-banner-install {
  flex: 0 0 auto;
  width: auto;
  padding: 7px 16px;
  height: 32px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  margin: 0;
}
.install-banner-install:hover { background: var(--accent-strong); }
.install-banner-dismiss {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  padding: 0;
  background: transparent;
  color: var(--text-muted);
  border: none;
  border-radius: var(--r-pill);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  margin: 0;
  /* Explicit centering so the × glyph sits flush regardless of font. */
  display: flex;
  align-items: center;
  justify-content: center;
}
.install-banner-dismiss:hover { color: var(--text-1); background: var(--bg-2); }

/* ── Select ────────────────────────────────────────────────────────────────────
   Shared boxed value-picker. Wraps a REAL native <select class="select-ctrl"> so
   keyboard/mobile/a11y come for free; the .select box is just resting chrome
   (border + optional cap + chevron). Generalises replay's .replay-seg pattern and
   monitor's .m-iface-pill into one primitive.
     <span class="select"><span class="cap">REC</span><select class="select-ctrl">…</select><svg class="chev">…</svg></span>
   Focus ring modelled on .m-iface-pill:focus (accent border + --ring). */
.select{
  position:relative;
  display:inline-flex;align-items:center;gap:8px;
  padding:7px 10px 7px 12px;
  background:var(--bg-input);border:1px solid var(--border);border-radius:var(--r-sm);
  font-size:13px;line-height:1;box-sizing:border-box;
  transition:border-color .15s,box-shadow .15s;
}
/* `.select { display:inline-flex }` out-specifies the UA `[hidden]` rule, so a
   Select hidden with `el.hidden = true` (the 3D detection picker while it has
   nothing to pick) stayed visible as an empty box. Same fix as the DECT chips. */
.select[hidden] { display: none; }

.select:focus-within{border-color:var(--accent);box-shadow:var(--ring)}
.select .cap{
  font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--text-muted);flex:none;pointer-events:none;
}
/* Visible current value — the box hugs THIS (not the widest option), so a short
   value like "1x" leaves no dead space. Ellipsises a long recording name. */
.select .select-value{
  color:var(--text-1);font-family:var(--font-ui);font-size:13px;font-weight:500;
  line-height:1.4;pointer-events:none;
  max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* Native control = full-box transparent overlay: a click anywhere in the box —
   including over the chevron — opens the picker; the visible cap/value/chevron
   are painted underneath (pointer-events:none). */
.select .select-ctrl{
  position:absolute;inset:0;width:100%;height:100%;
  -webkit-appearance:none;appearance:none;
  border:0;background:transparent;color:transparent;
  font-family:var(--font-ui);font-size:13px;font-weight:500;line-height:1.4;
  outline:none;cursor:pointer;padding:0;margin:0;
}
.select .select-ctrl:disabled{cursor:not-allowed}
.select.is-disabled{opacity:.55;cursor:not-allowed}
.select .select-ctrl option,.select .select-ctrl optgroup{color:var(--text-1);background:var(--bg-raised)}
.select .chev{width:10px;height:10px;flex:none;color:var(--text-muted);pointer-events:none}
.select.compact{padding:4px 8px 4px 9px;gap:6px;font-size:12px}
.select.compact .cap{font-size:9px;letter-spacing:.1em}
.select.compact .select-value{font-size:12px;max-width:120px}

/* ── Menu ──────────────────────────────────────────────────────────────────────
   The single canonical popover menu — rendered as the `content` of openPopover
   (see js/components/menu.js → openMenu). One style for command menus and
   single/multi-select menus. Selected rows carry a check icon (or checkbox) in a
   fixed-width .gutter so every row stays aligned. */
.menu{
  background:var(--bg-card);border:1px solid var(--border);border-radius:var(--r-md);
  box-shadow:0 12px 34px rgba(0,0,0,.55);padding:5px;min-width:200px;
}
.menu-head{
  font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--text-faint);padding:8px 10px 6px;
}
.menu-row{
  display:flex;align-items:center;gap:10px;
  padding:8px 10px 8px 8px;border-radius:6px;
  color:var(--text-1);font-family:var(--font-ui);font-size:13px;line-height:1.3;
  background:transparent;border:0;width:100%;text-align:left;cursor:pointer;
}
.menu-row:hover{background:rgba(255,255,255,.05)}
.menu-row .gutter{
  width:15px;height:15px;flex:none;
  display:inline-flex;align-items:center;justify-content:center;color:var(--accent);
}
.menu-row .gutter input[type=checkbox]{accent-color:var(--accent);margin:0;cursor:pointer}
.menu-row.sel{color:#fff}
.menu-row .menu-labelwrap{display:flex;flex-direction:column;gap:2px;min-width:0}
.menu-row .menu-label{display:inline-flex;align-items:center;gap:6px}
.menu-row .menu-detail{font-family:var(--font-mono);font-size:11px;color:var(--text-muted);line-height:1.3}
/* When a .menu is the content of openPopover, the outer .popover card would
   double the chrome — strip it so .menu owns the visuals (keep fixed position,
   z-index and pop-in animation from the base .popover). */
.popover.menu-pop{background:transparent;border:0;box-shadow:none;padding:0;min-width:0;max-width:none}

/* ── Tablet gap (761–1099px; iPad portrait/landscape) ─────────────────────────
   Wide admin tables (users, invitations, traces, DO status) sit between the
   ≤760px card reflow (.rtable) and the roomy desktop table: cells wrap
   mid-value ("45d ago" → "45d/ago", three-line dates) and edge columns clip.
   DataTable already wraps every table in an overflow-x:auto div — keep cells
   on one line at tablet widths so that scroller engages instead. The DO-status
   table (.amp-do-table, styled inline in admin-panels.js) has no wrapper, so
   it becomes its own horizontal scroll box here. Desktop ≥1100 and the ≤760
   card reflow are untouched. */
@media (min-width:761px) and (max-width:1099px){
  .dt td { white-space: nowrap; }
  .amp-do-table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .amp-do-table th, .amp-do-table td { white-space: nowrap; }
}

/* ─── Input + Field ───────────────────────────────────────────────────────────
   .input is the boxed text/number field — the .select's sibling (same box, the
   same 33px height as .btn so a row of input + chip + button lines up on one
   baseline). .input-num is the narrow numeric variant; .input-unit the small
   unit word after it; .input-range a slider sized to the same row height.
   .field is ONE labelled form row (label column left, control cluster right,
   caption UNDER the control); stack rows in .field-list so the label column
   lines up (js/components/field.js). */
.input{font-family:var(--font-ui);font-size:13px;font-weight:500;line-height:1.4;min-height:33px;padding:6px 10px;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--r-sm);color:var(--text-1);box-sizing:border-box;min-width:0;transition:border-color .15s,box-shadow .15s}
.input::placeholder{color:var(--text-muted);font-weight:400}
.input:focus{outline:none;border-color:var(--accent);box-shadow:var(--ring)}
.input:disabled{opacity:.55;cursor:not-allowed}
.input-num{width:92px;font-variant-numeric:tabular-nums}
.input-unit{font-size:12px;color:var(--text-muted);white-space:nowrap}
.input-range{accent-color:var(--accent);width:160px;height:33px;margin:0;box-sizing:border-box}
.field-list{display:flex;flex-direction:column;gap:10px}
.field{display:grid;grid-template-columns:112px minmax(0,1fr);gap:0 12px;align-items:start}
.field-label{font-family:var(--font-ui);font-size:12px;color:var(--text-2);line-height:33px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.field-ctl{min-width:0;display:flex;flex-direction:column}
.field-controls{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:33px}
.field-caption{font-size:12px;color:var(--text-muted);line-height:1.4;margin-top:4px;display:flex;gap:10px;flex-wrap:wrap}
.field-caption:empty{display:none}
@media (max-width:560px){.field{grid-template-columns:1fr}.field-label{line-height:1.4;padding-bottom:4px}}
/* Marker editor — Time slots picker (marker-fields.js openSlotPicker). Anchored to the field, above modals. */
.mslot-pop{position:fixed;z-index:1100;top:64px;left:8px;width:min(340px,calc(100vw - 16px));background:var(--bg-card);border:1px solid var(--border);border-radius:var(--r-md);box-shadow:0 16px 40px rgba(0,0,0,.55);padding:10px 10px 8px;display:flex;flex-direction:column;gap:8px;font-size:12px;color:var(--text-1)}
.mslot-title{font-size:13px;font-weight:600}
.mslot-list{display:flex;flex-direction:column;gap:2px;max-height:min(50vh,320px);overflow:auto}
.mslot-item{display:flex;align-items:flex-start;gap:8px;padding:5px 6px;border-radius:var(--r-sm);cursor:pointer}
.mslot-item:hover{background:var(--bg-raised)}
.mslot-item input{margin-top:2px;flex:none}
.mslot-entire{border-bottom:1px solid var(--border-subtle);border-radius:var(--r-sm) var(--r-sm) 0 0;padding-bottom:7px;margin-bottom:2px}
.mslot-text{display:flex;flex-direction:column;min-width:0}
.mslot-name{overflow-wrap:anywhere}
.mslot-when{color:var(--text-muted);font-size:11px;white-space:nowrap}
.mslot-empty{color:var(--text-muted);padding:4px 6px}
.mslot-foot{display:flex;justify-content:flex-end;gap:6px}
.mslot-foot .btn{min-height:28px;padding:5px 12px;font-size:12px}
