:root {
  /* Light theme — the general feel of the app. */
  --bg: #f5f3ee;           /* warm cream page background */
  --panel: #ffffff;        /* card / panel surfaces */
  --panel-2: #eef2f6;      /* inset / hover surfaces */
  --border: #d6dbe2;       /* subtle borders */
  --text: #0f172a;         /* primary text */
  --muted: #5b6573;        /* secondary text, labels */
  --accent: #0ea5e9;       /* primary CTA (sky) */
  --accent-text: #ffffff;  /* text drawn on top of --accent */
  --danger: #dc2626;       /* end-session / delete */
  --ok: #16a34a;           /* success / wake-lock held */

  /* Dark island tokens — used for surfaces that should stay dark even in
     light theme: map overlays (legible over the bright OSM tiles), and the
     donut chart container (slice colors pop better against dark). */
  --dark-bg: #0b1220;
  --dark-panel: #111a2f;
  --dark-text: #f8fafc;
  --dark-muted: #94a3b8;
  --dark-border: #1f2a44;

  --radius: 12px;
}

/* Patrick Hand — self-hosted (calendar handwritten numbers). */
@font-face {
  font-family: 'Patrick Hand';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/patrick-hand-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Afacad — self-hosted, used by the multi-session picker cards. */
@font-face {
  font-family: 'Afacad';
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/afacad-700italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Dymo (embossed label font, Letraset) — self-hosted, used by the top-menu tabs. */
@font-face {
  font-family: 'Dymo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/dymo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

/* ---- View Transitions API ---- */
/* Iris reveal between the Record / Sessions / detail views: the OLD view holds
   still underneath while the NEW view irises open from a growing circle clipped
   over the top of it. (Replaces the browser default cross-fade.) The circle is
   centred on the tap point (--iris-x / --iris-y, set per-switch in withTransition;
   defaults to centre) and grows to 150% so it always clears the screen corners
   on any aspect ratio, even from a corner tap (worst case needs ~141%).
   prefers-reduced-motion / the transitions setting still bypass this entirely
   (see the media query below and withTransition in app.js). */
::view-transition-old(root) {
  animation: none;                 /* old view stays put beneath the iris */
}
::view-transition-new(root) {
  animation: iris-reveal 520ms cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes iris-reveal {
  from { clip-path: circle(0% at var(--iris-x, 50%) var(--iris-y, 50%)); }
  to   { clip-path: circle(150% at var(--iris-x, 50%) var(--iris-y, 50%)); }
}
/* The shared-element morph (multi-session picker card ⇆ detail header) keeps its
   own slightly longer timing so the flying header reads clearly. */
::view-transition-group(*) {
  animation-duration: 700ms;
  animation-timing-function: cubic-bezier(0.2, 1, 0.36, 1);
}

/* While a transition runs, the API paints snapshot pseudo-elements in an
   overlay on top of the page; by default that overlay intercepts taps, so the
   app feels frozen for the animation's duration even though the real DOM is
   already in its final state underneath. Let pointer events pass straight
   through the overlay to the live UI so taps register mid-animation. The only
   interactive controls reachable during a swap (topbar tabs + hamburger) are
   fixed and don't move, so there's no mis-click risk. */
::view-transition {
  pointer-events: none;
}

/* NOTE: Settings does NOT use the View Transitions API. It's a slide-over
   drawer driven by a plain CSS transition on transform (see #view-settings
   below), precisely so it can be interrupted mid-slide and reversed smoothly --
   which the View Transitions API cannot do (it plays fixed keyframes to the end
   and snaps on re-entry). The rules above only govern the tracking / sessions /
   detail cross-fades. */

/* Honour OS-level reduced-motion preference: skip all transition animations
   so the swap is instant. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Hide scrollbars across the whole app (keep scroll behavior). Cleaner
   mobile feel; matches how PWAs typically present. */
* {
  scrollbar-width: none;       /* Firefox */
  -ms-overflow-style: none;    /* IE / legacy Edge */
}
*::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;               /* Chrome / Safari / Webkit Android */
}
html, body { height: 100%; margin: 0; }
/* The root element has no background of its own: normally it sits behind <body>,
   and during a session it becomes the fullscreen element (see enterImmersive in
   app.js). Its default canvas is WHITE, which leaks out as a 1px hairline at the
   very top edge on some devices -- invisible on the cream views but obvious on
   every dark screen (and the fullscreen session overlay). Paint it black: <body>'s
   opaque cream fully covers it on the light views (verified), so the black only
   ever shows through on the dark screens, which is exactly where we want it. */
html { background: #000; }
:fullscreen::backdrop { background: #000; }
body {
  font: 15px/1.4 system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
  padding-top: env(safe-area-inset-top);
}

.topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 56px;
  display: flex;
  align-items: center;
  gap: 16px;                     /* doubled spacing between Record / Sessions */
  padding: 0 12px;
  background: transparent;       /* float the buttons over the content, no bar */
  z-index: 1000;
  padding-top: env(safe-area-inset-top);
  pointer-events: none;          /* let taps fall through the empty bar... */
}
/* ...but keep the tabs themselves interactive. (The hamburger is no longer a
   topbar child — see below — so it doesn't need this.) */
.topbar .tab { pointer-events: auto; }
.topbar .brand { color: var(--muted); font-weight: 600; letter-spacing: 0.5px; }

/* Settings (hamburger) button. Lives OUTSIDE the topbar in the DOM and is
   fixed-positioned at top-right with a z-index ABOVE the Settings drawer
   (z 1100), so when the drawer slides up over the whole topbar it still floats
   on top and can be tapped to close. Rendered as a filled white circle with a
   thin black border so the icon stays legible over any background (splash
   art, map, or the cream drawer). */
.hamburger-btn {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 9px);
  right: 12px;
  z-index: 1200;
  width: 38px;
  height: 38px;
  background: #fff;
  border: 1px solid #000;
  border-radius: 50%;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.hamburger-btn:active { opacity: 0.6; }       /* subtle press feedback only */
.hamburger-btn.active { color: var(--accent); } /* tint the icon when settings is open */
#wake-indicator {
  margin-left: auto;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #cbd5e1;
  box-shadow: 0 0 0 2px rgba(203,213,225,0.4);
  transition: background-color 200ms ease, box-shadow 200ms ease;
}
#wake-indicator.on {
  background: var(--ok);
  box-shadow: 0 0 0 2px rgba(22,163,74,0.25), 0 0 10px rgba(22,163,74,0.45);
}
.tab {
  background: #ffffff;
  color: #000000;              /* inactive: black embossed lettering */
  border: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-family: 'Dymo', system-ui, sans-serif;
  font-size: 2.4rem;
  letter-spacing: 0.5px;
  line-height: 0.6;
  -webkit-user-select: none;   /* label text is not selectable */
  user-select: none;
}
/* Active tab: the embossed lettering tinted olive green instead of black. */
.tab.active { color: #6b7a2e; }

.view {
  display: none;
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  padding-top: env(safe-area-inset-top);
}
.view.active { display: block; }
/* Settings = slide-over drawer (NOT one of the cross-faded .view screens).
   It stays in the DOM permanently, parked off-screen to the right, and slides
   in/out via a CSS transition on transform. Toggled by .open from app.js.
   - Live element + CSS transition => fully interruptible: tapping the hamburger
     mid-slide reverses it smoothly from wherever it is, and it stays clickable
     the whole time (no View Transitions overlay swallowing taps).
   - display:block overrides .view's display:none so it's always rendered (you
     cannot CSS-transition out of display:none).
   - top:0 + z 1100 covers the WHOLE screen including the topbar tabs (z 1000),
     so the menu disappears under the drawer. The hamburger lives above it
     (z 1200, see .hamburger-btn) so you can still tap it to close.
   - visibility is delayed to the end of the slide-OUT (so the panel stays
     visible while it leaves) but applied immediately on open; it keeps the
     parked panel out of tab order / hit-testing when closed. */
#view-settings {
  display: block;
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  z-index: 1100;
  /* Tiled cardboard texture (50x50 seamless SVG). Opaque tan fill keeps the
     drawer covering the content beneath it; the color is also a fallback. */
  background-color: #D8C09A;
  background-image: url('assets/cardboard.svg');
  background-repeat: repeat;
  background-size: 50px 50px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  transform: translateX(100%);
  visibility: hidden;
  pointer-events: none;
  transition: transform 320ms cubic-bezier(0.2, 1, 0.36, 1),
              visibility 0s linear 320ms;
}
#view-settings.open {
  transform: translateX(0);
  visibility: visible;
  pointer-events: auto;
  transition: transform 320ms cubic-bezier(0.2, 1, 0.36, 1),
              visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  #view-settings { transition: none; }
}

.map { width: 100%; height: 100%; background: #e6e3dc; }

.hud {
  position: absolute;
  top: 12px;
  right: 12px;
  display: flex;
  gap: 8px;
  pointer-events: none;
  z-index: 500;
  max-width: calc(100vw - 24px);
}
.hud-item:nth-child(2) { min-width: 110px; } /* intensity bar needs room */
/* HUD chips are dark islands over the light OSM map so they stay readable. */
.hud-item {
  background: rgba(11, 18, 32, 0.85);
  border: 1px solid var(--dark-border);
  border-radius: var(--radius);
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  color: var(--dark-text);
}
.hud-item .label { color: var(--dark-muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
.hud-item > :last-child { font-weight: 600; font-variant-numeric: tabular-nums; font-size: 16px; }
.intensity-bar {
  display: inline-block;
  width: 100%;
  height: 10px;
  background: rgba(255,255,255,0.08);
  border-radius: 6px;
  overflow: hidden;
  margin-top: 4px;
}
.intensity-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--ok);
  transition: width 200ms ease, background-color 200ms ease;
}

/* Tracking view = splash screen with a randomly-picked image as background
   and a single centered Start button on top. JS sets the background-image
   inline whenever the view becomes active (see setRandomSplash). */
#view-tracking {
  background-color: var(--bg);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.splash-actions {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: calc(40px + env(safe-area-inset-bottom));
}
/* The FlipBoard skateboard-deck button. FlipBoard sizes its inner board to
   100% of the host, so the host must have an explicit aspect ratio. The
   deck PNG is 3.896:1 (long way). */
.flipboard-deck {
  width: min(72vw, 320px);
  aspect-ratio: 3.896 / 1;
}

.controls {
  position: absolute;
  bottom: 24px;
  left: 0; right: 0;
  display: flex;
  justify-content: center;
  gap: 12px;
  z-index: 500;
  padding-bottom: env(safe-area-inset-bottom);
  pointer-events: none;
}
.controls > * { pointer-events: auto; }
.controls button {
  font: inherit;
  font-weight: 600;
  border: none;
  border-radius: 999px;
  padding: 14px 28px;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.18);
}
.primary { background: var(--accent); color: var(--accent-text); }
.danger { background: var(--danger); color: white; }

/* Active-session overlay: full-screen black with one big tap target.
   Sits above everything else so the click can't be intercepted. */
#session-overlay {
  position: fixed;
  inset: 0;
  background: #000;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
}
#session-overlay[hidden] { display: none; }
/* Stop screen — full-screen black, slide-both-ends to end session.
   Everything here is unselectable so Android's text-selection actions can't
   trigger and so accidental long-presses do nothing. */
