.btn{font-family:var(--font-ui);font-size:13px;font-weight:500;min-height:var(--control-h);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}
/* Sizes. min-height stays ≥24px (WCAG 2.5.8). These sit above the coarse-pointer
   block, so that block's min-height:var(--tap-min) on .btn still wins there.
   Icon min-width is restated inside that block: a (0,2,0) rule here would beat it. */
.btn-sm{min-height:28px;padding:4px 10px;font-size:12px;gap:5px}
.btn-xs{min-height:24px;padding:2px 8px;font-size:11px;gap:4px}
.btn-sm svg{width:13px;height:13px}
.btn-xs svg{width:12px;height:12px}
.btn-sm.btn-icon{padding:5px;min-width:28px}
.btn-xs.btn-icon{padding:3px;min-width:24px}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn[aria-busy="true"]{opacity:.75;cursor:progress}
/* Back link, shared. Admin used to inject this; the other six headers had the
   class and no rule. margin-bottom puts the link above a stacked title.
   A row header cancels that gap so the link stays on the title's line. */
.view-header{display:flex;align-items:center;flex-wrap:wrap;gap:12px;min-width:0}
.btn-back{
  display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;
  font-size:12px;color:var(--text-muted);text-decoration:none;white-space:nowrap;
  margin-bottom:10px;
  background:none;border:none;padding:0;cursor:pointer;font-family:inherit;
}
.btn-back:hover{color:var(--accent-text)}
.view-header .btn-back,
.cl-header .btn-back,
.pv-head .btn-back,
.map-standalone-head .btn-back{margin-bottom:0}
.state{margin:auto;max-width:34ch;text-align:center;padding:var(--s5);color:var(--text-muted)}
.state-inline{margin:0;max-width:none;text-align:inherit;padding:0}
.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}
.state-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@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:color-mix(in srgb, var(--danger-strong) 92%, #fff)}
.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}
button.badge{border:0;cursor:pointer;font:inherit;font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:inherit}
button.badge:focus-visible{outline:none;box-shadow:var(--ring)}
.badge.badge-prose{text-transform:none;letter-spacing:0;font-family:var(--font-ui);font-size:12px;font-weight:600}
button.badge.badge-prose{text-transform:none;letter-spacing:0;font-family:var(--font-ui);font-size:12px}
.badge-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
.badge-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border: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-text)}
.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-text);border-color:var(--accent)}

/* ── Card ────────────────────────────────────────────────────────────────────
   <section class="card"> */
/* Two tiers: the page card is r-md; a card nested in a card is the inset (r-sm). */
.card{background:var(--bg-card);border:1px solid var(--border-subtle);border-radius:var(--r-md);overflow:hidden}
.card .card{border-radius:var(--r-sm)}
.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-text)}

/* ── 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-text);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)}
/* Group header rows (DataTable { __group }): a quiet full-width band. */
.dt tbody tr.dt-group td{padding:0;background:var(--bg-1);border-bottom:1px solid var(--border)}
.dt tbody tr.dt-group:hover td{background:var(--bg-1)}
.dt-group-head{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;padding:8px 12px;font-family:var(--font-ui);font-size:12px;font-weight:600;color:var(--text-2);background:none;border:0;text-align:left}
button.dt-group-head{cursor:pointer}
button.dt-group-head:hover{color:var(--text-1)}
.dt-group-chev{width:10px;color:var(--text-muted)}
.dt-group-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}
.dt-group-meta{font-weight:400;color:var(--text-muted)}

/* ── 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-text)}
/* 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-text,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}}

/* ── Switch ──────────────────────────────────────────────────────────────────
   Saved settings. Track + thumb from the admin switch; the button carries
   role=switch and aria-checked. Host pills replace the track and keep their
   own look. The ::before is the hit target (24px fine, 44px on the kit pair
   below) and does not change the track size. */