#session-overlay,
#session-overlay * {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
.stop-screen {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 20px;
  padding-top: calc(20px + env(safe-area-inset-top));
  padding-bottom: calc(20px + env(safe-area-inset-bottom));
  color: #fff;
}
/* Stop-screen text + handles use flat colors with no glows so the surrounding
   pixels stay black on OLED. The screen needs to feel calm and battery-cheap,
   not punchy. */
.stop-title {
  font-weight: 900;
  font-size: 64px;
  letter-spacing: 12px;
  color: var(--danger);
  text-align: center;
}
.stop-instruction {
  color: #94a3b8;
  font-size: 14px;
  letter-spacing: 0.6px;
  text-align: center;
  max-width: 320px;
}
.stop-slider {
  position: relative;
  width: min(86vw, 360px);
  height: 92px;
  touch-action: none;          /* don't let the browser steal the drag */
  user-select: none;
}
.stop-track {
  position: absolute;
  top: 50%;
  left: 12px;
  right: 12px;
  height: 6px;
  background: rgba(255,255,255,0.15);
  border-radius: 3px;
  transform: translateY(-50%);
}
.stop-end {
  position: absolute;
  top: 50%;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(255,255,255,0.18);
  border: 2px solid rgba(255,255,255,0.3);
  transform: translate(-50%, -50%);
  transition: background 200ms ease, border-color 200ms ease, box-shadow 200ms ease;
}
.stop-end-left  { left: 0%; }
.stop-end-right { left: 100%; }
.stop-end.touched {
  background: #16a34a;
  border-color: #ffffff;
  /* no glow — keeps surrounding pixels black */
}
.stop-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 70px;
  height: 70px;
  border-radius: 50%;
  background: var(--danger);
  border: 3px solid #ffffff;
  /* no glow — flat fill so OLED pixels around the handle stay off */
  transform: translate(-50%, -50%);
  cursor: grab;
  touch-action: none;
  will-change: left;
}
.stop-handle:active { cursor: grabbing; }
.stop-handle.armed {
  background: #16a34a;
  border-color: #ffffff;
  /* no glow */
}

/* Live status row on the stop screen — glance-able before pocketing. */
.stop-status {
  margin-top: 28px;
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 13px;
  color: #94a3b8;
  letter-spacing: 0.3px;
}
.stop-status .status-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 999px;
  min-height: 36px;
}
.stop-status .status-label {
  color: #94a3b8;
  font-weight: 500;
}
.stop-status .status-value, .stop-status b {
  color: #f8fafc;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.stop-status .status-sep { color: #475569; padding: 0 2px; }
.stop-status .status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #475569;
  display: inline-block;
  /* no surrounding glow ring — kept flat for OLED black pixels */
}
.stop-status .status-dot.on {
  background: #16a34a;
}
.stop-status .status-dot.warn {
  background: #dc2626;
  animation: status-warn-pulse 1.2s ease-in-out infinite;
}
@keyframes status-warn-pulse {
  /* opacity-only pulse — no glow, so no surrounding lit pixels */
  0%, 100% { opacity: 1;   }
  50%      { opacity: 0.4; }
}
.stop-status .status-item.warn .status-label,
.stop-status .status-item.warn .status-value,
.stop-status .status-item.warn b { color: #fca5a5; }
.rewake-btn {
  margin-left: 6px;
  padding: 5px 10px;
  font: inherit;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.4px;
  background: #ef4444;
  color: #fff;
  border: none;
  border-radius: 999px;
  cursor: pointer;
}
.rewake-btn:active { background: #b91c1c; }

/* Settings view */
#view-settings { overflow-y: auto; }
.settings-page {
  max-width: 480px;
  margin: 0 auto;
  /* Extra top padding clears the floating hamburger (which now sits ABOVE the
     drawer at top-right) so the sub-tab row never tucks under it. The drawer's
     own .view safe-area padding handles the notch on top of this. */
  padding: 56px 16px 24px;
}
.settings-page h2 { margin-top: 0; }

/* Sub-tab navigation inside the Settings drawer (Settings / Share / About).
   Styled to match the top-bar tabs: Dymo embossed font, white tape body,
   black when inactive and olive green when active (color alone marks the
   selection, no underline). */
.subtabs {
  display: flex;
  gap: 16px;
  margin: 0 0 18px 0;
}
.subtab {
  background: #ffffff;
  border: none;
  padding: 0;
  font: inherit;
  font-family: 'Dymo', system-ui, sans-serif;
  font-size: 2.5rem;
  letter-spacing: 0.5px;
  line-height: 0.6;
  color: #000000;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.subtab.active { color: #6b7a2e; }
.subtab:active { opacity: 0.6; }

/* Only the selected pane shows. JS also sets the [hidden] attribute, but this
   keeps display correct even before scripts run. */
.settings-pane:not(.active) { display: none; }

/* About pane lead-in (app name) above the muted description. */
.setting-about-lead { margin: 0 0 4px; font-weight: 700; font-size: 18px; }

.setting-group {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 0 0 12px 0;
}
.setting-group legend { padding: 0 6px; color: var(--muted); font-size: 13px; }
.stance-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.toggle-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 6px;
  cursor: pointer;
}
.toggle-row input { accent-color: var(--accent); transform: scale(1.2); margin-top: 4px; }
.stance-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  background: var(--panel);
  border-radius: 8px;
  cursor: pointer;
  min-width: 0;
}
.stance-option:hover { background: var(--panel-2); }
.stance-option:has(input:checked) {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.stance-option input { accent-color: var(--accent); transform: scale(1.2); flex: 0 0 auto; }
.setting-hint { margin: 10px 4px 0; font-size: 12px; }
.version-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 6px 4px;
  gap: 12px;
}
.version-row + .version-row { border-top: 1px solid var(--border); }
.version-row .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
.setting-version .setting-hint { margin-top: 12px; font-style: italic; }
.data-btn {
  display: block;
  width: 100%;
  padding: 12px 14px;
  margin: 8px 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 8px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.data-btn:active { background: var(--border); }

/* Palette picker — list of tappable rows, each with a name and a live
   strip of color stops. Active row gets an accent ring. */
.palette-options {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.palette-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  font: inherit;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  text-align: left;
  transition: border-color 120ms ease, background 120ms ease, box-shadow 120ms ease;
}
.palette-row:hover { background: var(--panel-2); }
.palette-row.active {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.palette-name { flex: 0 0 auto; min-width: 96px; font-size: 13px; }
.palette-swatches {
  flex: 1 1 auto;
  display: flex;
  height: 14px;
  border-radius: 4px;
  overflow: hidden;
  border: 1px solid var(--border);
}
.palette-chip { flex: 1; }

/* Share (inside Settings) */
.setting-share { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.setting-share .setting-hint { text-align: center; }
#share-qr {
  background: #fff;
  padding: 16px;
  border-radius: 12px;
  width: min(280px, 80vw);
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
#share-qr svg { width: 100%; height: 100%; display: block; }
.share-url-btn {
  font: inherit;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 14px;
  word-break: break-all;
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  width: 100%;
  padding: 12px 14px;
  border-radius: 8px;
  cursor: pointer;
}
.share-url-btn:active { background: var(--panel-2); }
.share-native {
  width: 100%;
  padding: 14px 22px;
  border-radius: 999px;
  border: none;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.ghost { background: transparent; color: var(--text); border: 1px solid var(--border); padding: 6px 12px; border-radius: 8px; cursor: pointer; font: inherit; }
.danger-text { color: var(--danger); }
.hidden { display: none !important; }


.banner {
  position: absolute;
  bottom: 100px;
  left: 16px; right: 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  z-index: 500;
}
.banner p { margin: 0; }

/* Sessions view = paper calendar (the-cal.js). On a phone the calendar
   image should cover the full screen with no scrolling — any overflow at
   the bottom is cropped. On desktop the calendar is capped to a sensible
   width and centered. */
#view-sessions { overflow: hidden; }
.sessions-page {
  padding: 16px;             /* calendar bleeds up behind the floating menu */
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  overflow: hidden;
}
.sessions-cal {
  width: 100%;
  max-width: 460px;     /* desktop cap */
}
/* Phone widths: zero padding, no overflow, calendar bleeds edge to edge
   and any image height past the viewport is clipped. */
@media (max-width: 500px) {
  .sessions-page { padding: 0; gap: 0; }
  .sessions-cal { max-width: none; }
}
.sessions-empty {
  text-align: center;
  font-size: 14px;
  padding: 0 16px;
}

/* LOST SESSION recovery poster — full-screen "torn ticket" takeover shown on
   launch when an auto-saved in-progress session is found (crash / pocket-swipe /
   OS-killed tab). Dark backdrop is inert; the user must choose Review or Trash
   it. Reuses the app's day-picker visual language: yellow card, red Afacad
   lettering with a sharp white offset shadow. */
.recovery-poster {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.78);     /* same dark overlay as the day picker */
  z-index: 9999;                       /* above homing / session overlays */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.recovery-poster[hidden] { display: none; }

.recovery-poster-card {
  width: min(420px, 100%);
  max-height: calc(100vh - 32px);
  overflow-y: auto;                    /* scroll on short screens, never clip */
  background: #FFEA00;                 /* poster yellow (matches day cards) */
  border: 2px solid #1a1a1a;
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.35);
  padding: 28px 26px 0;                /* actions row owns the bottom padding */
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  color: #1a1a1a;
}

/* Supplied SVG art. Sized by width; height follows each file's aspect ratio. */
.rp-title { width: 88%; max-width: 320px; height: auto; display: block; }
.rp-phone { width: 42%; max-width: 150px; height: auto; display: block; margin: 14px 0 4px; transform: rotate(-15deg); }

.rp-lead {
  font-family: 'Afacad', system-ui, sans-serif;
  font-style: italic;
  font-weight: 700;
  font-size: 22px;
  line-height: 1.1;
  margin: 14px 0 4px;
}
.rp-sub {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;  /* matches share-url */
  font-size: 15px;
  line-height: 1.25;
  margin: 0 0 18px;
}

/* Label / value rows. Labels right-aligned, values bold and left-aligned, so
   the colon column lines up like the mockup. */
.rp-meta {
  display: grid;
  grid-template-columns: auto auto;
  column-gap: 10px;
  row-gap: 4px;
  align-items: baseline;
  margin: 0 0 22px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;  /* matches share-url */
  font-size: 17px;
}
.rp-meta dt { text-align: right; }
.rp-meta dd { margin: 0; text-align: left; font-weight: 700; }

/* Torn-ticket perforation: a dashed rule across the card, then the two action
   zones split by a dashed vertical divider. */
.rp-actions {
  width: calc(100% + 52px);            /* bleed back over the card's padding */
  margin: 0 -26px;
  border-top: 2px dashed #1a1a1a;
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.rp-btn {
  background: transparent;
  border: 0;
  padding: 22px 8px;
  cursor: pointer;
  font-family: 'Afacad', system-ui, sans-serif;
  font-style: italic;
  font-weight: 700;
  font-size: 30px;
  line-height: 1;
  color: #D1341E;                      /* poster red */
  text-shadow: 2px 2px 0 #ffffff;      /* sharp white offset, no blur */
}
.rp-btn + .rp-btn { border-left: 2px dashed #1a1a1a; }
.rp-btn:active { filter: brightness(0.9); }

/* Multi-session day picker. A small modal sheet that appears when a tapped
   day has more than one session, so the user can choose which to open. */
#day-picker {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.78);     /* darker overlay */
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
#day-picker[hidden] { display: none; }
.day-picker-card {
  /* No card chrome — the yellow cards float directly on the dark overlay. */
  background: transparent;
  border: none;
  border-radius: 0;
  width: min(420px, 100%);
  /* Constrain to viewport so it never gets pushed off-screen on small
     phones. The list inside scrolls when there are more sessions than fit. */
  max-height: calc(100vh - 32px);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.day-picker-card h3 {
  margin: 0 0 4px;
  font-family: 'Afacad', system-ui, sans-serif;
  font-style: italic;
  font-weight: 700;
  font-size: 32px;
  line-height: 1;
  color: #D1341E;
  text-align: left;
  text-shadow: 2px 2px 0 #ffffff;       /* same treatment as the times */
}
/* Day-picker list = stack of yellow Afacad cards. Numbers right-aligned so
   you can scan TIME / DURATION columns vertically across cards. Scrolls
   internally when there are more sessions than fit on screen. */
.day-picker-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  perspective: 800px;            /* gives the rotateX flip some depth */
  flex: 1 1 auto;                /* take all remaining space in the modal */
  min-height: 0;                 /* allow shrinking below content size  */
  overflow-y: auto;              /* scroll when there's overflow         */
}
.dpc-card {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  align-items: center;
  gap: 18px;
  padding: 8px 20px;
  background: #FFEA00;
  color: #D1341E;
  border: none;
  border-radius: 0;          /* sharp corners */
  font-family: 'Afacad', system-ui, sans-serif;
  font-style: italic;
  font-weight: 700;
  cursor: pointer;
  text-align: left;
  transform-origin: center;
  transform-style: preserve-3d;
  /* drop-shadow (not box-shadow) so the shadow follows the tilted outline
     in 3D — looks more natural than a rectangular box-shadow when each
     card is rotated around its Y axis. */
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.55));
  /* Spin-in entrance. Cascaded with animation-delay below. */
  opacity: 0;
  animation: dpcEnter 700ms cubic-bezier(0.2, 1, 0.36, 1) forwards;
}
.dpc-card:active { filter: brightness(0.96); }
/* Each .dpc-card is wrapped in an <li>, so the nth-child selector needs to
   target the <li> for the cascade to land on different cards. */
.day-picker-list > li:nth-child(1)    .dpc-card { animation-delay: 0ms;    }
.day-picker-list > li:nth-child(2)    .dpc-card { animation-delay: 200ms;  }
.day-picker-list > li:nth-child(3)    .dpc-card { animation-delay: 400ms;  }
.day-picker-list > li:nth-child(4)    .dpc-card { animation-delay: 600ms;  }
.day-picker-list > li:nth-child(5)    .dpc-card { animation-delay: 800ms;  }
.day-picker-list > li:nth-child(6)    .dpc-card { animation-delay: 1000ms; }
.day-picker-list > li:nth-child(7)    .dpc-card { animation-delay: 1200ms; }
.day-picker-list > li:nth-child(8)    .dpc-card { animation-delay: 1400ms; }
.day-picker-list > li:nth-child(9)    .dpc-card { animation-delay: 1600ms; }
.day-picker-list > li:nth-child(10)   .dpc-card { animation-delay: 1800ms; }
.day-picker-list > li:nth-child(n+11) .dpc-card { animation-delay: 2000ms; }

@keyframes dpcEnter {
  /* Each card spins 180° around the Y axis to land at its final --tilt-y
     position. Mid-spin (≈ +90° from final) the card is edge-on, which gives
     a natural "reveal" moment. Dark + invisible at start, yellow once
     facing forward. */
  0%   { opacity: 0; transform: perspective(800px) rotateY(calc(var(--tilt-y, 0deg) + 180deg)); background-color: #1a1a1a; color: #f8fafc; }
  100% { opacity: 1; transform: perspective(800px) rotateY(var(--tilt-y, 0deg));               background-color: #FFEA00; color: #D1341E; }
}

/* Each "column" inside the card is a label stacked above a right-aligned
   value. The label sits at the column's left edge; the value sits on the
   column's right edge so the digit columns line up across multiple cards
   when the picker has more than one session. */
.dpc-col {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  min-width: 0;
}
.dpc-label {
  font-size: 14px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: left;
  line-height: 1;
}
.dpc-value {
  font-size: 32px;
  font-variant-numeric: tabular-nums;
  text-align: left;
  line-height: 1;
  text-shadow: 2px 2px 0 #ffffff;   /* sharp white offset, down-right, no blur */
}
.dpc-arrow {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  display: block;
}

#day-picker .ghost {
  align-self: flex-end;
  background: #ffffff;
  color: #0f172a;
  border-color: #ffffff;
}

/* Detail view */
#view-detail { overflow-y: auto; }
.detail-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
}
.detail-header h2 { margin: 0; font-size: 16px; flex: 1; text-align: center; }
#playback-wrap {
  position: relative;
  height: 320px;
  background: #111827;
}
#map-detail { height: 100%; }
#playback-wrap:fullscreen { width: 100vw; height: 100vh; }
#playback-wrap:-webkit-full-screen { width: 100vw; height: 100vh; }
.map-corner-btn {
  position: absolute;
  top: 66px;          /* clear the floating topbar buttons (56px bar + 10) */
  right: 10px;
  width: 38px; height: 38px;
  border-radius: 8px;
  /* Dark island so the icon stays legible over the light OSM map */
  background: rgba(11, 18, 32, 0.92);
  border: 1px solid var(--dark-border);
  color: var(--dark-text);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1000;
  padding: 0;
}
.map-corner-btn:active { background: var(--dark-panel); }
.detail-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  padding: 12px;
}
.detail-stats > div {
  background: var(--panel);
  border-radius: var(--radius);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.detail-stats .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
.detail-stats select {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  font: inherit;
}
.speed-buttons {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.speed-buttons button {
  flex: 1;
  padding: 4px 10px;
  background: var(--panel-2);
  color: var(--muted);
  border: none;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  border-right: 1px solid var(--border);
}
.speed-buttons button:last-child { border-right: none; }
.speed-buttons button.active { background: var(--accent); color: var(--accent-text); }

/* Combined Playback + Speed cell — spans both columns of the detail stats.
   Inside the cell: two equal columns. Speed segmented control fills the
   left column; play+stop fill the right column. */
.detail-stats > .playback-cell { grid-column: 1 / -1; }
.playback-bar {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 4px;
  align-items: stretch;
}
.playback-bar .speed-buttons {
  display: flex;
  width: 100%;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.playback-bar .speed-buttons button { flex: 1; padding: 0; height: 100%; }

.play-stop-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.icon-btn {
  width: 100%;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: #000000;
  border: 1px solid #000000;
  color: #ffffff;
  cursor: pointer;
  padding: 0;
  transition: background 120ms ease, color 120ms ease;
}
.icon-btn:active { background: #1f2937; }
.icon-btn .ic-pause { display: none; }
.icon-btn.play-btn.is-playing .ic-play  { display: none; }
.icon-btn.play-btn.is-playing .ic-pause { display: inline-block; }
.icon-btn:disabled {
  background: #ffffff;
  color: #000000;
  opacity: 0.35;
  cursor: not-allowed;
}

/* Soundwave spans the full content width so bars get as much room as possible. */
.donut-wrap {
  position: relative;
  margin: 8px 12px 0;
}
.chart-caption {
  text-align: center;
  padding: 10px 12px 0;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
#donut { width: 100%; height: auto; display: block; }
.muted { color: var(--muted); font-size: 12px; }

.maneuver-counts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
  padding: 0 12px 12px;
}
.maneuver-counts .mc-item {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 10px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.maneuver-counts b { margin-left: auto; font-variant-numeric: tabular-nums; }
.mc-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.mc-dot.just { background: #38bdf8; }
.mc-dot.fakie { background: #a855f7; }
.mc-dot.fs { background: #f97316; }
.mc-dot.bs { background: #22c55e; }
.mc-star { color: #fde047; font-size: 16px; }

.kickturn-star {
  color: #fde047;
  font-size: 22px;
  text-align: center;
  line-height: 24px;
  text-shadow: 0 0 4px rgba(0,0,0,0.8), 0 0 2px rgba(0,0,0,1);
  pointer-events: none;
}

.legend {
  display: flex;
  gap: 8px;
  justify-content: center;
  padding: 16px 12px 24px;
  flex-wrap: wrap;
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--muted);
}
.legend-swatch {
  width: 14px; height: 14px; border-radius: 3px;
  border: 1px solid rgba(255,255,255,0.1);
}

/* ---- Homing-in overlay (pre-session GPS lock) ---- */
#homing-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.95);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  padding-top: calc(16px + env(safe-area-inset-top));
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
}
.homing-card {
  position: relative;                  /* anchor for the over-hanging pulse */
  width: min(420px, 100%);
  /* Cardboard texture tile (50x50, stitches seamlessly) instead of a flat
     white panel; --D8C09A is the tile's own base colour as a fallback. */
  background-color: #D8C09A;
  background-image: url('assets/cardboard.svg');
  background-repeat: repeat;
  background-size: 60px;
  border: 1px solid #2a3a5a;
  border-radius: var(--radius);
  /* Extra top padding leaves room for the lower half of the pulse, which now
     overlaps the card top (the upper half sticks out above it). */
  padding: 70px 20px 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.homing-card h2 {
  margin: 4px 0 0;
  font-size: 22px;
  /* Afacad (the calendar / poster display face) — only an italic-700 cut is
     self-hosted, so use those to hit the real webfont rather than a fallback. */
  font-family: 'Afacad', system-ui, sans-serif;
  font-style: italic;
  font-weight: 700;
}
.homing-msg {
  color: var(--muted);
  font-size: 13px;
  margin: 0;
  text-align: center;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;  /* matches share-url / caption */
}
/* Homing animation: animated GIF that loops while we wait for a tight GPS
   fix. When the overlay flips to .locked we swap it for a single green dot
   so the user knows the search is over. */
.homing-pulse {
  /* Pulled out of flow and anchored to the card's top edge so the upper half
     of the 120px GIF sticks out above the card and the chap pokes into the
     dark overlay. top:-60px = half the height. */
  position: absolute;
  top: -60px;
  left: 50%;
  transform: translateX(-50%);
  width: 120px;
  height: 120px;
  background: url('assets/animation.gif') center / contain no-repeat;
}
#homing-overlay.locked .homing-pulse {
  background: url('assets/ridin.png') center / contain no-repeat;
}
#homing-overlay.locked .homing-msg { color: #22c55e; font-weight: 600; }
.homing-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  width: 100%;
  margin-top: 6px;
}
.homing-stats > div {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--panel-2);
  border-radius: 8px;
  padding: 8px 6px;
}
.homing-stats .label { color: var(--muted); font-size: 11px; letter-spacing: 0.4px; text-transform: uppercase; }
.homing-stats b { font-size: 16px; font-variant-numeric: tabular-nums; margin-top: 2px; }
.homing-rule {
  color: var(--muted);
  font-size: 11px;
  text-align: center;
  margin: 4px 0 6px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;  /* matches .homing-msg */
}
.homing-actions {
  display: flex;
  gap: 10px;
  width: 100%;
}
.homing-actions button {
  flex: 1;
  padding: 12px 14px;
  border-radius: 999px;
  border: 1px solid #2a3a5a;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.homing-actions button.primary {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}

/* Press-and-hold "fill" gesture for the homing buttons. Holding sweeps a
   bright yellow bar (matching the Lost Session poster) in from the left; the
   action only fires once the bar is full (handled in app.js). The label is
   stacked twice: the base copy is the button's normal text, and a dark copy
   lives inside .hold-fill so wherever the yellow has reached, the text reads
   dark-on-yellow — i.e. white/light text inverts as the fill grows. */
.homing-actions button.hold-btn {
  position: relative;
  overflow: hidden;                 /* clip the fill to the pill shape */
  display: flex;
  align-items: center;
  justify-content: center;
  isolation: isolate;
  touch-action: none;               /* press-hold shouldn't scroll/zoom */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.hold-label {
  position: relative;
  z-index: 1;
  pointer-events: none;
}
.hold-fill {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #FFEA00;              /* Lost Session poster yellow */
  color: #0f172a;                  /* inverted (dark) label on the yellow */
  /* Start fully clipped from the right edge (invisible). Holding animates it
     open to inset(0) = full width; releasing snaps it back quickly. */
  clip-path: inset(0 100% 0 0);
  transition: clip-path 220ms ease-out;
  pointer-events: none;
}
.homing-actions button.hold-btn.holding .hold-fill {
  clip-path: inset(0 0 0 0);
  transition: clip-path 750ms linear;
}

/* ---- Drop-in pins + popup ---- */
.dropin-pin {
  background: none !important;
  border: none !important;
}
.dropin-pin-inner {
  /* Background is set inline per drop-in from the donut palette + tier.
     Default is a neutral mid-gray for any edge case (missing peakAccel etc.) */
  width: 30px;
  height: 30px;
  background: #94a3b8;
  color: #0b1220;
  border: 2px solid #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  text-shadow: 0 0 2px rgba(255, 255, 255, 0.6);
  box-shadow: 0 2px 8px rgba(0,0,0,0.4);
}
/* Drop-in burst: a star that pops in, rises a touch, and fades out during
   playback when the playhead crosses a detected drop-in. */
.dropin-burst-wrap { background: none !important; border: none !important; }
.dropin-burst {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 30px;
  line-height: 1;
  color: #fde047;
  text-shadow:
    0 0 6px rgba(253, 224, 71, 0.9),
    0 0 12px rgba(245, 158, 11, 0.7),
    0 1px 2px rgba(0, 0, 0, 0.6);
  animation: dropin-burst-pop 1600ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
  pointer-events: none;
  transform-origin: center;
  will-change: transform, opacity;
}
@keyframes dropin-burst-pop {
  0%   { transform: scale(0.15) translateY(0);     opacity: 0;   }
  15%  { transform: scale(1.6)  translateY(-2px);  opacity: 1;   }
  30%  { transform: scale(1.0)  translateY(-8px);  opacity: 1;   }
  100% { transform: scale(0.9)  translateY(-44px); opacity: 0;   }
}

.dropin-popup { font-size: 13px; color: #0b1220; line-height: 1.4; }
.dropin-popup-head {
  font-weight: 700;
  margin-bottom: 2px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dropin-tier-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid rgba(0,0,0,0.15);
  display: inline-block;
}
.dropin-popup .dropin-remove {
  margin-top: 8px;
  padding: 6px 10px;
  border: 1px solid #ef4444;
  background: #fff;
  color: #ef4444;
  border-radius: 6px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.mc-drop {
  display: inline-block;
  font-size: 16px;
  font-weight: 700;
  color: #f59e0b;
  width: 16px;
  text-align: center;
}

/* ---- "Update available" banner ---- */
.update-banner {
  position: fixed;
  left: 0; right: 0;
  bottom: env(safe-area-inset-bottom);
  z-index: 9000;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--accent);
  color: var(--accent-text);
  box-shadow: 0 -4px 18px rgba(0, 0, 0, 0.2);
  font-weight: 600;
}
.update-banner-text { flex: 1; }
.update-banner-btn {
  font: inherit;
  font-weight: 700;
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  cursor: pointer;
}
.update-banner-btn.primary { background: var(--accent-text); color: var(--accent); border-color: var(--accent-text); }
.update-banner-btn.ghost {
  background: transparent;
  color: var(--accent-text);
  border-color: rgba(255, 255, 255, 0.45);
  padding: 4px 10px;
  font-size: 18px;
}

/* ---- Install promotion toast (PWA "Add to home screen") ---- */
/* A card-style toaster pinned to the bottom; app.js toggles [hidden]. Sits at
   the same z-index as the update banner but the two are never shown together. */
.install-toast {
  position: fixed;
  left: 12px; right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom));
  z-index: 9000;
  margin: 0 auto;
  max-width: 460px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 12px 12px 14px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.22);
  animation: install-toast-in 260ms ease both;
}
@keyframes install-toast-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
.install-toast-icon {
  width: 46px; height: 46px;
  flex: 0 0 auto;
  border-radius: 10px;
}
.install-toast-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.install-toast-text strong { font-weight: 800; font-size: 15px; }
.install-toast-text span {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--muted);
}
.install-toast-btn {
  font: inherit;
  font-weight: 700;
  flex: 0 0 auto;
  padding: 9px 16px;
  border-radius: 9px;
  border: 1px solid transparent;
  cursor: pointer;
}
.install-toast-btn.primary {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}
.install-toast-btn.ghost {
  background: transparent;
  color: var(--muted);
  border-color: transparent;
  padding: 4px 8px;
  font-size: 20px;
  line-height: 1;
}