.ui-switch{position:relative;cursor:pointer}
.ui-switch:focus-visible{outline:none;box-shadow:var(--ring)}
/* Admin mkSwitch adds .adm-toggle, which already rings .adm-toggle-track. */
.ui-switch.adm-toggle:focus-visible{box-shadow:none}
.ui-switch:has(.ui-switch-track){display:inline-flex;align-items:center;gap:6px;padding:0;border:none;background:transparent;font:inherit;color:inherit}
.ui-switch-track{display:block;box-sizing:border-box;position:relative;width:40px;height:22px;border-radius:99px;flex:none;background:var(--bg-raised);border:1px solid var(--border-subtle)}
.ui-switch-sm .ui-switch-track{width:30px;height:16px}
.ui-switch-thumb{position:absolute;top:50%;transform:translateY(-50%);left:3px;width:14px;height:14px;border-radius:50%;background:var(--text-muted);transition:left 200ms ease,background 200ms ease}
.ui-switch-sm .ui-switch-thumb{left:2px;width:10px;height:10px}
.ui-switch[aria-checked="true"] .ui-switch-thumb{left:calc(100% - 17px);background:var(--ok)}
.ui-switch-sm[aria-checked="true"] .ui-switch-thumb{left:calc(100% - 12px)}
.ui-switch[aria-checked="true"] .ui-switch-track{background:var(--ok-dim);border-color:var(--ok)}
.ui-switch-label{font-family:var(--font-ui);font-size:12px;color:var(--text-1);line-height:1.2}
.ui-switch:has(.ui-switch-track):disabled{opacity:.45;cursor:not-allowed}
.ui-switch::before{content:'';position:absolute;left:50%;top:50%;width:max(100%,24px);height:24px;transform:translate(-50%,-50%)}

/* ── 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}
  .btn-sm.btn-icon,.btn-xs.btn-icon{min-width:var(--tap-min);min-height:var(--tap-min);padding:0}
  .ui-switch::before{width:max(100%,var(--tap-min));height:var(--tap-min)}
  /* 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)}
  /* A group header is a band, not a card. */
  .rtable tr.dt-group{border:0;padding:0;margin-bottom:var(--s1)}
  .rtable tr.dt-group td{display:block;padding:0}
}

/* === 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-muted);padding:8px 10px 6px;
}
.menu-note{
  padding:8px 10px;font-size:12.5px;line-height:1.35;color:var(--text-1);
  white-space:normal;overflow-wrap:anywhere;
}
.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-text);
}
.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). */
/* Help "?" (components/help-tip.js): a quiet icon button; the card is a normal
   .popover with a fixed, viewport-bounded width so it never hugs the edge. */
.help-tip{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:20px;height:20px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--text-muted);cursor:pointer;line-height:1}
.help-tip:hover,.help-tip[aria-expanded="true"]{color:var(--accent-text);background:var(--bg-raised)}
.help-tip:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* Touch: the glyph stays 20px, the HIT area grows to ~44px around it. */
@media (pointer:coarse),(max-width:760px){.help-tip{position:relative}.help-tip::after{content:'';position:absolute;inset:-12px}}
.popover.help-tip-card{width:min(300px,calc(100vw - 32px));max-width:min(300px,calc(100vw - 32px));overflow-wrap:anywhere;font-size:12px;line-height:1.45;color:var(--text-2)}
.help-tip-title{font-weight:600;color:var(--text-1);margin-bottom:6px}
.help-tip-text{margin:0 0 6px}
.help-tip-text:last-child{margin-bottom:0}
.help-tip-list{margin:0;display:flex;flex-direction:column;gap:6px}
.help-tip-list dt{font-weight:600;color:var(--text-1)}
.help-tip-list dd{margin:1px 0 0}
.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). */
/* One form-row control. .form-select / .adm-select / .amp-select share this
   chrome; only their width rules differ. Toolbar .select stays on its own rule. */
.input,
.form-select,
.adm-select,
.amp-select{font-family:var(--font-ui);font-size:13px;font-weight:500;line-height:1.4;min-height:var(--control-h);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}
select.input,
select.form-select,
select.adm-select,
select.amp-select{height:var(--control-h);min-height:var(--control-h);cursor:pointer}
.input:focus,
.form-select:focus,
.adm-select:focus,
.amp-select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent)}
.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}
select.input{width:fit-content;min-width:var(--field-xs);max-width:min(100%, var(--field-l))}
/* FrequencyField: every MHz box is token s. Compact is the header jump
   (104px). The panel header still forces that jump's height via .m-jump. */
input.freq-field[data-field="s"]{width:var(--field-s);max-width:100%}
input.freq-field.freq-field--compact{width:104px;max-width:100%;-moz-appearance:textfield;appearance:textfield}
input.freq-field.freq-field--compact::-webkit-inner-spin-button,
input.freq-field.freq-field--compact::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
@media (pointer:coarse),(max-width:760px){input.freq-field{font-size:16px}}
.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}
/* display:grid beats the user-agent [hidden] rule, so a hidden Field stayed on screen. */
.field[hidden]{display:none}
.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}
.field-contents{display:contents}
.field-stack{display:flex;flex-direction:column;align-items:stretch;gap:4px;min-width:0}
.field-stack>.field-label{line-height:1.4}
@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}

/* Receiver group: one receiver, its channels collapsible underneath
   (components/receiver-group.js — RX Vitals "+ Add receiver" and the attach dialog). */
.rxg{border-bottom:1px solid var(--border-subtle)}
.rxg:last-child{border-bottom:0}
.rxg-head{display:flex;align-items:center;gap:8px;padding:6px 8px;font-size:12px;color:var(--text-1)}
.rxg-caret{background:none;border:0;color:var(--text-muted);cursor:pointer;width:18px;height:18px;line-height:1;font-size:11px;padding:0;flex:none}
.rxg-caret:hover{color:var(--text-1)}
.rxg input[type=checkbox]{width:15px;height:15px;min-width:15px;min-height:15px;flex:none;margin:0}
.rxg-name{flex:1;min-width:0;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rxg-meta{color:var(--text-muted);font-size:10.5px;white-space:nowrap;flex:none}
.rxg-body{padding:0 8px 6px 34px}
.rxg-ch{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:11.5px;color:var(--text-2);cursor:pointer}
.rxg-ch input:disabled{cursor:default}
.rxg-ch .rxg-ch-name{flex:1;min-width:0}

/* .form-input / .adm-input / .adm-select / .amp-input / .amp-select.
   Appearance and width live here so a cold load (before Settings or Admin
   injects its layout sheet) already has the border, padding and token width.
   The number default is declared BEFORE data-field. The
   [type=number][data-field] pairs are more specific, so an explicit size
   wins over the 8ch number default. */
.form-input{
  background:var(--bg-input);border:1px solid var(--border-strong);border-radius:var(--r-sm);
  color:var(--text-1);font-size:0.825rem;padding:0.4rem 0.55rem;min-width:0;
  width:var(--field-w, var(--field-m));max-width:100%;box-sizing:border-box;
  box-shadow:inset 0 1px 2px rgba(0,0,0,0.35);
}
.form-input:focus{outline:none;border-color:var(--accent);box-shadow:inset 0 1px 2px rgba(0,0,0,0.28), 0 0 0 3px var(--accent-soft)}
.form-input::placeholder{color:var(--text-muted)}
.form-input--mono{font-family:var(--font-mono, monospace)}
.form-select{width:fit-content;min-width:var(--field-xs);max-width:min(100%, var(--field-l))}
.form-input[type="number"]{width:var(--field-xs);min-width:2.75rem;max-width:var(--field-l)}
.form-input--sm{--field-w:var(--field-s);width:var(--field-s)}
.form-input--xs{--field-w:var(--field-xs);width:var(--field-xs)}
.form-input[data-field="xs"],.form-input.form-input--xs{--field-w:var(--field-xs);width:var(--field-xs)}
.form-input[data-field="s"],.form-input.form-input--sm{--field-w:var(--field-s);width:var(--field-s)}
.form-input[data-field="sm"]{--field-w:var(--field-sm);width:var(--field-sm)}
.form-input[data-field="m"]{--field-w:var(--field-m);width:var(--field-m)}
.form-input[data-field="l"]{--field-w:var(--field-l);width:var(--field-l)}
.form-input[data-field="fill"],.form-input.form-input--full[data-field="fill"]{--field-w:100%;width:100%}
.form-input[type="number"][data-field="xs"]{width:var(--field-xs);max-width:100%}
.form-input[type="number"][data-field="s"]{width:var(--field-s);max-width:100%}
.form-input[type="number"][data-field="sm"]{width:var(--field-sm);max-width:100%}
.form-input[type="number"][data-field="m"]{width:var(--field-m);max-width:100%}
.form-input[type="number"][data-field="l"]{width:var(--field-l);max-width:100%}
.form-input[type="number"][data-field="fill"],.form-input.form-input--full[data-field="fill"][type="number"]{width:100%;max-width:100%}
/* --grow: content decides the width (trace name, zone picker). align-self
   stops a column flex from stretching width:auto back to the row. */
.form-input--grow{field-sizing:content;width:auto;flex:0 1 auto;min-width:9rem;max-width:100%;align-self:flex-start}
@supports not (field-sizing: content) { input.form-input--grow { width: 18rem; } }
.select.compact.form-input--grow{max-width:min(22rem, 100%)}
.select.compact.form-input--grow .select-value{max-width:100%}

.adm-input{
  height:var(--control-h);padding:0 12px;background:var(--bg-card);border:1px solid var(--border-subtle);
  border-radius:var(--r-sm);color:var(--text-1);font-size:13px;font-family:var(--font-ui);outline:none;
  transition:border-color 120ms ease, box-shadow 120ms ease;box-sizing:border-box;
  min-width:9ch;max-width:100%;width:var(--field-w, var(--field-m));
}
.adm-input--sm{height:30px;font-size:12px}
.adm-input::placeholder{color:var(--text-muted)}
.adm-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent)}
.adm-select,.amp-select{min-width:var(--field-xs);max-width:min(100%, var(--field-l));width:fit-content}
.amp-input{
  height:var(--control-h);padding:0 10px;background:var(--bg-input);border:1px solid var(--border-subtle);
  border-radius:var(--r-sm);color:var(--text-1);font-size:13px;font-family:var(--font-ui);
  outline:none;box-sizing:border-box;min-width:9ch;max-width:100%;width:var(--field-w, var(--field-m));
}
.amp-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent)}

.adm-input[type="number"],.amp-input[type="number"]{width:var(--field-xs)}
.adm-input[data-field="xs"],.adm-select[data-field="xs"],.amp-input[data-field="xs"],.amp-select[data-field="xs"]{width:var(--field-xs)}
.adm-input[data-field="s"],.adm-select[data-field="s"],.amp-input[data-field="s"],.amp-select[data-field="s"]{width:var(--field-s)}
.adm-input[data-field="sm"],.adm-select[data-field="sm"],.amp-input[data-field="sm"],.amp-select[data-field="sm"]{width:var(--field-sm)}
.adm-input[data-field="m"],.adm-select[data-field="m"],.amp-input[data-field="m"],.amp-select[data-field="m"]{width:var(--field-m)}
.adm-input[data-field="l"],.adm-select[data-field="l"],.amp-input[data-field="l"],.amp-select[data-field="l"]{width:var(--field-l)}
.adm-input[data-field="fill"],.adm-select[data-field="fill"],.amp-input[data-field="fill"],.amp-select[data-field="fill"]{width:100%}
.adm-input[type="number"][data-field="xs"],.amp-input[type="number"][data-field="xs"]{width:var(--field-xs)}
.adm-input[type="number"][data-field="s"],.amp-input[type="number"][data-field="s"]{width:var(--field-s)}
.adm-input[type="number"][data-field="sm"],.amp-input[type="number"][data-field="sm"]{width:var(--field-sm)}
.adm-input[type="number"][data-field="m"],.amp-input[type="number"][data-field="m"]{width:var(--field-m)}
.adm-input[type="number"][data-field="l"],.amp-input[type="number"][data-field="l"]{width:var(--field-l)}
/* Phones: an l field takes the row (48ch is wider than a 390px column). */
@media (max-width:480px){.form-input[data-field="l"],.form-input[type="number"][data-field="l"],.adm-input[data-field="l"],.adm-select[data-field="l"],.amp-input[data-field="l"],.amp-select[data-field="l"],.adm-input[type="number"][data-field="l"],.amp-input[type="number"][data-field="l"]{width:100%}}
.adm-input[type="number"][data-field="fill"],.amp-input[type="number"][data-field="fill"]{width:100%}
/* One height for every single-line field, whatever view chrome it carries
   (--control-h, tokens.css; the tap floor on touch). Specificity beats the
   view sheets that load later, so a view cannot make its field taller than
   the button beside it. .adm-input--sm is the one deliberate compact field.
   A date or number box has no intrinsic height of its own once this is set. */
:is(input.input,input.form-input,input.adm-input:not(.adm-input--sm),input.amp-input,input.cost-studio-input,select.input,select.form-select,select.adm-select,select.amp-select,select.cost-studio-select){height:var(--control-h);min-height:var(--control-h);box-sizing:border-box}
:is(textarea.input,textarea.form-input,textarea.adm-input,textarea.cost-studio-textarea){min-height:var(--control-h)}

/* Credit pause (owner decision 2026-10-15): the "Paused: credit used up" card on
   event pages of an org whose credit and 2-hour buffer are used up. It sits ABOVE
   the page (history and downloads stay usable), wraps at phone width, and is
   absent while the slot is empty. */
.credit-lockout-slot:empty{display:none}
.credit-lockout{display:flex;flex-wrap:wrap;align-items:center;gap:10px 16px;margin:8px 16px;padding:12px 14px;border:1px solid var(--warn);border-radius:var(--r-md);background:var(--warn-dim);color:var(--text-1);box-sizing:border-box}
.credit-lockout-body{flex:1 1 240px;min-width:0}
.credit-lockout-title{margin:0 0 2px;font-size:14px;font-weight:600;color:var(--warn);overflow-wrap:anywhere}
.credit-lockout-hint{margin:0;font-size:13px;line-height:1.4;color:var(--text-1);overflow-wrap:anywhere}
.credit-lockout-topup{text-decoration:none;flex:0 0 auto}
@media (max-width:480px){.credit-lockout{margin:8px 16px;padding:10px 12px}.credit-lockout-topup{width:100%}}

/* ── Link FreqCoord event dialog (js/views/freqcoord-link.js) ─────────────────
   One dialog: paste the link, click Link. The pasted link is shown masked, so
   the field is read-only text that must never overflow the 343px phone body:
   everything shrinks (min-width:0), long text ellipsises or wraps. */
.fc-link{display:flex;flex-direction:column;gap:var(--s2);min-width:0}
.fc-link-lead{margin:0 0 var(--s1);color:var(--text-2);font-size:13px;line-height:1.5}
.fc-link-label{font-size:12px;font-weight:600;color:var(--text-1)}
.fc-link-field{display:flex;align-items:center;gap:var(--s2);min-width:0}
.fc-link-input{flex:1 1 auto;min-width:0;text-overflow:ellipsis}
.fc-link-masked{flex:1 1 auto;min-width:0;box-sizing:border-box;padding:0.4rem 0.55rem;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:var(--r-sm);color:var(--text-2);font-family:var(--font-mono,monospace);font-size:0.775rem;line-height:1.45;overflow:hidden;text-overflow:ellipsis;overflow-wrap:normal;word-break:normal}
.fc-link-seg{white-space:nowrap}
.fc-link [hidden]{display:none !important}
.fc-link-input[aria-invalid="true"]{border-color:var(--danger)}
.fc-link-change{flex:0 0 auto}
.fc-link-keyline,.fc-link-hint{margin:0;font-size:12px;line-height:1.5;color:var(--text-muted);overflow-wrap:anywhere}
.fc-link-error{margin:0;padding:var(--s2) var(--s3);font-size:12px;line-height:1.5;color:var(--danger);background:var(--danger-dim);border:1px solid color-mix(in srgb,var(--danger) 40%,transparent);border-radius:var(--r-sm);overflow-wrap:anywhere}
.fc-link-keyblock{display:flex;flex-direction:column;gap:var(--s2);min-width:0}
.fc-link-check{display:flex;align-items:flex-start;gap:var(--s2);margin-top:var(--s2);cursor:pointer;font-size:13px;color:var(--text-1)}
.fc-link-check.fc-link-check input[type="checkbox"]{flex:0 0 auto;width:16px;height:16px;min-width:16px;min-height:16px;transform:none;margin:2px 0 0;accent-color:var(--accent);cursor:pointer}
/* Normal-size box in the theme accent on phones too (the global phone rule scales
   checkboxes 1.25x); the whole label row stays the tap target. */
.fc-link-check-text{display:flex;flex-direction:column;gap:2px;min-width:0}
.fc-link.is-busy{opacity:.85}
.fc-link-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--s2);width:100%}
@media(max-width:480px){
  .fc-link-modal .modal-footer{padding:var(--s3)}
  .fc-link-footer .btn{flex:1 1 0}
  .fc-link-check{min-height:var(--tap-min);padding:var(--s2) 0}
}

/* ── Event toolbar (batch 8) ────────────────────────────────────────────────
   One 44px row under .m-subhdr. Slot order is scope, status, spacer, actions,
   settings, view. --event-toolbar-bg is the root --bg-1, captured here so a
   view that redefines --bg-1 (DECT) cannot paint the row a different colour
   from the event header. top is the header's measured height, never 0.
   33px is the resting .m-subhdr height before ResizeObserver runs.
   z-index 90 sits under .m-subhdr (100) and above in-view sticky heads. */
:root{
  --event-toolbar-bg:var(--bg-1);
  /* 0 until .m-body sets the real inset. A 12px fallback here is padding the
     host does not have: the DECT harness has no .m-body, so the bar was
     1304px in a 1280 viewport (left -12, document scrollWidth 1292). */
  --m-body-pad-x:0px;
  --m-body-pad-top:0px;
}
.event-toolbar{
  display:flex;align-items:center;gap:8px;flex-wrap:nowrap;
  box-sizing:border-box;height:44px;padding:0 12px;flex:none;
  background:var(--event-toolbar-bg, var(--bg-1));
  border-bottom:1px solid var(--border);
  position:sticky;top:var(--event-toolbar-top, 33px);z-index:90;
  min-width:0;max-width:100%;
}
.event-toolbar[hidden],
.event-toolbar [hidden]{display:none !important}
.event-toolbar-scope,
.event-toolbar-actions,
.event-toolbar-settings{display:inline-flex;align-items:center;gap:8px;flex:none;min-width:0}
.event-toolbar-scope{flex:none;overflow:visible}
.event-toolbar-scope > .select,
.event-toolbar-scope > .dm2-nodewrap{flex:none;min-width:max-content;max-width:none}
.event-toolbar-spacer{flex:1 1 auto;min-width:8px}
.event-toolbar [data-slot="view"],
.event-toolbar [data-slot="more"]{flex:none}
.event-toolbar > [data-slot="status"]{
  display:inline-flex;align-items:center;gap:8px;flex:none;
  overflow:visible;flex-wrap:nowrap;min-width:max-content;
}
.event-toolbar .btn{
  height:28px;min-height:28px;padding:0 10px;font-size:12px;flex:none;box-sizing:border-box;
}
.event-toolbar .btn.btn-icon{width:28px;min-width:28px;padding:0}
.event-toolbar .btn[aria-pressed="true"]{
  color:var(--text-1);border-color:var(--accent);background-color:var(--accent-soft);
}
/* The buttons fill the pill's inner height, so the whole pill clicks (no dead zone above and below the label). */
.event-toolbar .segmented.is-compact{
  height:28px;box-sizing:border-box;align-items:stretch;padding:2px;flex:none;
}
.event-toolbar .segmented.is-compact .seg-btn{
  height:auto;min-height:0;padding:0 9px;font-size:12px;align-self:stretch;
}
.event-toolbar .select.compact{
  height:28px;min-height:28px;padding-top:0;padding-bottom:0;box-sizing:border-box;
  flex:none;min-width:max-content;max-width:none;
}
.event-toolbar .select.compact .select-value{
  display:block;min-width:max-content;max-width:none;overflow:visible;text-overflow:clip;white-space:nowrap;
}
.event-toolbar .dm2-nodewrap{display:flex;min-width:max-content}
.event-toolbar .dm2-chip{
  height:28px;min-height:28px;min-width:max-content;max-width:none;
  overflow:visible;text-overflow:clip;white-space:nowrap;flex:none;
}
.event-toolbar .chip,
.event-toolbar .pill{
  white-space:nowrap;flex:none;overflow:visible;text-overflow:clip;max-width:none;
  align-self:center;justify-content:flex-start;
}
.event-toolbar .chip{height:26px;min-height:26px}
.event-toolbar .pill{height:28px;min-height:0}
/* Neutral until a view marks the pill live. Green is is-live only. */
.event-toolbar > .dm2-status,
.event-toolbar > .wifi-status{
  gap:6px;height:24px;padding:0 9px;border:0;border-radius:var(--r-pill);
  font-size:12px;font-weight:500;white-space:nowrap;overflow:visible;flex:none;
  background:var(--bg-2);color:var(--text-2);
}
/* Phone: a status that will not fit becomes the dot. The name is aria-label + title. */
.event-toolbar.is-status-icon > .dm2-status{
  width:24px;min-width:24px;padding:0;justify-content:center;
}
.event-toolbar > .wifi-status.is-live{
  background:var(--live-soft);color:var(--live);
}
.event-toolbar > .dm2-status::before,
.event-toolbar > .wifi-status > .dot{
  content:'';width:7px;height:7px;border-radius:50%;background:currentColor;flex:none;
}
.event-toolbar > .wifi-status > .dot{display:block}
.event-toolbar > .dm2-status.is-down{
  background:var(--danger-soft);color:var(--danger);
}
/* Bleed uses the same padding variables as .m-body and .dm2. RX sits above
   both columns and uses the same horizontal bleed as Wi-Fi. */
.dm2 > .event-toolbar{
  margin-top:calc(-1 * (var(--m-body-pad-top, 0px) + var(--dm2-pad, 12px)));
  margin-left:calc(-1 * (var(--m-body-pad-x, 0px) + var(--dm2-pad, 12px)));
  margin-right:calc(-1 * (var(--m-body-pad-x, 0px) + var(--dm2-pad, 12px)));
  width:calc(100% + 2 * (var(--m-body-pad-x, 0px) + var(--dm2-pad, 12px)));
  max-width:none;
}
/* .m-body is overflow:auto, which traps sticky. switchMode adds this class
   only while DECT, Wi-Fi or RX is the active tab. A hidden tab's toolbar
   must not change it. */
.m-body.m-body--toolbar{overflow:visible}
.wifi > .event-toolbar{
  margin-top:calc(-1 * var(--m-body-pad-top, 0px));
  margin-left:calc(-1 * var(--m-body-pad-x, 0px));
  margin-right:calc(-1 * var(--m-body-pad-x, 0px));
  width:calc(100% + 2 * var(--m-body-pad-x, 0px));
  max-width:none;
}
.rxv > .event-toolbar{
  margin-top:calc(-1 * var(--m-body-pad-top, 0px));
  margin-left:calc(-1 * var(--m-body-pad-x, 0px));
  margin-right:calc(-1 * var(--m-body-pad-x, 0px));
  width:calc(100% + 2 * var(--m-body-pad-x, 0px));
  max-width:none;
}
/* Same full-bleed on the tabs the rollout added. A direct-child rule would
   miss a bar nested one level under the mount (replay, map, health). */
.m-replay-mount .event-toolbar,
.m-analyzers-mount .event-toolbar,
.m-usage-mount .event-toolbar,
.m-map-mount .event-toolbar,
.m-spatial-mount .event-toolbar,
.m-clock-mount .event-toolbar,
.m-health-mount .event-toolbar,
.m-audiomap-mount .event-toolbar,
.m-warroom-mount > .wr-warroom > .event-toolbar,
.m-ask-mount > .event-toolbar{
  margin-top:calc(-1 * var(--m-body-pad-top, 0px));
  margin-left:calc(-1 * var(--m-body-pad-x, 0px));
  margin-right:calc(-1 * var(--m-body-pad-x, 0px));
  width:calc(100% + 2 * var(--m-body-pad-x, 0px));
  max-width:none;
}
.map-standalone-head{display:flex;align-items:center;gap:8px;min-width:0;padding:8px 12px}
.map-standalone-head .page-header-title{margin:0}
/* Standalone #/map has no event tab strip. A leftover --event-toolbar-top
   from an event shell would pad the map bar down from the top of the page. */
.map-standalone-head,
.map-view:has(.map-standalone-head){--event-toolbar-top:0px}
/* The fold (.is-narrow) is set by event-toolbar.js: at <=600px, and whenever
   the row would not fit its measured width (601px up to the content width).
   These rules follow the class, not a media query. */
.event-toolbar.is-narrow{gap:6px}
/* The nodes chip is in the ⋯ menu. display:contents drops the empty wrap's gap.
   The popover then anchors to the sticky toolbar. */
.event-toolbar.is-narrow .dm2-nodewrap{display:contents}
.event-toolbar.is-narrow .dm2-pop{position:absolute;top:calc(100% + 4px);left:10px;z-index:30}
/* Doubled class beats .rxv .chip {display:inline-flex} (views-rx.css, later file). */
.event-toolbar.is-narrow .rxv-vendor-mix.rxv-vendor-mix{display:none}
/* Last resort, set by event-toolbar.js only when the folded row still does not
   fit: a long status ("Stale · no data for 12 min (simulated)") or area name
   shortens with an ellipsis instead of pushing the page sideways. */
.event-toolbar.is-tight > [data-slot="status"],
.event-toolbar.is-tight .event-toolbar-scope > .select{min-width:0;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis}
.event-toolbar.is-tight .event-toolbar-scope{flex:0 1 auto;min-width:0}
.event-toolbar.is-tight .event-toolbar-scope > .select .select-value{min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis}
@media (max-width:600px){
  .event-toolbar{gap:6px;padding-left:10px;padding-right:10px}
}
/* Touch: the same `@media (pointer:coarse), (max-width:760px)` gate as the
   platform tap rule above. The 28px compaction above is desktop-only: here the
   toolbar controls take the kit's --tap-min floor (44px) instead. Same
   specificity as the 28px rules, later in the file, so the floor wins.
   The row grows 4px above and below so the 44px controls do not touch its border. */
@media (pointer:coarse), (max-width:760px){
  .event-toolbar{height:calc(var(--tap-min) + 9px);padding-top:4px;padding-bottom:4px}
  .event-toolbar .btn,
  .event-toolbar .select.compact,
  .event-toolbar .dm2-chip{min-height:var(--tap-min)}
  .event-toolbar .btn.btn-icon{min-width:var(--tap-min)}
  .event-toolbar .segmented.is-compact{height:auto;min-height:var(--tap-min);padding:0}
  .event-toolbar .segmented.is-compact .seg-btn{min-height:var(--tap-min);min-width:var(--tap-min)}
}

.board-st-sel{min-height:24px}
.dt th.num,.dt td.num{text-align:right}
.dt td.num{font-family:var(--font-mono)}
