/* ============================================================================
   Sweet View — visual system
   Friendly, rounded, sunny-sky. Brand blue #29abe2 from the logo.
   Headers: Lobster. Body: Jost.
   ============================================================================ */
:root {
  --sky: #29abe2;            /* the brand — straight off the logo */
  --sky-deep: #1487bd;       /* pressed / darker sky */
  --sky-soft: #8fd6f3;       /* light highlight */
  --cloud: #eaf7fe;          /* near-white, faint blue — primary text on dark */
  --navy: #0e2f43;           /* deep evening-sky panel */
  --navy-2: #123c55;
  --ink: #34302a;            /* polaroid caption ink */
  --cream: #fbf8f0;          /* polaroid paper */
  --sun: #ffce4a;            /* warm friendly accent (clock, scores) */
  --mint: #44c08a;           /* a good shot */
  --coral: #ff8a9b;          /* a gentle "keep exploring" */
  --dim: #94b6c9;            /* muted blue-grey */

  --r-lg: 24px;              /* big rounded panels */
  --r-md: 16px;             /* cards */
  --r-sm: 10px;
  --shadow: 0 10px 30px rgba(8, 30, 45, 0.42);
  --font-head: 'Lobster', 'Segoe Script', cursive;
  --font-body: 'Jost', 'Segoe UI', system-ui, sans-serif;
  --pname-weight: 600;       /* patch-name typography on cards, live-tunable via ⚙ Graphics */
  --pname-track: 0.05em;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: var(--navy);
             touch-action: none; overscroll-behavior: none; }
body { font-family: var(--font-body); color: var(--cloud); font-weight: 400;
       -webkit-tap-highlight-color: transparent; }
kbd { background: rgba(41, 171, 226, 0.16); border-radius: 6px; padding: 1px 7px;
      border: 1px solid rgba(143, 214, 243, 0.4); color: var(--cloud);
      font-family: var(--font-body); font-weight: 600; font-size: 0.92em; }

/* friendly section headers everywhere */
.head { font-family: var(--font-head); font-weight: 400; color: var(--sky);
        letter-spacing: 0.5px; }

/* ===== layout: stage + sidebar ===== */
#app { display: flex; width: 100%; height: 100%; }
#stage { position: relative; flex: 1; min-width: 0; touch-action: none; }
#cesiumContainer { position: absolute; inset: 0; }

#side { position: relative; z-index: 10; width: 432px; display: none; flex-direction: column; gap: 14px;
        padding: 18px 16px; overflow-y: auto;
        background: linear-gradient(180deg, #123c55 0%, #0c2839 100%);
        border-left: 1px solid rgba(143, 214, 243, 0.16); }
#side .brand { display: flex; align-items: center; gap: 10px; }
#side .brand img { height: 42px; width: auto; filter: drop-shadow(0 2px 6px rgba(0,0,0,0.35)); }
#side .rowline { display: flex; justify-content: space-between; align-items: baseline; }
#side .roundlab { font-family: var(--font-head); color: var(--sky-soft); font-size: 19px; }
#side .score { color: var(--sun); font-size: 17px; font-weight: 600;
               font-variant-numeric: tabular-nums; }
#timer { font-size: 48px; font-weight: 600; color: var(--sun); line-height: 1;
         text-align: center; font-variant-numeric: tabular-nums; }
#timer.low { color: var(--coral); animation: pulse 0.8s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }
#timerBarWrap { height: 8px; background: rgba(8, 28, 40, 0.7); border-radius: 999px; overflow: hidden; }
#timerBar { height: 100%; width: 100%; border-radius: 999px;
            background: linear-gradient(90deg, var(--sky), var(--sun));
            transition: width 0.15s linear; }
#timerBar.low { background: var(--coral); }

/* skeuomorphic polaroid: white paper, SQUARE corners, thick caption border at
   the bottom, pinned on a slight angle */
.refcard { background: #fcfaf4; padding: 11px 11px 0; border-radius: 0;
           box-shadow: 0 10px 26px rgba(8, 30, 45, 0.5); cursor: zoom-in;
           transform: rotate(-2.5deg); transition: transform 0.18s ease; }
.refcard:hover { transform: rotate(-0.5deg) scale(1.03); }
.refcard img { display: block; width: 100%; border-radius: 0; background: #d6e6ef; }
.refcard .cap { color: var(--ink); font-size: 13px; text-align: center; padding: 13px 4px 17px;
                font-family: var(--font-body); font-style: italic; opacity: 0.8; }
.refcard.developing img { filter: sepia(0.6) brightness(1.6) blur(8px); }

#side .stats { color: var(--dim); font-size: 13px; line-height: 1.85; }
#side .stats .spd { color: var(--sky-soft); font-size: 19px; font-weight: 600; }
#side .keys { color: var(--dim); font-size: 12px; line-height: 2.1; margin-top: auto; }
/* ===== in-stage HUD ===== */
#crosshair { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
             z-index: 9; display: none; pointer-events: none; opacity: 0.9; }
#crosshair::before, #crosshair::after { content: ''; position: absolute; background: #fff; }
#crosshair::before { width: 16px; height: 2px; left: -8px; top: 0; border-radius: 2px; }
#crosshair::after { width: 2px; height: 16px; left: 0; top: -8px; border-radius: 2px; }

#finder { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
          z-index: 8; display: none; pointer-events: none;
          border: 1.5px solid rgba(255,255,255,0.55); border-radius: 8px;
          box-shadow: 0 0 0 9999px rgba(8, 30, 45, 0.18); }
#finder .fc { position: absolute; width: 22px; height: 22px;
              border: 3px solid var(--sky-soft); border-radius: 3px; }
#finder .fc1 { top: -2px; left: -2px; border-right: none; border-bottom: none; }
#finder .fc2 { top: -2px; right: -2px; border-left: none; border-bottom: none; }
#finder .fc3 { bottom: -2px; left: -2px; border-right: none; border-top: none; }
#finder .fc4 { bottom: -2px; right: -2px; border-left: none; border-top: none; }
#finder .flabel { position: absolute; bottom: -24px; right: 0; font-size: 12px; font-weight: 500;
                  color: rgba(255,255,255,0.85); }

/* ===== orientation HUD: friendly compass + area/street ===== */
/* Desktop-leaning defaults; the mobile media query compacts it. */
#compass { position: absolute; display: none; z-index: 12; pointer-events: none;
           top: calc(env(safe-area-inset-top, 8px) + 14px); left: 50%; transform: translateX(-50%);
           width: min(440px, 42vw); padding: 10px 22px 13px; text-align: center;
           background: rgba(11, 35, 50, 0.9); border: 1px solid rgba(143, 214, 243, 0.28);
           border-radius: 18px; }
#compass .cbar { position: relative; height: 28px; overflow: hidden; margin: 0 auto;
                 -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
                         mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); }
#compass .cstrip { position: absolute; inset: 0; }
.cmark { position: absolute; top: 2px; transform: translateX(-50%); white-space: nowrap;
         font-size: 13px; font-weight: 600; color: var(--cloud); }
.cmark.card { color: #fff; font-size: 17px; font-weight: 700; }
.ctick { position: absolute; top: 10px; width: 2px; height: 8px; transform: translateX(-50%);
         background: rgba(143, 214, 243, 0.5); border-radius: 2px; }
#compass .cneedle { position: absolute; top: 0; left: 50%; transform: translateX(-50%);
                    width: 3px; height: 16px; border-radius: 2px;
                    background: var(--sun); box-shadow: 0 0 8px var(--sun); }
#compass .cdeg { font-size: 13px; color: var(--sun); letter-spacing: 1.5px; font-weight: 600;
                 font-variant-numeric: tabular-nums; margin-top: 4px; }
#compass .carea { font-size: 18px; font-weight: 600; color: #fff; margin-top: 4px; line-height: 1.25;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#compass .cstreet { font-size: 14.5px; color: var(--sky-soft); margin-top: 1px;
                    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ===== graphics config panel (⚙) ===== */
#cfg { position: fixed; z-index: 31;
       top: calc(env(safe-area-inset-top, 8px) + 8px);
       left: calc(env(safe-area-inset-left, 0px) + 8px); }
/* the developing poster's wordmark and eyebrow own the top-left, so the gear
   moves to the poster's empty bottom-left while it is up */
body.devUp #cfg { top: auto; bottom: calc(env(safe-area-inset-bottom, 0px) + 64px); }
/* ...and with the gear at the bottom the panel must open UPWARD, or its body
   drops straight off the viewport (both reviewers caught only the tab strip) */
body.devUp #cfgPanel { top: auto; bottom: 46px; }
#cfgBtn { width: 38px; height: 38px; border-radius: 50%; cursor: pointer; line-height: 1;
          background: rgba(12, 38, 54, 0.92); border: 1px solid rgba(143, 214, 243, 0.3);
          color: var(--cloud); font-size: 18px;
          box-shadow: 0 4px 12px rgba(8, 30, 45, 0.35); transition: transform .1s, background .15s; }
#cfgBtn:hover { background: rgba(41, 171, 226, 0.3); }
#cfgBtn:active { transform: scale(0.94); }
/* The SLIDER LIST scrolls, not the panel: the tabs have to stay reachable at
   the top and the launch and copy rows at the bottom. When the whole panel
   scrolled, adding the launch buttons pushed both off the bottom edge. */
#cfgPanel { position: absolute; top: 46px; left: 0; display: none; width: 268px;
            max-height: 78vh; padding: 10px 12px 12px;
            background: rgba(10, 32, 46, 0.97); border: 1px solid rgba(143, 214, 243, 0.3);
            border-radius: 16px; box-shadow: var(--shadow); }
#cfgPanel.open { display: flex; flex-direction: column; }
#cfgBody { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
#cfgLaunch, .cfgBtns { flex: none; }
.cfgTop { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 6px; }
.cfgTitle { font-family: var(--font-head); color: var(--sky-soft); font-size: 20px; }
.cfgTabs { display: flex; gap: 4px; }
.cfgTab { cursor: pointer; border: none; border-radius: 999px; padding: 5px 11px;
          font: 600 11.5px var(--font-body); background: rgba(143, 214, 243, 0.12);
          color: var(--sky-soft); transition: background .12s, color .12s; }
.cfgTab:hover { background: rgba(41, 171, 226, 0.24); color: #fff; }
.cfgTab.on { background: var(--sky); color: #fff; }
.cfgFps { color: var(--dim); font-size: 11px; font-variant-numeric: tabular-nums; }
.cfgGroup { font-size: 11px; font-weight: 600; letter-spacing: 0.8px; text-transform: uppercase;
            color: var(--sky); margin: 12px 0 4px; border-bottom: 1px solid rgba(143,214,243,0.16);
            padding-bottom: 3px; }
.cfgRow { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.cfgLab { flex: 0 0 92px; font-size: 11.5px; color: var(--cloud); overflow: hidden;
          text-overflow: ellipsis; white-space: nowrap; }
.cfgRow input[type=range] { flex: 1; min-width: 0; accent-color: var(--sky); height: 3px; }
.cfgRow input[type=color] { flex: 1; min-width: 0; height: 22px; padding: 0 2px;
                             border: 1px solid rgba(143, 214, 243, 0.3); border-radius: 5px;
                             background: transparent; cursor: pointer; }
.cfgVal { flex: 0 0 42px; text-align: right; font-size: 11px; color: var(--sky-soft);
          font-variant-numeric: tabular-nums; }
.cfgToggle { justify-content: space-between; cursor: pointer; }
.cfgToggle span { font-size: 12px; color: var(--cloud); }
.cfgToggle input { accent-color: var(--sky); width: 16px; height: 16px; }
#cfgLaunch { display: flex; flex-direction: column; gap: 6px; margin-top: 14px;
             padding-top: 12px; border-top: 1px solid rgba(143, 214, 243, 0.16); }
#cfgLaunch button { cursor: pointer; border: none; border-radius: 999px; padding: 9px 6px;
                    font: 600 12px var(--font-body); color: #fff; background: var(--sky);
                    transition: filter .12s; }
#cfgLaunch button:hover { filter: brightness(1.08); }
#cfgLaunch .cfgGhost { background: rgba(143, 214, 243, 0.16); color: var(--cloud); }
.cfgBtns { display: flex; gap: 8px; margin-top: 10px; }
.cfgBtns button { flex: 1; cursor: pointer; border: none; border-radius: 999px; padding: 9px 6px;
                  font: 600 12px var(--font-body); color: #fff; background: var(--sky);
                  transition: filter .12s, transform .08s; }
#cfgRevert { background: rgba(143, 214, 243, 0.16); color: var(--cloud); }
.cfgBtns button:hover { filter: brightness(1.08); }
.cfgBtns button:active { transform: scale(0.97); }

/* free-fly badge */
#freeFlyBadge { position: fixed; z-index: 31; display: none; align-items: center; gap: 10px;
                top: calc(env(safe-area-inset-top, 8px) + 8px); right: calc(env(safe-area-inset-right, 0px) + 10px);
                background: rgba(12, 38, 54, 0.7); border: 1px solid rgba(143, 214, 243, 0.3);
                border-radius: 999px; padding: 6px 8px 6px 14px; font-size: 12px; font-weight: 600;
                letter-spacing: 1px; color: var(--sun); }
#freeFlyExit { cursor: pointer; border: none; border-radius: 999px; padding: 5px 12px;
               font: 600 11px var(--font-body); color: #fff; background: var(--sky); letter-spacing: 0.5px; }
#freeFlyExit:hover { filter: brightness(1.08); }

/* debug row on the splash */
#debugRow { margin-top: 14px; }
#freeFlyBtn { cursor: pointer; background: rgba(143, 214, 243, 0.12); color: var(--sky-soft);
              border: 1px solid rgba(143, 214, 243, 0.28); border-radius: 999px;
              padding: 7px 18px; font: 500 12px var(--font-body); transition: background .15s; }
#freeFlyBtn:hover { background: rgba(41, 171, 226, 0.25); color: #fff; }

#flash { position: absolute; inset: 0; background: #fff; opacity: 0;
         pointer-events: none; z-index: 40; }
#flash.on { animation: flashAnim 0.4s ease-out; }
@keyframes flashAnim { 0% { opacity: 0.95; } 100% { opacity: 0; } }

/* Above EVERY overlay. #stage is position:relative with z-index:auto, so this
   competes in the root stacking context with .overlay (30) and friends — at
   20 it sat behind the dashboard, and a failed game start ("out of tokens",
   "fully booked") looked exactly like nothing happening. */
#toast { position: absolute; top: 24%; left: 50%; transform: translateX(-50%);
         z-index: 62; font-family: var(--font-head); font-size: 44px;
         color: #fff; text-shadow: 0 3px 0 var(--sky-deep), 0 6px 24px rgba(8,30,45,0.7);
         opacity: 0; pointer-events: none; white-space: nowrap; }
#toast.on { animation: toastAnim 1.6s ease-out; }
@keyframes toastAnim { 0% { opacity: 0; transform: translateX(-50%) scale(1.4); }
                       18% { opacity: 1; transform: translateX(-50%) scale(1); }
                       75% { opacity: 1; } 100% { opacity: 0; } }

/* mobile top bar + floating polaroid + touch controls */
#mtop { position: absolute; top: env(safe-area-inset-top, 8px); left: 50%;
        transform: translateX(-50%); z-index: 10; display: none; gap: 16px;
        pointer-events: none;   /* never steals a joystick thumb */
        background: rgba(12, 38, 54, 0.92); border: 1px solid rgba(143, 214, 243, 0.28);
        border-radius: 999px; padding: 8px 18px; font-size: 15px; align-items: baseline;
        white-space: nowrap; }
#mtop #mTimer { color: var(--sun); font-weight: 600; font-size: 20px;
                font-variant-numeric: tabular-nums; }
#mtop #mTimer.low { color: var(--coral); animation: pulse 0.8s ease-in-out infinite; }
#mtop #mRound { color: var(--sky-soft); }
#mtop #mScore { color: var(--sun); font-weight: 600; }

#refbox { position: absolute; top: calc(env(safe-area-inset-top, 8px) + 4px); right: 10px;
          z-index: 14; display: none; width: 168px; cursor: zoom-in; pointer-events: auto;
          background: #fcfaf4; padding: 8px 8px 0; border-radius: 0;
          box-shadow: 0 8px 22px rgba(8, 30, 45, 0.55); transform: rotate(2.5deg); }
#refbox img { display: block; width: 100%; border-radius: 0; background: #d6e6ef; }
#refbox .cap { color: var(--ink); font-size: 11px; text-align: center; padding: 7px 2px 10px;
               font-family: var(--font-body); font-style: italic; opacity: 0.8; }

#touchCtl { position: absolute; inset: 0; z-index: 11; display: none;
            pointer-events: none; touch-action: none; }
/* raised base contrast + a dark offset shadow under a soft inset top
   highlight so the disc reads as a control against a bright sky, not a
   flat ring that vanishes into it */
.tbtn { position: absolute; pointer-events: auto; user-select: none; touch-action: none;
        display: flex; align-items: center; justify-content: center;
        border-radius: 50%; border: 2px solid rgba(255,255,255,0.45);
        background: rgba(12, 38, 54, 0.78);
        box-shadow: 0 4px 12px rgba(8, 30, 45, 0.55), inset 0 1px 0 rgba(255,255,255,0.14);
        color: #fff; font-size: 24px; }
.tbtn:active, .tbtn.held { background: rgba(41, 171, 226, 0.4); border-color: var(--sky); }
/* round shutter — white ring + sky-blue disc, lifted above the joysticks */
#shutterBtn { left: 50%; transform: translateX(-50%);
              bottom: calc(env(safe-area-inset-bottom, 0px) + 152px);
              width: 74px; height: 74px; padding: 0; font-size: 0;
              background: transparent; border: 4px solid rgba(255,255,255,0.95);
              box-shadow: 0 9px 24px rgba(8, 30, 45, 0.7), inset 0 1px 0 rgba(255,255,255,0.5);
              transition: transform 0.08s ease, filter 0.1s ease; }
#shutterBtn::after { content: ''; position: absolute; inset: 6px; border-radius: 50%;
                     background: linear-gradient(180deg, var(--sky) 0%, var(--sky) 45%, var(--sky-deep) 100%);
                     box-shadow: inset 0 2px 4px rgba(255,255,255,0.4), inset 0 -3px 6px rgba(8,30,45,0.4);
                     transition: transform 0.08s ease; }
#shutterBtn:active { transform: translateX(-50%) scale(0.96); filter: brightness(1.12); }
#shutterBtn:active::after { transform: scale(0.85); }

/* twin virtual joysticks — same depth treatment: a domed base (radial-shaded,
   lighter at the centre, darker at the rim, still the same dark-navy family
   so the MOVE/LOOK caption over it stays legible) and a knob shadowed and
   top-lit like a raised disc */
.stick { position: absolute; width: 122px; height: 122px;
         bottom: calc(env(safe-area-inset-bottom, 0px) + 112px);
         border-radius: 50%; border: 2px solid rgba(143, 214, 243, 0.5);
         background: radial-gradient(circle at 50% 40%,
           rgba(26, 56, 76, 0.72) 0%, rgba(9, 30, 44, 0.68) 55%, rgba(4, 15, 23, 0.78) 100%);
         box-shadow: 0 9px 24px rgba(8, 30, 45, 0.7), inset 0 1px 0 rgba(255,255,255,0.12);
         pointer-events: none; touch-action: none; z-index: 11;
         opacity: 0.7; transition: opacity 0.12s, transform 0.08s ease, filter 0.1s ease; }
#stickL { left: calc(env(safe-area-inset-left, 0px) + 16px); }
#stickR { right: calc(env(safe-area-inset-right, 0px) + 16px); }
.stick.on { opacity: 1; }
.stick:active, .stick.on { transform: scale(0.96); filter: brightness(1.12); }
.stick .knob { position: absolute; left: 50%; top: 50%; width: 52px; height: 52px;
               margin: -26px 0 0 -26px; border-radius: 50%;
               background: radial-gradient(circle at 35% 30%,
                 rgba(94, 200, 240, 0.9) 0%, rgba(41, 171, 226, 0.72) 45%, rgba(15, 95, 135, 0.8) 100%);
               border: 2px solid rgba(255,255,255,0.8);
               box-shadow: 0 5px 12px rgba(8, 30, 45, 0.7),
                           inset 0 4px 6px rgba(255,255,255,0.6),
                           inset 0 -5px 9px rgba(8, 30, 45, 0.55);
               will-change: transform; }
.stick .ring { position: absolute; left: 50%; bottom: 9px; transform: translateX(-50%);
               font-size: 10px; letter-spacing: 1.5px; font-weight: 600; color: rgba(234, 247, 254, 0.7); }

/* ===== overlays ===== */
.overlay { position: fixed; inset: 0; z-index: 30; display: none;
           flex-direction: column; align-items: center; justify-content: center;
           text-align: center; padding: 20px; }
#dev, #results, #offline, #preview {
  background: radial-gradient(ellipse at 50% 30%, rgba(18, 60, 85, 0.92) 0%, rgba(8, 26, 38, 0.96) 100%);
}

/* friendly rounded card that holds overlay content */
.sheet { background: linear-gradient(165deg, rgba(20, 64, 88, 0.96), rgba(11, 36, 52, 0.97));
         border: 1.5px solid rgba(143, 214, 243, 0.32); border-radius: var(--r-lg);
         padding: 26px 30px 28px; max-width: min(94vw, 1000px);
         box-shadow: var(--shadow), inset 0 1px 0 rgba(255,255,255,0.06); }

/* primary pill button. A slim top-to-bottom sheen (same hue, no new color)
   plus a neutral drop shadow give it real thickness — house rule: no colored
   glow at rest, that's reserved for hover/active/selected states. */
.btn { display: inline-flex; align-items: center; gap: 8px; margin-top: 18px; cursor: pointer;
       color: #fff; border: none; border-radius: 999px;
       background: linear-gradient(180deg, var(--sky) 0%, var(--sky) 45%, var(--sky-deep) 100%);
       padding: 14px 40px; font: 600 17px var(--font-body); letter-spacing: 0.3px;
       box-shadow: 0 5px 14px rgba(8, 30, 45, 0.45); user-select: none;
       /* legibility pass 2026-08-20: thin glyph strokes on the cyan fill wash
          out on bright/washed screens — a faint dark label shadow, not a fill
          change, keeps them readable. */
       text-shadow: 0 1px 2px rgba(0, 0, 20, 0.35);
       transition: transform 0.08s, filter 0.15s, box-shadow 0.15s; }
.btn:hover { filter: brightness(1.08); box-shadow: 0 9px 20px rgba(8, 30, 45, 0.55); transform: translateY(-1px); }
.btn:active { transform: translateY(1px); box-shadow: 0 3px 8px rgba(8, 30, 45, 0.4); filter: brightness(0.97); }

/* ===== splash: translucent — the cinematic orbit plays underneath ===== */
#splash { display: flex; overflow-y: auto; -webkit-overflow-scrolling: touch;
          background: radial-gradient(ellipse at 50% 42%,
            rgba(10, 34, 49, 0.46) 0%, rgba(9, 30, 44, 0.72) 52%, rgba(6, 22, 33, 0.95) 100%); }
/* While references develop on the player's own viewer up front, the camera
   jumps between spots — make the splash fully opaque so that isn't visible
   behind the logo. Removed once they're all shot (then the orbit shows through). */
#splash.developing { background: radial-gradient(ellipse at 50% 42%,
            #0a2231 0%, #091e2c 52%, #06161f 100%); }
#splash .logo { width: clamp(240px, 46vw, 460px); height: auto;
                filter: drop-shadow(0 10px 30px rgba(8, 30, 45, 0.7)); }
/* An eyebrow over the wordmark, not a subtitle under it. Same spec as
   #dev .devEyebrow, which does the identical job above the patch name:
   600 12.5px, 2.4px tracking, uppercase, sky-soft. */
#splash .tag { margin: 0 0 12px; color: var(--sky-soft);
               font: 600 12.5px var(--font-body); letter-spacing: 2.4px;
               text-transform: uppercase;
               text-shadow: 0 2px 14px rgba(8,30,45,0.95); }
#splash .brief { margin-top: 18px; max-width: 500px; color: #eef8ff; font-size: 15px;
                 line-height: 1.8; text-shadow: 0 1px 2px rgba(8,30,45,0.5);
                 background: rgba(11, 35, 51, 0.86); border: 1px solid rgba(143,214,243,0.24);
                 border-radius: var(--r-md); padding: 16px 22px; }
#splash .brief b { color: var(--sun); font-weight: 600; }
/* .brief is one card holding paragraphs now, so kill the default <p> margins
   and space them here instead — otherwise the card's padding gets doubled up. */
#splash .brief p { margin: 0; }
/* The maker's aside, not part of the pitch: hairline off the sentence above,
   quieter type. It carries what the "flying is free right now" pill used to.
   Selector MUST out-specify `#splash .brief p` above (1,1,1) or the margin is
   silently zeroed and the rule sits flush against the sentence — two classes
   beat one class plus a type, so `.brief .briefNote` (1,2,0) wins.
   Gaps are uneven ON PURPOSE: the paragraph above carries ~6px of half-leading
   below its last line and the note ~5px above its first, so 18/15 lands at
   roughly 24px and 20px optically. Equal numbers here look top-light. */
#splash .brief .briefNote { margin-top: 18px; padding-top: 15px; font-size: 13.5px;
                     color: var(--sky-soft);
                     border-top: 1px solid rgba(143, 214, 243, 0.16); }
#startBtn { font-size: 21px; padding: 16px 60px; margin-top: 22px; }

/* The splash cheat-sheet (#controls, .desk/.mob) is GONE — it sat on the
   sign-in page, before anyone had an account. The same .ctrl/.ckey/.clab
   language survives, scoped to #dev .devKeys further down. */

/* ===== game-over summary (inside the splash) ===== */
#finalSummary { display: none; margin-top: 16px; }
#finalSummary .rank { font-family: var(--font-head); font-size: 28px; color: var(--sun);
                      margin-bottom: 2px; text-shadow: 0 2px 12px rgba(8,30,45,0.9); }
#finalSummary .ftotal { font-size: 38px; font-weight: 700; color: var(--sky-soft);
                        font-variant-numeric: tabular-nums; text-shadow: 0 2px 12px rgba(8,30,45,0.9); }
/* the run's photos: a full-width strip of BIG clickable cards (click = round
   review). Five per row; 3 rounds make one short row, 10 make two rows.
   Same quiet sub-panel language as .fsSheet .fsLeft (2026-08-20) so the
   sheet chunks into hero / board / photos instead of the strip floating in
   shared whitespace. */
#finalRows { display: flex; gap: clamp(10px, 1.1vw, 18px); margin-top: clamp(18px, 2vw, 30px);
             justify-content: center; flex-wrap: wrap;
             background: rgba(143, 214, 243, 0.07); border: 1px solid rgba(143, 214, 243, 0.16);
             border-radius: var(--r-lg); padding: clamp(14px, 1.8vw, 24px); }
#finalRows .row { flex: 0 1 calc(20% - clamp(10px, 1.1vw, 18px)); min-width: 150px; max-width: 265px;
                  background: var(--cream); border: none; border-radius: 10px;
                  padding: 7px 7px 6px; cursor: pointer; font-family: var(--font-body);
                  box-shadow: 0 6px 16px rgba(8, 30, 45, 0.45); text-align: center;
                  transition: transform 0.1s ease, box-shadow 0.15s ease; }
#finalRows .row:hover { transform: translateY(-4px); box-shadow: 0 14px 28px rgba(8, 30, 45, 0.55); }
#finalRows .row img { width: 100%; border-radius: 5px; display: block; }
#finalRows .row .nm { color: var(--ink); font-size: clamp(11px, 0.9vw, 15px); font-style: italic; opacity: 0.85;
                      padding-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#finalRows .row .pt { color: var(--sky-deep); font-size: clamp(14px, 1.15vw, 20px); font-weight: 700; }

/* ===== leaderboard (on the game-over screen) ===== */
#lb { margin-top: 22px; width: min(420px, 92vw); }
.lbTitle { font: 600 12px var(--font-body); color: var(--dim); letter-spacing: 1.6px;
           text-transform: uppercase; text-align: left; margin-bottom: 8px; }
#lbList { list-style: none; display: flex; flex-direction: column; gap: 3px; }
#lbList li { display: flex; align-items: baseline; gap: 10px; padding: 6px 12px; border-radius: var(--r-sm);
             background: rgba(143, 214, 243, 0.1); font-variant-numeric: tabular-nums; }
#lbList li .r { flex: 0 0 19px; color: var(--dim); font-weight: 700; text-align: right; }
#lbList li .nm { flex: 1; text-align: left; color: var(--cloud); overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
#lbList li .sc { color: var(--sky-soft); font-weight: 700; }
#lbList li.me { background: rgba(255, 206, 74, 0.18); border: 1px solid rgba(255, 206, 74, 0.5); }
#lbList li.me .nm, #lbList li.me .sc, #lbList li.me .r { color: var(--sun); }
/* podium chips — same treatment as .lbRows (#mpBoard), see that block's
   comment for why this reads after .me. */
#lbList li:nth-child(1) .r, #lbList li:nth-child(2) .r, #lbList li:nth-child(3) .r {
  flex-basis: 16px; height: 16px; line-height: 16px; text-align: center;
  border-radius: 50%; align-self: center; font-weight: 800;
}
#lbList li:nth-child(1) .r { background: rgba(255, 206, 74, 0.22);
  box-shadow: inset 0 0 0 1px rgba(255, 206, 74, 0.45); color: var(--sun); }
#lbList li:nth-child(2) .r { background: rgba(180, 202, 212, 0.18);
  box-shadow: inset 0 0 0 1px rgba(180, 202, 212, 0.42); color: #c9d8e0; }
#lbList li:nth-child(3) .r { background: rgba(184, 138, 94, 0.16);
  box-shadow: inset 0 0 0 1px rgba(184, 138, 94, 0.4); color: #c99b70; }
#lbEntry { display: flex; gap: 8px; margin-top: 10px; justify-content: center; }
#lbName { flex: 1; max-width: 220px; border: 1px solid rgba(143, 214, 243, 0.4); border-radius: 999px;
          background: rgba(8, 28, 40, 0.6); color: var(--cloud); padding: 9px 16px;
          font: 500 14px var(--font-body); outline: none; }
#lbName:focus { border-color: var(--sky); }
#lbName::placeholder { color: var(--dim); }
#lbSubmit { cursor: pointer; border: none; border-radius: 999px; padding: 9px 18px;
            font: 600 13px var(--font-body); color: #fff; background: var(--sky);
            transition: filter .12s, transform .08s; }
#lbSubmit:hover { filter: brightness(1.08); }
#lbSubmit:active { transform: scale(0.97); }
#lbSubmit:disabled { opacity: 0.55; cursor: default; }
#lbNote { margin-top: 8px; color: var(--sun); font-size: 14px; font-weight: 600; min-height: 18px; }
#finalActions { margin-top: 14px; display: flex; gap: 10px; justify-content: center; align-items: center; flex-wrap: wrap; }
/* legibility pass 2026-08-20: label lightened sky-soft -> cloud, see
   #apLocate above. */
#shareBtn, #newPatchBtn:not(.btn), #feedbackLink, #menuBtn, #finalPublishBtn { cursor: pointer; background: rgba(143, 214, 243, 0.14); color: var(--cloud);
            border: 1px solid rgba(143, 214, 243, 0.32); border-radius: 999px; text-decoration: none;
            padding: 9px 20px; font: 600 13px var(--font-body); transition: background .15s; }
#shareBtn:hover, #newPatchBtn:not(.btn):hover, #feedbackLink:hover, #menuBtn:hover, #finalPublishBtn:hover { background: rgba(41, 171, 226, 0.25); color: #fff; }

/* ===== reference preview (before each round) ===== */
#preview .pvround { font-family: var(--font-head); color: var(--sky-soft); font-size: 24px;
                    margin-bottom: 16px; }
#preview .pvcard { max-width: min(88vw, 940px); transform: rotate(-1deg); cursor: default; }
#preview .pvcount { margin-top: 20px; color: var(--sun); font-size: 17px; font-weight: 600;
                    letter-spacing: 0.5px; font-variant-numeric: tabular-nums; }

/* ===== developing — a full-bleed poster of the patch you're about to fly =====
   Not a centred sheet: the patch's own satellite still sits blurred behind
   left-aligned type, with a horizontal scrim so the city stays visible on the
   right, behind the cards. Every rule here is id-scoped (#dev …) on purpose —
   the old centred styles were too, so class-only rules would lose to them. */
/* Solid dark, ALWAYS — not the shared translucent radial-gradient the other
   overlays use. The live Cesium canvas sits right behind this at a lower
   z-index, and while the satellite still is still loading there is nothing
   else covering it: a translucent background let it flash through. #dev never
   goes see-through; only the satellite image (once loaded) fades in on top. */
#dev { padding: 0; align-items: stretch; justify-content: flex-start;
       background: #06161f; }
#dev.poster { background: #06161f; }
/* Blur enough to sit behind type, NOT so much that the place stops being
   readable. City grain at this scale is ~10px wide, so anything past ~8px
   turns a real aerial into green mud and the whole point of showing the
   patch is lost. (The brief said 34px — that was written against a different,
   coarser reference image.) Starts invisible (opacity 0, NOT display:none —
   display can't transition) and fades in only once paintDevShot's Image has
   actually fired its load event and added .on; until then #dev's own solid
   background above is the entire poster. */
#dev .devShot { position: absolute; inset: -24px; background-size: cover;
                background-position: center; transform: scale(1.04);
                filter: blur(7px) saturate(1.08) brightness(0.58);
                opacity: 0;
                transition: opacity 0.6s ease-out, filter 0.9s ease-out; }
#dev .devShot.on { opacity: 1; }
/* ready: loading finished — the poster "develops" to sharp. That motion IS
   the statement, so there's no separate banner to miss. */
#dev.ready .devShot { filter: blur(0) saturate(1.08) brightness(0.58); }
/* Two scrims: horizontal (dark under the type, open on the right) over a
   vertical one that seats the footer and keeps the whole frame from glaring. */
#dev .devScrim { position: absolute; inset: 0; background:
                 linear-gradient(90deg, rgba(6, 22, 33, 0.9) 0%, rgba(6, 22, 33, 0.68) 24%,
                   rgba(6, 22, 33, 0.34) 46%, rgba(6, 22, 33, 0.22) 62%, rgba(6, 22, 33, 0.3) 100%),
                 linear-gradient(180deg, rgba(6, 22, 33, 0.5) 0%, rgba(6, 22, 33, 0.1) 30%,
                   rgba(6, 22, 33, 0.28) 72%, rgba(6, 22, 33, 0.72) 100%); }
#dev .devWrap { position: relative; height: 100%; width: 100%; display: flex;
                flex-direction: column; padding: 40px 34px 24px; text-align: left; }
#dev .devHead { display: flex; align-items: center; gap: 14px; flex: none; }
/* 1.7x the old 30px — there was empty real estate above devMain's content
   for the header to grow into. devWrap's top padding and devMain's own top
   padding (below) both grew alongside it for breathing room. */
#dev .devLogo { height: 51px; width: auto; opacity: 0.9; }
#dev .devSpacer { flex: 1; }
#dev #devBack { cursor: pointer; border-radius: 999px; padding: 8px 16px;
                background: rgba(143, 214, 243, 0.14); color: var(--sky-soft);
                border: 1px solid rgba(143, 214, 243, 0.32); font: 500 13.5px var(--font-body); }
#dev #devBack:hover { background: rgba(41, 171, 226, 0.24); color: #fff; }

#dev .devMain { flex: 1; min-height: 0; overflow-y: auto; display: flex;
                align-items: flex-start; gap: 56px; padding: 36px 0 18px; }
/* The rail stays at the top; the hero sits in the middle of the poster instead
   of stacking against the top edge with half the frame empty under it.
   "safe" so a short window pushes it back to the top rather than clipping. */
#dev .devLeft { flex: 1; min-width: 0; display: flex; flex-direction: column;
                align-self: safe center; }
#dev .devEyebrow { font: 600 12.5px var(--font-body); color: var(--sky-soft);
                   letter-spacing: 2.4px; text-transform: uppercase; }
#dev h2.devName { font-family: var(--font-head); font-weight: 400; color: #fff;
                  line-height: 1; font-size: clamp(40px, 6.4vw, 104px); text-wrap: pretty;
                  margin-top: 6px; text-shadow: 0 6px 30px rgba(6, 22, 33, 0.8); }
#dev p.devRules { font-size: 15.5px; color: var(--cloud); opacity: 0.85; margin-top: 12px; }
/* the wrap is a fixed-footprint SLOT, not just a progress bar: the track and
   the Fly button are stacked in the same grid cell (grid-area 1/1) so the
   button appears exactly where the bar was, not somewhere else on the poster. */
#dev .devBarWrap { position: relative; display: grid; align-items: center;
                   height: 52px; margin-top: 22px; max-width: 520px; }
#dev .devBarWrap .devTrack { grid-area: 1 / 1; position: relative; height: 10px;
                   border-radius: 999px; background: rgba(143, 214, 243, 0.16);
                   overflow: hidden; transition: opacity 0.2s ease; }
/* empty-track shimmer: at 0% progress the bar reads as stuck otherwise. Same
   sweep as the fill's sheen but much fainter, so it never competes with it —
   and it's naturally hidden wherever the fill has already covered the track. */
#dev .devBarWrap .devTrack::before { content: ''; position: absolute; inset: 0; width: 200%;
               background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,0.12) 50%, transparent 70%);
               animation: devTrackSheen 2.6s linear infinite; }
#dev .devBar { position: relative; height: 100%; width: 0; border-radius: 999px;
               overflow: hidden; transition: width 0.5s ease;
               background: linear-gradient(90deg, var(--sky), var(--sun)); }
#dev .devBar::after { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 200%;
               background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,0.35) 50%, transparent 70%);
               animation: devBarSheen 1.8s linear infinite; }
/* the sheen is a single soft band, not a repeating tile, so the seamless loop
   comes from BOTH endpoints sitting fully off-screen (own-width is 200% of
   the bar, so -100% clears the left edge and 50% clears the right) rather
   than from wrapping a fraction of the gradient like goldShimmer does */
@keyframes devBarSheen { from { transform: translateX(-100%); } to { transform: translateX(50%); } }
@keyframes devTrackSheen { from { transform: translateX(-100%); } to { transform: translateX(50%); } }
#devTip { transition: opacity 0.28s ease; }
#devTip.out { opacity: 0; }
#dev p.devStatus { font-size: 13px; color: var(--cloud); opacity: 0.75; margin-top: 10px;
                   transition: opacity 0.28s ease; }
#dev p.devStatus.out { opacity: 0; }
#dev p.devReady { font: 600 14px var(--font-body); color: var(--sun); margin-top: 9px;
                  font-variant-numeric: tabular-nums; }
/* only exists when a survey verdict is set (see surveySpot in main.js) — display
   stays none until JS opts it in, so an absent warning never shifts the layout. */
#dev p.devWarn { display: none; font-size: 12.5px; color: rgba(255, 206, 74, 0.85);
                 max-width: 520px; margin-top: 8px; text-align: center; line-height: 1.4;
                 white-space: pre-line; }
/* ready: the track fades out of the slot and the Fly button takes over the
   exact space it occupied — the one spot the pilot is already staring at —
   instead of popping up somewhere new. */
#dev.ready .devBarWrap .devTrack, #dev.ready p.devStatus { opacity: 0; pointer-events: none; }
#dev .devBarWrap #devGo { grid-area: 1 / 1; width: 100%; height: 52px;
                          margin-top: 0; justify-content: center; font-size: 17px;
                          position: relative; overflow: hidden; }
#dev.ready #devGo { animation: devGoIn 0.25s ease both; }
@keyframes devGoIn { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
/* shimmer sweep so the ready button reads as alive, not just appeared — same
   translating-band technique as the progress bar's own sheen, clipped to the
   pill radius by the button's overflow: hidden above. */
#dev .devBarWrap #devGo::after { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 200%;
               background: linear-gradient(105deg, transparent 40%, rgba(255,255,255,0.35) 50%, transparent 60%);
               animation: devGoSheen 1.8s linear infinite; pointer-events: none; }
@keyframes devGoSheen { from { transform: translateX(-100%); } to { transform: translateX(50%); } }
@media (prefers-reduced-motion: reduce) {
  #dev .devBarWrap #devGo::after { animation: none; }
}
/* the control cheat-sheet — the only place it lives now (the splash one is gone) */
#dev .devKeys { display: flex; gap: 8px; margin-top: 22px; flex-wrap: wrap; }
#dev .devKeys .ctrl { display: flex; flex-direction: column; align-items: center; gap: 5px;
                      background: var(--sky-soft); color: var(--navy); border-radius: var(--r-sm);
                      padding: 8px 13px; min-width: 84px; }
#dev .devKeys .ckey { display: flex; align-items: center; gap: 3px; font-size: 15px; font-weight: 700; }
#dev .devKeys .ckey kbd { background: var(--navy); color: #fff; border: none; font-size: 12px;
                          padding: 2px 6px; border-radius: 5px; }
#dev .devKeys .clab { font-size: 11.5px; font-weight: 600; letter-spacing: 0.3px; opacity: 0.8; }

#dev .devSide { width: 330px; flex: none; display: flex; flex-direction: column; gap: 14px; }
/* the patch card: what you're dropping into + the board you're chasing */
#devPatch { border-radius: var(--r-lg); padding: 20px; text-align: left;
            background: rgba(9, 30, 44, 0.86); border: 1.5px solid rgba(143, 214, 243, 0.32);
            box-shadow: var(--shadow); display: flex; flex-direction: column; gap: 8px; }
#devPatch:empty { display: none; }
#devPatch .dpLabel { font: 600 12px var(--font-body); color: var(--sky-soft);
                     letter-spacing: 1.6px; text-transform: uppercase; }
#devPatch .dpName { font-weight: 600; font-size: 17px; color: #fff;
                    letter-spacing: 0.5px; }
#devPatch .dpRules { color: var(--dim); font-size: 12.5px; margin-top: -4px; }
#devPatch .dpBoard { display: flex; flex-direction: column; gap: 3px; }
#devPatch .dpBoard:empty { display: none; }
#devPatch .dpRow { display: flex; align-items: baseline; gap: 10px; padding: 6px 10px;
                   border-radius: var(--r-sm); background: rgba(143, 214, 243, 0.08);
                   color: var(--cloud); font-size: 13.5px; font-variant-numeric: tabular-nums; }
#devPatch .dpRow .r { flex: 0 0 20px; color: var(--dim); font-weight: 700; }
#devPatch .dpRow .nm { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#devPatch .dpRow .sc { color: var(--sky-soft); font-weight: 700; }
#devPatch .dpRow.me { background: rgba(255, 206, 74, 0.18); border: 1px solid rgba(255, 206, 74, 0.5); }
#devPatch .dpRow.me .r, #devPatch .dpRow.me .nm, #devPatch .dpRow.me .sc { color: var(--sun); }
#devPatch .dpEmpty { color: var(--dim); justify-content: center; background: none; }
#devPatch .dpFoot { color: var(--dim); font-size: 12.5px; line-height: 1.5; text-wrap: pretty; }

#dev .devTips { border-radius: var(--r-lg); padding: 20px; display: flex;
                flex-direction: column; gap: 7px; background: rgba(255, 206, 74, 0.1);
                border: 1px solid rgba(255, 206, 74, 0.35); }
#dev .devTips .tLabel { font: 600 12px var(--font-body); color: var(--sun);
                        letter-spacing: 1.6px; text-transform: uppercase; }
#dev .devTips .tText { font-size: 14.5px; line-height: 1.65; color: var(--cloud); text-wrap: pretty; }
#dev .devTips .tDots { display: flex; gap: 5px; margin-top: 4px; }
#dev .devTips .tDots i { width: 18px; height: 4px; border-radius: 999px;
                         background: rgba(255, 206, 74, 0.3); transition: background 0.3s; }
#dev .devTips .tDots i.on { background: var(--sun); }

#dev .devFoot { flex: none; display: flex; align-items: center; gap: 20px; padding-top: 14px;
                border-top: 1px solid rgba(143, 214, 243, 0.14); }
#dev .devFoot .note { font-size: 12px; color: rgba(234, 247, 254, 0.5); text-wrap: pretty; }
#dev .devFoot .credit { margin-left: auto; font-size: 11.5px; color: rgba(234, 247, 254, 0.42);
                        text-align: right; }

/* ===== results ===== */
#results .sheet { max-width: min(96vw, 1060px); }
#results h2 { font-family: var(--font-head); color: var(--sky-soft); font-size: 28px; }
#shots { display: flex; gap: 20px; justify-content: center; margin: 18px 0 10px; flex-wrap: wrap; }
#shots figure { background: var(--cream); padding: 10px 10px 7px; border-radius: var(--r-md);
                box-shadow: 0 14px 36px rgba(8, 30, 45, 0.5); position: relative;
                transform: rotate(-1deg); animation: cardIn 0.45s ease-out backwards; }
#shots figure:nth-child(2) { transform: rotate(1.2deg); animation-delay: 0.15s; }
@keyframes cardIn { from { opacity: 0; transform: translateY(18px) rotate(-2deg); }
                    to { opacity: 1; } }
#shots img { display: block; width: min(36vw, 430px); border-radius: 7px; }
#shots figcaption { color: var(--ink); font-size: 12.5px; padding-top: 6px;
                    font-style: italic; opacity: 0.85; }
/* the verdict is a STICKER slapped inside the photo: solid fill, white edge,
   tight shadow, one sheen sweep after it lands. No screen shake. */
#stamp, .stampTag { position: absolute; top: 16px; right: 16px; transform: rotate(-6deg);
         max-width: calc(100% - 44px); overflow: hidden; white-space: nowrap;
         font-family: var(--font-head); border: 3px solid rgba(255, 255, 255, 0.95);
         border-radius: 10px; padding: 5px 18px; font-size: clamp(24px, 2.6vw, 36px);
         line-height: 1.15; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
         animation: stampIn 0.28s cubic-bezier(0.2, 1.5, 0.4, 1) 0.7s backwards; }
#stamp::after, .stampTag::after { content: ''; position: absolute; inset: 0; pointer-events: none;
                background: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.55) 47%, transparent 62%);
                transform: translateX(-130%);
                animation: stampSheen 0.7s ease-out 1.1s forwards; }
@keyframes stampSheen { to { transform: translateX(130%); } }
@keyframes stampIn { from { opacity: 0; transform: rotate(-6deg) scale(2.6); }
                     60% { opacity: 1; transform: rotate(-6deg) scale(0.94); }
                     to { opacity: 1; transform: rotate(-6deg) scale(1); } }
/* gold is METAL, not yellow: moving metallic sheet so the top tiers read
   instantly different from the amber "so close". The id+class selector is
   deliberate — plain .s-gold would lose the animation slot to #stamp. */
.s-gold { background: linear-gradient(105deg, #d9a417 0%, #ffe082 25%, #f6c33a 45%,
            #c2911a 65%, #ffd964 85%, #d9a417 100%);
          background-size: 200% 100%; color: #4a3705; }
#stamp.s-gold, .stampTag.s-gold {
  animation: stampIn 0.28s cubic-bezier(0.2, 1.5, 0.4, 1) 0.7s backwards,
             goldShimmer 2.8s linear 1s infinite; }
@keyframes goldShimmer { from { background-position: 0% 0; } to { background-position: -200% 0; } }
.s-green { background: #2fbe7e; color: #0b3524; }
.s-yel { background: #ffb84a; color: #4a3105; }
.s-org { background: #ff9c5c; color: #4d2a08; }
.s-red { background: #ff7488; color: #47101c; }
#verdict { font-size: 15px; color: var(--cloud); margin-top: 8px; }
#verdict b { color: var(--sun); font-weight: 600; }
/* legibility pass 2026-08-20: fine print lightened dim -> sky-soft, one
   step up in the same blue family, at this small size dim read too faint. */
#verdict .sub { color: var(--sky-soft); font-size: 13px; }
#scoreLine { font-size: 34px; color: var(--sky-soft); font-weight: 700; margin-top: 6px;
             font-variant-numeric: tabular-nums; }
#totalLine { font-size: 14px; color: var(--dim); }

/* ===== offline ===== */
#offline h2 { font-family: var(--font-head); color: var(--sun); font-size: 28px; }
#offline p { margin-top: 12px; color: var(--cloud); font-size: 14px; line-height: 1.8; }
#offline code { color: var(--sky-soft); background: rgba(41,171,226,0.14);
                padding: 1px 7px; border-radius: 6px; }

/* ===== lightbox: big reference ===== */
#lightbox { display: none; background: rgba(8, 26, 38, 0.92); z-index: 45; cursor: zoom-out; }
#lightbox .refcard { max-width: min(92vw, 1280px); transform: none; cursor: grab; }
#lightbox .refcard:active { cursor: grabbing; }

/* ===== responsive ===== */
@media (min-width: 921px) {
  #mtop, #refbox { display: none !important; }
}
@media (max-width: 920px) {
  #side { display: none !important; }
  /* compass centred up top (compact on phones); the score pill + reference
     polaroid tuck just beneath it, left and right, with their centres aligned */
  #compass { top: calc(env(safe-area-inset-top, 8px) + 8px); width: min(300px, 76vw);
             padding: 6px 12px 8px; }
  #compass .cbar { height: 22px; }
  #compass .cdeg { font-size: 10.5px; margin-top: 2px; letter-spacing: 1px; }
  #compass .carea { font-size: 14px; }
  #compass .cstreet { font-size: 11.5px; }
  #compass .cmark { font-size: 11px; }
  #compass .cmark.card { font-size: 14px; }
  #mtop { top: calc(env(safe-area-inset-top, 8px) + 120px); left: 12px; transform: none; }
  #refbox { top: calc(env(safe-area-inset-top, 8px) + 96px); width: 158px; }
  /* keep the map attribution legible but compact, and clear of the LOOK stick */
  .cesium-widget-credits { font-size: 10px !important; line-height: 1.3 !important; max-width: 62vw !important; }
  #shots img { width: 70vw; max-width: 460px; }
  /* game-over content (with leaderboard) can exceed a phone screen — scroll it */
  #splash { justify-content: flex-start; padding-top: 28px;
            padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 40px); }
  #results { justify-content: flex-start; overflow-y: auto; -webkit-overflow-scrolling: touch;
             padding-top: calc(env(safe-area-inset-top, 8px) + 56px);
             padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 96px); }
}
/* Mobile: the 3D view fills the whole screen in BOTH orientations (the old
   portrait letterbox band is gone — thumbs kept missing it). FOV parity with
   the reference is handled in JS: updateFov() pins the HORIZONTAL slice to
   60° on a portrait canvas (Cesium reads frustum.fov as vertical there), so
   the viewfinder frames identically on every device. Stick input is captured
   on the whole #stage, not just the canvas. */
@media (pointer: fine) { #touchCtl { display: none !important; } }

/* ===== the fence editor: shared parts =====
   There is no picker SCREEN any more — the editor's chrome is #drawBar and the
   ready-to-fly module is #apConfirm, both bottom-anchored panels on the Explore
   map. The mode rules that show and hide them live with the pane, further down. */
.apInfo { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* the area you are outlining is a patch name standing as a label */
.apInfo .apPlace { font-weight: var(--pname-weight, 600); color: #fff; font-size: 16.5px;
                   letter-spacing: var(--pname-track, 0.05em);
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.apInfo .apSpan { color: var(--dim); font-size: 12.5px; }
.apInfo .apBest { color: var(--sun); font-size: 12.5px; }
.apInfo .apWarn { color: var(--sun); font-size: 12.5px; display: none; }
.apInfo .apHint { color: var(--sun); font-size: 12.5px; }
.apBtns { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* legibility pass 2026-08-20: label lightened sky-soft -> cloud (same
   family, already the house "primary text on dark" token) — sky-soft on the
   navy fill sat too close in hue/lightness to read as a button. */
#apLocate, #apReset, #drawCancel { cursor: pointer; background: rgba(143,214,243,0.14); color: var(--cloud);
            border: 1px solid rgba(143,214,243,0.32); border-radius: 999px;
            padding: 9px 15px; font: 600 13px var(--font-body); white-space: nowrap; }
#apLocate:hover, #apReset:hover, #drawCancel:hover { background: rgba(41,171,226,0.25); color: #fff; }
#drawCancel { background: none; border-color: rgba(143,214,243,0.2); color: var(--sky-soft); }
#apPlay { margin: 0; padding: 11px 26px; font-size: 15px; }
#apPlay:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
/* draggable boundary pins — a white ring (crisp against any terrain) with an
   accent dot at the centre, like a target: reads as "grab here", not as a
   pin. ::after carries the dot so the markup stays a single span. The drop
   shadow is neutral dark (not the sky-tinted one it used to share with the
   pin dots) so the handle visibly lifts off the map instead of blending. */
.apPin span { position: relative; display: block; width: 28px; height: 28px; border-radius: 50%;
              background: #fff; box-shadow: 0 3px 10px rgba(2,10,16,0.6), 0 0 0 1px rgba(2,10,16,0.2);
              cursor: grab; transition: transform .15s ease; }
.apPin span::after { content: ''; position: absolute; inset: 7px; border-radius: 50%; background: var(--sky); }
.apPin span:hover { transform: scale(1.14); }
.apPin span:active { cursor: grabbing; }

/* the pilot's own position on the Explore map — quiet and non-interactive.
   pointer-events:none is the WHOLE guard now: a MapLibre DOM marker has no
   "interactive" option, so without this the person marker would swallow clicks
   meant for a patch pin underneath it. */
.svMeIcon { pointer-events: none; transition: opacity .22s ease; }
.svMeIcon span { display: flex; align-items: center; justify-content: center;
                 width: 22px; height: 22px; border-radius: 50%; background: var(--navy);
                 border: 2px solid var(--sky); box-shadow: 0 0 0 3px rgba(41, 171, 226, 0.28), 0 2px 7px rgba(8,30,45,0.55); }
.svMeIcon svg { width: 12px; height: 12px; color: #fff; }

/* The hero (ready-to-fly) fence's glow is NOT CSS any more: the fence is GL
   geometry on the map canvas, which no stylesheet can reach. It is the
   `fenceGlow-line` layer (a wide, blurred, low-opacity line), added in
   addFenceLayers in account.js and switched on by apRender in main.js. */

/* the rules, for a NEW patch only: an icon and its number, no label to read */
.apSettings { display: flex; gap: 8px; align-items: center; }
.rulePick { display: flex; align-items: center; gap: 7px; padding: 6px 12px;
            background: rgba(8,26,38,0.85); border: 1px solid rgba(143,214,243,0.3);
            border-radius: 999px; color: var(--sky-soft); cursor: pointer; }
.rulePick:focus-within { border-color: var(--sky); }
.rulePick .lucide { width: 16px; height: 16px; flex: none; }
.rulePick select, .rulePick input { background: none; border: none; outline: none;
            color: var(--cloud); font: 600 14px var(--font-body); cursor: pointer; }
.rulePick select option { background: #0b2433; color: var(--cloud); }
/* seconds is typed, not picked — narrow field, spinners kept (they are the
   quickest way to nudge it) */
.rulePick input[type=number] { width: 46px; cursor: text; }
.rulePick .ruleUnit { color: var(--dim); font: 600 13px var(--font-body); }

/* the one way back into edit mode for a patch that is otherwise read-only */
/* legibility pass 2026-08-20: label lightened sky-soft -> cloud, see
   #apLocate above. */
#apDuplicate { display: none; cursor: pointer; align-self: flex-start; margin-top: 12px;
               background: rgba(143,214,243,0.1); color: var(--cloud);
               border: 1px solid rgba(143,214,243,0.28); border-radius: 999px;
               padding: 6px 14px; font: 600 12px var(--font-body); }
#apDuplicate:hover { background: rgba(41,171,226,0.2); color: #fff; }

/* ready to fly: the footer actions */
.apConfirmBtns { display: flex; gap: 10px; justify-content: flex-end; align-items: center; margin-top: 16px; }
/* legibility pass 2026-08-20: label lightened sky-soft -> cloud, see
   #apLocate above. */
#apConfirmNo { cursor: pointer; background: rgba(143,214,243,0.12); color: var(--cloud);
               border: 1px solid rgba(143,214,243,0.3); border-radius: 999px; padding: 11px 18px;
               font: 600 14px var(--font-body); white-space: nowrap; }
#apConfirmNo:hover { background: rgba(41,171,226,0.22); color: #fff; }
/* Start must clearly outrank Cancel / Duplicate beside it — size and weight
   carry that, not color (house rule: no colored glow on a button at rest). */
#apConfirmYes { margin: 0; padding: 13px 26px; font-size: 16px; font-weight: 700;
                box-shadow: 0 7px 18px rgba(8, 30, 45, 0.55); }

/* ============================================================================
   v2 — accounts, tokens, shop, patches, drone-footage splash
   ============================================================================ */

/* ===== drone footage layer (behind splash + developing overlays) ===== */
#vidLayer { position: fixed; inset: 0; z-index: 28; display: none; pointer-events: none;
            background: #06161f; }
#vidLayer.on { display: block; }
#vidLayer video { width: 100%; height: 100%; object-fit: cover;
                  filter: saturate(1.08) brightness(0.72); }
#vidCredit { position: absolute; right: 14px; bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);
             font-size: 11px; color: rgba(234, 247, 254, 0.55); letter-spacing: 0.3px; }
/* when footage plays, thin out the splash wash so the city shows through */
#splash.vid { background: linear-gradient(180deg,
              rgba(6, 20, 30, 0.42) 0%, rgba(6, 20, 30, 0.6) 55%, rgba(6, 18, 27, 0.88) 100%); }
#splash.vid.developing { background: rgba(6, 18, 27, 0.72); }
#dev { z-index: 30; }
/* the poster's own blurred still replaces the footage — two moving/blurred
   backgrounds fight. account.js keeps #vidLayer off whenever #dev.hasShot. */
#dev.hasShot { background: #06161f; }

/* ===== auth box (signed out) ===== */
#authBox { display: none; margin-top: 18px; width: min(480px, 92vw); }
#authBox .brief { margin-top: 0; }
.authFields { display: flex; gap: 8px; margin-top: 16px; }
#authEmail { flex: 1; min-width: 0; border: 1px solid rgba(143, 214, 243, 0.4); border-radius: 999px;
             background: rgba(8, 28, 40, 0.75); color: var(--cloud); padding: 12px 18px;
             font: 500 15px var(--font-body); outline: none; }
#authEmail:focus { border-color: var(--sky); }
#authEmail::placeholder { color: var(--dim); }
#authMagic { cursor: pointer; border: none; border-radius: 999px; padding: 12px 20px;
             font: 600 14px var(--font-body); color: #fff; background: var(--sky);
             box-shadow: 0 5px 14px rgba(8, 30, 45, 0.45); white-space: nowrap;
             transition: filter .12s, transform .08s; }
#authMagic:hover { filter: brightness(1.08); }
#authMagic:active { transform: scale(0.97); }
#authMagic:disabled { opacity: 0.55; cursor: default; }
#authGoogle { cursor: pointer; margin-top: 10px; width: 100%; border-radius: 999px;
              padding: 11px 20px; font: 600 14px var(--font-body);
              color: var(--cloud); background: rgba(143, 214, 243, 0.12);
              border: 1px solid rgba(143, 214, 243, 0.32); transition: background .15s; }
#authGoogle:hover { background: rgba(41, 171, 226, 0.25); color: #fff; }
#authNote { margin-top: 10px; min-height: 20px; font-size: 13.5px; color: var(--mint); font-weight: 500; }
#authNote.err { color: var(--coral); }
/* Hidden until renderAuthUI decides it has something to say. In free-play mode
   it stays hidden: the pitch says the game is free in its own words, and a pill
   repeating it is the same fact twice. Default-hidden rather than hidden-on-load
   so the index.html fallback text can't flash before /config lands. */
.authPerk { display: none; margin-top: 12px; font-size: 13.5px; color: var(--cloud);
            background: rgba(255, 206, 74, 0.1); border: 1px solid rgba(255, 206, 74, 0.35);
            border-radius: var(--r-md); padding: 10px 16px; }
.authPerk b { color: var(--sun); }

/* Touch devices get told, up front and at the decision point, that this plays
   better on a real machine. Hidden entirely on a pointer:fine device — the same
   test main.js uses for the joysticks, so the warning lands exactly where the
   touch controls do. The footer says it too, quietly, so that copy stands down
   here rather than saying it twice. */
.mobileNote { display: none; margin-top: 14px; font: 600 14.5px var(--font-body);
              line-height: 1.55; color: #fff2cf;
              background: rgba(255, 206, 74, 0.13);
              border: 1px solid rgba(255, 206, 74, 0.45);
              border-radius: var(--r-md); padding: 13px 18px; }
@media (pointer: coarse) {
  .mobileNote { display: block; }
  .sysNote .sysReq { display: none; }
}

/* ===== pilot bar (signed in) ===== */
#pilotBar { display: none; align-items: center; gap: 10px; margin-top: 16px; flex-wrap: wrap;
            justify-content: center; background: rgba(11, 35, 51, 0.82);
            border: 1px solid rgba(143, 214, 243, 0.26); border-radius: 999px; padding: 8px 12px; }
/* the name is a control now: one place to set it, reachable from every screen */
#pilotBar .pName { font: 600 14px var(--font-body); line-height: 20px; color: var(--cloud); padding: 4px 8px;
                   max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
                   background: none; border: 1px solid transparent; border-radius: 999px;
                   cursor: pointer; transition: border-color .12s, color .12s; }
#pilotBar .pName:hover { color: #fff; border-color: rgba(143, 214, 243, 0.35); }
#pilotBar .pStat { font: 500 12.5px var(--font-body); line-height: 20px; color: var(--dim); white-space: nowrap;
                   padding: 4px 0; border: 1px solid transparent; }
#pilotBar .pStat:empty { display: none; }
#pilotBar .pTokens { cursor: pointer; border: 1px solid rgba(255, 206, 74, 0.5); border-radius: 999px;
                     background: rgba(255, 206, 74, 0.14); color: var(--sun);
                     font: 600 14px var(--font-body); padding: 6px 14px; }
#pilotBar .pTokens:hover { background: rgba(255, 206, 74, 0.26); }
#pilotBar .pBuy { cursor: pointer; border: none; border-radius: 999px; padding: 7px 14px;
                  font: 600 13px var(--font-body); color: #fff; background: var(--sky); }
#pilotBar .pBuy:hover { filter: brightness(1.08); }
#pilotBar .pOut { cursor: pointer; background: none; border: none; color: var(--dim);
                  font: 500 12.5px var(--font-body); text-decoration: underline; }
#pilotBar .pOut:hover { color: var(--cloud); }

/* ===== menu + saved patches ===== */
#menu { display: none; }
#patches { margin-top: 20px; width: min(600px, 94vw); }
.patchTitle { font: 600 12px var(--font-body); color: var(--dim); letter-spacing: 1.6px;
              text-transform: uppercase; text-align: left; margin-bottom: 8px; }
#patchList { display: flex; flex-direction: column; gap: 7px; max-height: 34vh; overflow-y: auto;
             overflow-x: hidden; padding-right: 2px; }
.patch { display: grid; grid-template-columns: 1fr auto; grid-template-rows: auto auto auto; gap: 1px 12px;
         text-align: left; cursor: pointer; padding: 10px 16px;
         background: none; border: none; border-bottom: 1px solid rgba(143, 214, 243, 0.24);
         color: var(--cloud); font-family: var(--font-body); transition: background .12s; }
.patch:hover { background: rgba(18, 60, 85, 0.9); }
.patch .pPlace { font-weight: var(--pname-weight); font-size: 15px; color: #fff; grid-column: 1;
                 letter-spacing: var(--pname-track); }
.patch .pRules { font-size: 12.5px; color: var(--dim); grid-column: 1; }
.patch .pRules .lucide, .pcRules .lucide { width: 13px; height: 13px; vertical-align: -2px;
              color: var(--sky-soft); flex: none; }
.patch .pRules b, .pcRules b { font-weight: 600; }
.patch .pLoc { grid-column: 1; grid-row: 3; font-size: 12px; color: var(--dim); white-space: nowrap;
               overflow: hidden; text-overflow: ellipsis; }
.patch .pLoc:empty { display: none; }
.patch .pBest { font-size: 12.5px; color: var(--sun); grid-column: 2; grid-row: 1 / span 3;
                align-self: center; }
/* the row SELECTS (its board opens in the rail); .pFly beside it is what spends
   a flight window — same split the cards above already use */
.patch.on { border-color: var(--sky); background: rgba(41, 171, 226, 0.18); }
.patchEmpty { color: var(--dim); font-size: 14px; padding: 12px; text-align: center;
              background: rgba(11, 35, 51, 0.6); border-radius: var(--r-md);
              border: 1px dashed rgba(143, 214, 243, 0.3); }

/* ===== token shop ===== */
#shop { background: rgba(6, 18, 27, 0.88); z-index: 44; }
.shopSheet { max-width: min(94vw, 760px); }
.shopTitle { font-family: var(--font-head); color: var(--sky); font-weight: 400; font-size: 30px; }
.shopSub { color: var(--cloud); font-size: 14.5px; line-height: 1.65; margin-top: 8px;
           max-width: 540px; margin-left: auto; margin-right: auto; }
.shopSub b { color: var(--sun); font-weight: 600; }
#packRow { display: flex; gap: 14px; justify-content: center; margin-top: 22px; flex-wrap: wrap; }
.pack { position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px;
        cursor: pointer; width: 170px; padding: 20px 12px 16px; border-radius: var(--r-md);
        background: rgba(11, 35, 51, 0.92); border: 1.5px solid rgba(143, 214, 243, 0.3);
        color: var(--cloud); font-family: var(--font-body);
        transition: transform .1s, border-color .12s, box-shadow .15s; }
.pack:hover { transform: translateY(-3px); border-color: var(--sky);
              box-shadow: 0 10px 22px rgba(8, 30, 45, 0.5); }
.pack.hot { border-color: var(--sun); background: rgba(30, 48, 38, 0.35); }
.packTag { position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
           background: var(--sun); color: #3a2c05; font-size: 10.5px; font-weight: 700;
           letter-spacing: 0.6px; text-transform: uppercase; border-radius: 999px; padding: 3px 12px;
           white-space: nowrap; }
.packEmoji { font-size: 30px; }
.packN { font-size: 19px; font-weight: 700; color: #fff; }
.packPrice { font-size: 16px; font-weight: 600; color: var(--sun); }
.packBlurb { font-size: 11.5px; color: var(--dim); }
.shopFoot { display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 22px; }
#shopClose { cursor: pointer; background: rgba(143, 214, 243, 0.12); color: var(--sky-soft);
             border: 1px solid rgba(143, 214, 243, 0.3); border-radius: 999px; padding: 9px 18px;
             font: 600 13px var(--font-body); }
#shopClose:hover { background: rgba(41, 171, 226, 0.22); color: #fff; }
.shopNote { color: var(--dim); font-size: 12px; }

/* ===== publish sheet: the one confirmation before a patch goes public ===== */
#pubSheet { background: rgba(6, 18, 27, 0.88); z-index: 50; }
.pubCard { max-width: min(92vw, 420px); text-align: left; }
.pubLabel { font: 600 12.5px var(--font-body); color: var(--sky-soft);
            letter-spacing: 2.4px; text-transform: uppercase; }
.pubBody { margin-top: 10px; color: var(--cloud); font-size: 14px; line-height: 1.6; }
#pubNameInput { margin-top: 14px; width: 100%; box-sizing: border-box; padding: 11px 14px;
                border-radius: var(--r-sm); background: rgba(11, 35, 51, 0.85);
                border: 1px solid rgba(143, 214, 243, 0.3); color: #fff;
                font: 600 14px var(--font-body); }
#pubNameInput:focus { outline: none; border-color: var(--sky); }
.pubBtns { margin-top: 18px; display: flex; gap: 10px; justify-content: flex-end; }
#pubCancel { cursor: pointer; background: rgba(143, 214, 243, 0.12); color: var(--sky-soft);
             border: 1px solid rgba(143, 214, 243, 0.3); border-radius: 999px; padding: 9px 18px;
             font: 600 13px var(--font-body); }
#pubCancel:hover { background: rgba(41, 171, 226, 0.22); color: #fff; }
#pubConfirm { margin-top: 0; padding: 9px 22px; font-size: 14px; }
#pubConfirm:disabled { opacity: 0.55; cursor: default; }

/* ===== quota sheet: the free-play cap deny, same language as the publish
   sheet but sentence case (this is a heading, not an eyebrow) ===== */
#quotaSheet { background: rgba(6, 18, 27, 0.88); z-index: 60; }
.quotaCard { max-width: min(92vw, 420px); text-align: left; }
.quotaLabel { font: 600 12.5px var(--font-body); color: var(--sky-soft); letter-spacing: 0.3px; }
.quotaBody { margin-top: 10px; color: var(--cloud); font-size: 14px; line-height: 1.6; }
.quotaBtns { margin-top: 18px; display: flex; justify-content: flex-end; }
#quotaOk { margin-top: 0; padding: 9px 22px; font-size: 14px; }

/* ===== confirm cost line + next pilot ===== */
.apCost { margin-top: 12px; font-size: 14px; font-weight: 600; color: var(--sun);
          background: rgba(255, 206, 74, 0.1); border: 1px solid rgba(255, 206, 74, 0.3);
          border-radius: var(--r-sm); padding: 8px 14px; display: inline-block; }
.apCost.free { color: var(--mint); background: rgba(68, 192, 138, 0.12);
               border-color: rgba(68, 192, 138, 0.4); }
/* free-play leaves this empty — an empty pill still painted its border */
.apCost:empty { display: none; }
#nextPilotBtn { margin-top: 0; font-size: 15px; padding: 12px 26px; }

@media (max-width: 620px) {
  .authFields { flex-direction: column; }
  #packRow { gap: 10px; }
  .pack { width: 46%; min-width: 140px; }
}

/* ===== patch row: card + a fixed icon column, nothing overflows ===== */
.patchRow { display: flex; gap: 6px; align-items: stretch; min-width: 0; }
.patchRow .patch { flex: 1 1 auto; min-width: 0; }
.pSide { flex: none; display: flex; align-items: stretch; gap: 6px; }
/* Secondary on purpose: the list is for LOOKING at patches (the row selects and
   opens its board), and three filled blue pills down a column would shout over
   the cards above, which already carry the primary Fly. */
.pFly { cursor: pointer; flex: none; border: 1.5px solid var(--sky); border-radius: var(--r-md);
        padding: 0 16px; background: rgba(41, 171, 226, 0.12); color: var(--sky-soft);
        font: 600 13.5px var(--font-body); white-space: nowrap; }
.pFly:hover { background: var(--sky); color: #fff; }
.pMini { cursor: pointer; width: 40px; flex: none; display: flex; align-items: center; justify-content: center;
         padding: 0; border-radius: var(--r-sm);
         background: rgba(11, 35, 51, 0.85); border: 1px solid rgba(143, 214, 243, 0.24);
         color: var(--sky-soft); transition: background .12s, border-color .12s, color .12s; }
.pMini svg { width: 15px; height: 15px; }
.pMini:hover { background: rgba(18, 60, 85, 0.9); border-color: var(--sky); color: #fff; }
/* .on marks "already published" — a lit indicator, not a control, so it gets
   the default cursor even though it shares .pMini's look with the buttons. */
.pMini.on { cursor: default; border-color: rgba(68, 192, 138, 0.6); background: rgba(68, 192, 138, 0.14); color: var(--mint); }
#popular { margin-top: 20px; width: min(600px, 94vw); }
#popularList { display: flex; flex-direction: column; gap: 7px; max-height: 30vh; overflow-y: auto; overflow-x: hidden; }
.patch.pop .pBest { color: var(--sky-soft); }

/* ===== v3: roster, two-column game over, focus hint, place search, governor ===== */

/* wider sidebar on big screens — the reference photo is the co-star (#7) */
#side { width: clamp(408px, 29vw, 640px); }

/* pilot roster */
.rosterSheet { max-width: 480px; text-align: center; }
.rosterSheet h2 { font-family: var(--font-head); color: var(--sky); font-weight: 400; font-size: 27px; }
.rosterSheet p { color: #dfeff8; font-size: 14px; line-height: 1.6; margin-top: 8px; }
#rosterChips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 18px 0 4px;
               min-height: 34px; }
.chip { display: inline-flex; align-items: center; gap: 7px; background: rgba(41, 171, 226, 0.18);
        border: 1px solid rgba(143, 214, 243, 0.45); border-radius: 999px; padding: 6px 8px 6px 14px;
        font: 600 14px var(--font-body); color: #fff; }
.chip button { cursor: pointer; border: none; border-radius: 50%; width: 20px; height: 20px;
               line-height: 1; font-size: 11px; background: rgba(8, 28, 40, 0.55); color: var(--sky-soft); }
.chip button:hover { background: var(--coral); color: #fff; }
.chipEmpty { color: var(--dim); font-size: 13.5px; padding: 7px 0; }
.rosterAdd { display: flex; gap: 8px; justify-content: center; margin-top: 10px; }
#rosterInput { flex: 1; max-width: 240px; border: 1px solid rgba(143, 214, 243, 0.4); border-radius: 999px;
               background: rgba(8, 28, 40, 0.7); color: var(--cloud); padding: 10px 16px;
               font: 500 14px var(--font-body); outline: none; }
#rosterInput:focus { border-color: var(--sky); }
#rosterAddBtn { cursor: pointer; border: none; border-radius: 999px; padding: 10px 18px;
                font: 600 13px var(--font-body); color: #fff; background: var(--sky); }
.rosterBtns { display: flex; gap: 12px; justify-content: center; align-items: center; margin-top: 20px; }
#rosterBack { cursor: pointer; background: rgba(143, 214, 243, 0.12); color: var(--sky-soft);
              border: 1px solid rgba(143, 214, 243, 0.3); border-radius: 999px; padding: 11px 18px;
              font: 600 14px var(--font-body); }
#rosterBack:hover { background: rgba(41, 171, 226, 0.22); color: #fff; }
#rosterGo { margin: 0; }

/* game over: summary left, board right (stacks on phones). The right column
   is a step narrower than it used to be (0.6fr/260px -> 0.5fr/220px) so the
   board reads as supporting the score, not competing with it. */
.fsGrid { display: grid; grid-template-columns: minmax(0, 1.8fr) minmax(220px, 0.5fr);
          gap: 12px 36px; align-items: center; width: min(94vw, 960px);
          margin: 0 auto; text-align: center; }
.fsPilot { color: var(--dim); font-size: 13.5px; letter-spacing: 0.4px; margin-bottom: 2px; }
.fsGrid #lb { margin-top: 6px; width: auto; }
#finalActions { margin-top: 20px; }
@media (max-width: 860px) {
  .fsGrid { grid-template-columns: 1fr; gap: 8px; }
}

/* preview: pilot line */
.pvpilot { color: var(--sun); font-size: 15px; font-weight: 600; margin: -8px 0 12px; }

/* click-to-focus hint */
#focusHint svg { width: 17px; height: 17px; vertical-align: -3px; margin-right: 6px; }
#focusHint { position: absolute; display: none; left: 50%; top: 58%; transform: translateX(-50%);
             z-index: 13; pointer-events: none; white-space: nowrap;
             background: rgba(11, 35, 50, 0.92); border: 1px solid rgba(255, 206, 74, 0.55);
             color: var(--sun); font: 600 15px var(--font-body); letter-spacing: 0.3px;
             border-radius: 999px; padding: 10px 22px;
             animation: hintPulse 1.6s ease-in-out infinite; }
@keyframes hintPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.55; } }

/* place search: floats over the Explore map itself (browse AND draw mode —
   see #paneExplore.mode-confirm .mapSearch below for the one mode it hides
   in), not tied to the draw bar any more. Stacked above .mpList rather than
   beside it, so it never collides with .mpPick sitting on the right no
   matter the screen width — see .mpList's top offset further down.
   ONE rule for both classes, not one each: .apSearch and .mapSearch land on
   the same element now (nothing else uses .apSearch any more), and splitting
   position/width across two same-specificity class rules lets source order —
   not either rule's own intent — decide which value wins. It did: .apSearch's
   width:100% used to stretch the box to the whole pane and swallow #mpPick. */
.apSearch.mapSearch { position: absolute; top: 16px; left: 16px; z-index: 5;
                       width: min(300px, calc(100vw - 24px)); }
#paneExplore.mode-confirm .mapSearch { display: none; }
#apSearch { width: 100%; border: 1px solid rgba(143, 214, 243, 0.35); border-radius: 999px;
            background: rgba(8, 28, 40, 0.8); color: var(--cloud); padding: 10px 18px;
            font: 500 14px var(--font-body); outline: none; }
#apSearch:focus { border-color: var(--sky); }
#apSearch::placeholder { color: var(--dim); }
#apSearchResults { position: absolute; top: 44px; left: 0; right: 0; z-index: 5; display: none;
                   background: rgba(10, 32, 46, 0.98); border: 1px solid rgba(143, 214, 243, 0.3);
                   border-radius: var(--r-md); overflow: hidden; box-shadow: var(--shadow); }
#apSearchResults button { display: block; width: 100%; text-align: left; cursor: pointer; border: none;
                          background: none; color: var(--cloud); font: 500 13px var(--font-body);
                          padding: 10px 16px; border-bottom: 1px solid rgba(143, 214, 243, 0.1); }
#apSearchResults button:hover { background: rgba(41, 171, 226, 0.2); }
#apSearchResults .noHit { display: block; color: var(--dim); font-size: 12.5px; padding: 10px 16px; }

/* confetti */
.confetti { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.confetti i { position: absolute; display: block; border-radius: 2px; opacity: 0;
              animation: confettiFly 2.2s cubic-bezier(0.15, 0.6, 0.35, 1) forwards; }
@keyframes confettiFly {
  0% { opacity: 1; transform: translate(0, 0) rotate(0); }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx), calc(var(--dy) + 46vmin)) rotate(var(--r)); }
}

/* small print */
.sysNote { margin-top: 16px; color: var(--dim); font-size: 12px; letter-spacing: 0.2px; }

/* ===== v5: session standings ===== */
/* The solo/party fork used to be a full screen here. It now lives on the
   confirm sheet (.apMode) — see index.html #apConfirm. */

/* Draw a new patch: the one CTA on the dashboard that must never be below the
   fold, so it's a solid pill and it is never hidden. */
#drawBtn { display: inline-flex; align-items: center; cursor: pointer; border-radius: 999px;
           padding: 12px 26px; font: 600 14.5px var(--font-body); border: none;
           background: var(--sky); color: #fff; white-space: nowrap;
           transition: filter .12s, transform .08s; }
#drawBtn:hover { filter: brightness(1.08); }
#drawBtn:active { transform: scale(0.97); }

/* session standings (group finish line) */
#sessionWrap { display: none; margin: 18px auto 0; width: min(440px, 92vw); }
#sessionWrap .lbTitle { color: var(--sun); }
#sessionList { list-style: none; display: flex; flex-direction: column; gap: 3px; }
#sessionList li { display: flex; align-items: baseline; gap: 10px; padding: 7px 12px;
                  border-radius: var(--r-sm); background: rgba(143, 214, 243, 0.1);
                  font-variant-numeric: tabular-nums; }
#sessionList li .r { flex: 0 0 24px; color: var(--dim); font-weight: 700; text-align: right; }
#sessionList li .nm { flex: 1; text-align: left; color: var(--cloud); overflow: hidden;
                      text-overflow: ellipsis; white-space: nowrap; }
#sessionList li .sc { color: var(--sky-soft); font-weight: 700; }
#sessionList li.me { background: rgba(255, 206, 74, 0.18); border: 1px solid rgba(255, 206, 74, 0.5); }
#sessionList li.me .nm, #sessionList li.me .sc, #sessionList li.me .r { color: var(--sun); }
/* podium chips — same treatment as .lbRows (#mpBoard), see that block's
   comment for why this reads after .me. */
#sessionList li:nth-child(1) .r, #sessionList li:nth-child(2) .r, #sessionList li:nth-child(3) .r {
  flex-basis: 20px; height: 20px; line-height: 20px; text-align: center;
  border-radius: 50%; align-self: center; font-weight: 800;
}
#sessionList li:nth-child(1) .r { background: rgba(255, 206, 74, 0.22);
  box-shadow: inset 0 0 0 1px rgba(255, 206, 74, 0.45); color: var(--sun); }
#sessionList li:nth-child(2) .r { background: rgba(180, 202, 212, 0.18);
  box-shadow: inset 0 0 0 1px rgba(180, 202, 212, 0.42); color: #c9d8e0; }
#sessionList li:nth-child(3) .r { background: rgba(184, 138, 94, 0.16);
  box-shadow: inset 0 0 0 1px rgba(184, 138, 94, 0.4); color: #c99b70; }

/* action buttons swap primary/ghost with the session state */
/* legibility pass 2026-08-20: label lightened sky-soft -> cloud, see
   #apLocate above (this rule wins the cascade over the #shareBtn block
   above — same specificity, later in file — so both need the change). */
#nextPilotBtn:not(.btn), #newPatchBtn:not(.btn) {
  cursor: pointer; background: rgba(143, 214, 243, 0.14); color: var(--cloud);
  border: 1px solid rgba(143, 214, 243, 0.32); border-radius: 999px;
  padding: 9px 20px; font: 600 13px var(--font-body); transition: background .15s; }
#nextPilotBtn:not(.btn):hover, #newPatchBtn:not(.btn):hover { background: rgba(41, 171, 226, 0.25); color: #fff; }
#newPatchBtn.btn, #nextPilotBtn.btn { font-size: 15px; padding: 12px 26px; margin-top: 0; }

/* setup sheet boxes */
#soloBox p, #groupBox p { color: #dfeff8; font-size: 14px; line-height: 1.6; margin-top: 8px; }
#aliasInput { width: min(260px, 80%); border: 1px solid rgba(143, 214, 243, 0.4); border-radius: 999px;
              background: rgba(8, 28, 40, 0.7); color: var(--cloud); padding: 10px 16px;
              font: 500 15px var(--font-body); outline: none; text-align: center; }
#aliasInput:focus { border-color: var(--sky); }

/* ===== v6: svg polish on the stragglers ===== */
#authGoogle svg { width: 16px; height: 16px; vertical-align: -3px; margin-right: 9px; }
.chip button svg { width: 10px; height: 10px; display: block; margin: auto; }

/* ===== v7: splash rhythm, fence center-handle, patch naming ===== */
#splash .logo { width: clamp(210px, 36vw, 360px); }
/* (the old `.tag` override lived here — it set a 15px subtitle margin-top,
   which fights the eyebrow rule above now that the tag sits over the logo) */
#pilotBar { margin-top: 26px; }
#menu { flex-direction: column; align-items: center; gap: 24px; margin-top: 28px;
        width: min(620px, 94vw); }
#patches, #popular { margin-top: 0; width: 100%; }
/* It had no max-width, so it ran the full window while everything above it sits
   in a ~500px column — which is what made it read as unwrapped. Slightly wider
   than the pitch card so it doesn't look pinched, and no wider. */
.sysNote { margin-top: 18px; max-width: min(560px, 92vw); line-height: 1.65;
           /* the footage behind this is DAYLIGHT — dim grey on a bright sky
              needs the same shadow .brief gets, or the byline disappears */
           text-shadow: 0 1px 3px rgba(8, 30, 45, 0.75); }
.sysNote p { margin: 0; }
/* The joke is flavour, the line above it is the actual requirement — so the
   byline carries less weight rather than the same. */
.sysNote .byline { margin-top: 6px; font-size: 11.5px; opacity: 0.82; }
.sysNote .sysLinks { margin-top: 9px; }
.sysNote a { color: var(--dim); }
#finalSummary { margin-top: 22px; }
#finalSummary .ftotal { font-size: clamp(96px, 11vw, 210px); }
/* .rank is id-specific here (#finalSummary .rank at line ~352 would otherwise
   outrank .fsSheet .rank below on specificity alone) — same trap as .ftotal,
   fixed the same way: override it here too, kept in sync with .fsSheet .rank. */
#finalSummary .rank { font-size: clamp(31px, 2.7vw, 48px); }

/* ===== v8: the map's night styling (the shell owns layout — see v10) ===== */
#menuMap { height: 330px; background: #0a2231; }
/* MapLibre puts .maplibregl-map on the CONTAINER itself, not a child. */
#menuMap.maplibregl-map { background: #0a2231; }
/* The ⚙ UI tab's tunable colorize. It rides on the GL canvas now, so it tints
   the whole map INCLUDING the fences and the selection ring (the pins are DOM
   and stay untouched) — the vector style in public/mapstyle.js is already
   painted in the house blues, so these five knobs ship NEUTRAL and exist for
   taste, not to rescue a greyscale basemap the way the sepia-first raster
   chain did. */
#menuMap .maplibregl-canvas { filter: sepia(var(--map-tint, 0)) hue-rotate(var(--map-hue, 0deg))
                               saturate(var(--map-sat, 1))
                               brightness(var(--map-bright, 1)) contrast(var(--map-contrast, 1)); }
#menuMap .maplibregl-ctrl-attrib { background: rgba(11, 35, 51, 0.8); color: var(--dim); }
#menuMap .maplibregl-ctrl-attrib a { color: var(--sky-soft); }
/* the compact form's toggle: MapLibre's default is a dark glyph on a white
   disc, which is a headlight in the corner of a night map */
#menuMap .maplibregl-ctrl-attrib-button { background-color: transparent;
                                          filter: invert(1) opacity(0.5); }
#menuMap .maplibregl-ctrl-attrib.maplibregl-compact { color: var(--dim); }
/* On phones the map's action buttons fill that same corner and would bury the
   credit toggle completely, so lift the control clear of them. */
@media (max-width: 720px) {
  #paneExplore .maplibregl-ctrl-bottom-right { bottom: 58px; }
}

.patchTitle { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
#nearBtn { cursor: pointer; background: none; border: 1px solid rgba(143, 214, 243, 0.3);
           color: var(--sky-soft); border-radius: 999px; padding: 3px 10px;
           font: 600 11px var(--font-body); letter-spacing: normal; text-transform: none; }
#nearBtn:hover { background: rgba(41, 171, 226, 0.2); }

/* the whole-fence drag handle — same white-ring family as .apPin (crisp
   border, neutral dark lift, hover scale) but kept visually distinct: a
   filled dark disc carrying the move icon, not a ring with a dot. */
.apCenter span { display: flex; align-items: center; justify-content: center;
                 width: 34px; height: 34px; border-radius: 50%; cursor: grab;
                 background: rgba(11, 35, 51, 0.92); border: 3px solid #fff;
                 color: #fff; box-shadow: 0 3px 12px rgba(2,10,16,0.65), 0 0 0 1px rgba(2,10,16,0.2);
                 transition: transform .15s ease; }
.apCenter span:hover { transform: scale(1.1); }
.apCenter span:active { cursor: grabbing; }
.apCenter svg { width: 18px; height: 18px; }

/* patch name on the ready-to-fly module — a NEW patch types its name in the
   same slot, at the same weight, as the hero line a saved patch shows. It stays
   sentence case while you type: forcing caps into the field would send the
   uppercase to the server as the stored name. */
.apName { display: none; gap: 8px; align-items: center; margin-top: 6px; }
#patchName { flex: 1; min-width: 0; border: 1px solid rgba(143, 214, 243, 0.3); border-radius: var(--r-sm);
             background: rgba(8, 28, 40, 0.7); color: #fff; padding: 5px 10px;
             font-family: var(--font-body); font-weight: var(--pname-weight, 600);
             font-size: clamp(19px, 2.1vw, 23px); letter-spacing: var(--pname-track, 0.05em);
             outline: none; text-align: left; }
#patchName:focus { border-color: var(--sky); }
#nameReroll { cursor: pointer; width: 40px; height: 40px; flex: none; border-radius: 50%;
              display: flex; align-items: center;
              justify-content: center; background: rgba(143, 214, 243, 0.14); color: var(--sky-soft);
              border: 1px solid rgba(143, 214, 243, 0.32); }
#nameReroll:hover { background: rgba(41, 171, 226, 0.25); color: #fff; }
#nameReroll svg { width: 15px; height: 15px; }

/* ===== v8: game over lives on ONE opaque sheet; the chrome gets out of the way ===== */
#splash.summary { background: linear-gradient(180deg, rgba(6, 18, 27, 0.9), rgba(6, 18, 27, 0.96)); }
#splash.summary .logo, #splash.summary .tag, #splash.summary #pilotBar,
#splash.summary #authBox { display: none !important; }
#finalSummary { margin-top: 0; width: 100%; }
/* fluid: the sheet grows with the window — fullscreen gets a full-size summary,
   not a postage stamp floating in the void */
.fsSheet { margin: 0 auto; width: min(88vw, 1440px); max-width: none; text-align: center;
           background: linear-gradient(165deg, rgba(17, 52, 73, 0.98), rgba(10, 31, 45, 0.99));
           padding: clamp(22px, 2.6vw, 46px) clamp(26px, 3.6vw, 64px) clamp(20px, 2.2vw, 38px); }
.fsSheet .fsGrid { width: 100%; max-width: none; gap: 12px clamp(28px, 4vw, 72px); }
/* the score hero gets its own panel — same subtle-fill language as the
   board rows (#lbList / .lbRows) — so it reads as contained rather than
   floating bare on the sheet, and a mount animation gives the reveal some
   drama. Runs once per game-over (the element goes display:none -> block
   each time via show(), which restarts a CSS animation on its own). */
.fsSheet .fsLeft { background: rgba(143, 214, 243, 0.07); border: 1px solid rgba(143, 214, 243, 0.16);
                    border-radius: var(--r-lg); padding: clamp(22px, 2.6vw, 40px) clamp(18px, 2.2vw, 34px);
                    animation: fsReveal 0.5s ease-out; }
@keyframes fsReveal { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .fsSheet .fsLeft { animation: none; }
}
.fsPilot { text-align: left; margin: 0 0 clamp(12px, 1.4vw, 22px); font-size: clamp(13px, 0.95vw, 17px); }
.fsSheet .ftotal { font-size: clamp(96px, 11vw, 210px); line-height: 1.08; }
.fsSheet .rank { font-size: clamp(31px, 2.7vw, 48px); margin-top: 2px; }
/* the reveal's punchline, mirrored from #lbNote (see finalStandingEl in
   main.js) right under the rank — bigger and bolder than a caption, smaller
   than the score it's reporting on. */
.fsSheet .fsStanding { margin-top: 8px; font-size: clamp(16px, 1.4vw, 24px);
                        font-weight: 600; color: var(--sun); letter-spacing: 0.2px;
                        text-shadow: 0 2px 12px rgba(8, 30, 45, 0.9); }
/* the leaderboard demotes into a supporting card once the score+standing
   carry the reveal: rows, title and note all step down ~20%. */
.fsSheet #lbList li { font-size: clamp(11px, 0.85vw, 15px); padding: clamp(5px, 0.55vw, 9px) clamp(10px, 1vw, 14px); }
.fsSheet .lbTitle { font-size: clamp(10px, 0.7vw, 12px); }
.fsSheet #lbNote { font-size: clamp(10px, 0.75vw, 14px); }
.fsSheet #finalActions .btn { font-size: clamp(15px, 1.15vw, 20px); padding: clamp(12px, 1vw, 17px) clamp(26px, 2.2vw, 42px); }
.fsSheet #finalActions button:not(.btn), .fsSheet #finalActions a {
  font-size: clamp(13px, 0.9vw, 16px); padding: clamp(9px, 0.8vw, 13px) clamp(20px, 1.6vw, 30px); }
.fsSheet #lb, .fsSheet #sessionWrap { width: 100%; margin-top: 0; }
.fsSheet #sessionWrap { margin-top: 18px; }
#finalActions { border-top: 1px solid rgba(143, 214, 243, 0.14); padding-top: 18px; margin-top: 22px; }
@media (max-width: 860px) {
  /* the action row was clipped by the page end on phones without the safe margin */
  .fsSheet { padding: 18px 14px calc(env(safe-area-inset-bottom, 0px) + 34px); }
  .fsPilot { text-align: center; }
  /* the desktop hero size (clamp min 96px) would overflow a single-column
     phone width at 5+ digit scores — its own, smaller-but-still-doubled clamp. */
  .fsSheet .ftotal, #finalSummary .ftotal { font-size: clamp(60px, 17vw, 100px); }
  .fsSheet .fsLeft { padding: clamp(18px, 5vw, 28px) 16px; }

  /* tighten the gaps between board / photos / actions so the primary button
     lands close to the fold instead of a screen down (2026-08-20). */
  #finalRows { margin-top: 10px; }
  #finalActions { margin-top: 10px; }

  /* The photos keep their wrapped grid on phones — a filmstrip variant lost
     the round-3 blind A/B (cropped thumbnails read as a defect). Only the
     bottom padding survives, keeping the sticky bar's fade off the last row.
     The sub-panel's padding ate the width the base min-width:150px pair
     needed (two cards + gap = 310px > the ~298px content box), which
     silently collapsed the grid to one giant card per row — size the pair
     explicitly instead. */
  #finalRows { padding: 12px 12px 26px; }
  #finalRows .row { flex: 0 1 calc(50% - 5px); min-width: 0; max-width: none; }

  /* #splash is the actual scroll container here (overflow-y: auto, line ~308) —
     #finalSummary and .fsSheet carry no overflow of their own, so sticky
     reaches straight through to it. Pins the primary actions to the bottom
     of the scrollport instead of letting #finalRows push them off the phone
     screen. shoot.js screenshots at scroll 0, which is exactly when a tall
     page shows the bug: if content overflows the viewport, the sticky bar is
     ALREADY stuck over the tail of #finalRows before any scrolling happens.
     Old bug: a 30px 0->99% fade sitting right on top of the filmstrip showed
     a real card ghosting through the translucent band. Fixed two ways — the
     fade is now short (16px) and reaches SOLID 0.99 (visually opaque) right
     after, so a covered card is hidden, not ghosted; and #finalRows' own
     padding-bottom (26px) plus #finalActions' margin-top (10px) keep that
     fade band itself off any card even when the sticky bar is stuck at
     scroll 0. */
  #finalActions { position: sticky; bottom: 0; padding-top: 16px;
                  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
                  background: linear-gradient(180deg, rgba(10, 31, 45, 0) 0,
                              rgba(10, 31, 45, 0.99) 16px, rgba(10, 31, 45, 0.99) 100%); }
}

/* ===== round review (click a photo on the end screen) ===== */
#roundPeek { background: rgba(6, 18, 27, 0.9); z-index: 47; cursor: zoom-out; }
.peekSheet { max-width: min(96vw, 1240px); cursor: default; text-align: center; }
.peekSheet h2 { font-family: var(--font-head); color: var(--sky-soft); font-size: clamp(22px, 2vw, 30px); }
.peekShots { display: flex; gap: clamp(12px, 1.6vw, 24px); justify-content: center;
             margin: 18px 0 8px; flex-wrap: wrap; }
.peekShots figure { position: relative; background: var(--cream); padding: 9px 9px 6px;
                    border-radius: var(--r-md); box-shadow: 0 14px 36px rgba(8, 30, 45, 0.5); }
.peekShots img { display: block; width: min(40vw, 520px); min-width: 260px; border-radius: 7px; }
.peekShots figcaption { color: var(--ink); font-size: 12.5px; padding-top: 6px;
                        font-style: italic; opacity: 0.85; }
#peekVerdict { font-size: clamp(14px, 1.1vw, 17px); color: var(--cloud); margin-top: 10px; }
#peekVerdict b { color: var(--sun); font-weight: 600; }
#peekClose { cursor: pointer; margin-top: 16px; background: rgba(143, 214, 243, 0.14);
             color: var(--sky-soft); border: 1px solid rgba(143, 214, 243, 0.32);
             border-radius: 999px; padding: 10px 26px; font: 600 13px var(--font-body); }
#peekClose:hover { background: rgba(41, 171, 226, 0.25); color: #fff; }

/* ===== v9: mobile pass — fullscreen world, landscape HUD, one-screen results ===== */

/* the launch gate on the developing sheet: hidden while shooting, shown once
   every reference is ready — the game starts on the pilot's tap, not on its own */
#devGo { display: none; }

/* pinch-zoom lightbox: the photo scales inside the polaroid frame (clipped by
   .lbWrap so the paper border and caption stay put) */
.lbWrap { overflow: hidden; }
#lbImg { will-change: transform; }

/* landscape phones: a short screen — everything to the corners, controls on
   the bottom edge, nothing mid-screen but the viewfinder */
@media (max-width: 920px) and (orientation: landscape) {
  #compass { width: min(280px, 34vw); }
  #mtop { top: calc(env(safe-area-inset-top, 8px) + 8px);
          left: calc(env(safe-area-inset-left, 0px) + 12px); transform: none; }
  #refbox { top: calc(env(safe-area-inset-top, 8px) + 8px);
            right: calc(env(safe-area-inset-right, 0px) + 10px); width: 128px; }
  .stick { bottom: calc(env(safe-area-inset-bottom, 0px) + 38px); }
  #stickL { left: calc(env(safe-area-inset-left, 0px) + 22px); }
  #stickR { right: calc(env(safe-area-inset-right, 0px) + 22px); }
  #shutterBtn { bottom: calc(env(safe-area-inset-bottom, 0px) + 30px); width: 64px; height: 64px; }
  /* the finder nearly spans the short screen — tuck its label inside the frame */
  #finder .flabel { bottom: 4px; right: 8px; }
  /* overlays sized to the short axis so nothing needs scrolling */
  #results { justify-content: center; padding: 8px 12px; overflow-y: auto; -webkit-overflow-scrolling: touch; }
  #results .sheet { padding: 12px 18px 14px; }
  #results h2 { font-size: 20px; }
  #shots { gap: 12px; margin: 8px 0 4px; flex-wrap: nowrap; }
  #shots figure { padding: 6px 6px 3px; }
  #shots img { width: min(38vw, calc(38svh * 1.6)); }
  #shots figcaption { font-size: 10.5px; padding-top: 3px; }
  #verdict { font-size: 12.5px; margin-top: 4px; }
  #verdict .sub { font-size: 11.5px; }
  #scoreLine { font-size: 22px; margin-top: 2px; }
  #totalLine { font-size: 12px; }
  #nextBtn { margin-top: 8px; padding: 10px 28px; font-size: 14px; }
  #roundPeek { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .peekShots img { min-width: 0; width: min(38vw, calc(36svh * 1.6)); }
  #preview .pvcard { max-width: calc(46svh * 1.6 + 22px); }
  #preview .pvround { margin-bottom: 8px; }
  #lightbox .refcard { max-width: calc(72svh * 1.6); }
}

/* portrait phones: the round card fits one screen — score and Next button
   visible without scrolling (images capped by height as well as width) */
@media (max-width: 920px) and (orientation: portrait) {
  #results { justify-content: center; padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 10px);
             overflow-y: auto; -webkit-overflow-scrolling: touch; }
  #results .sheet { padding: 14px 16px 16px; }
  #results h2 { font-size: 22px; }
  #shots { gap: 10px; margin: 6px 0 3px; }
  #shots figure { padding: 7px 7px 4px; }
  #shots img { width: min(72vw, calc(30svh * 1.6)); }
  #shots figcaption { font-size: 11px; padding-top: 4px; }
  #verdict { font-size: 13.5px; margin-top: 4px; }
  #scoreLine { font-size: clamp(34px, 9vw, 48px); line-height: 1.1; }
  #nextBtn { margin-top: 10px; padding: 12px 32px; font-size: 15px; }
  #roundPeek { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .peekShots img { min-width: 0; width: min(72vw, calc(28svh * 1.6)); }
}

/* ===== Pilot License card in the shop ===== */
.license { margin: 20px auto 4px; max-width: 560px; text-align: center;
           background: rgba(255, 206, 74, 0.08); border: 1.5px solid rgba(255, 206, 74, 0.45);
           border-radius: var(--r-md); padding: 18px 22px 16px; }
.license.on { background: rgba(68, 192, 138, 0.08); border-color: rgba(68, 192, 138, 0.5); }
.licHead { font-family: var(--font-head); color: var(--sun); font-size: 24px; }
.license.on .licHead { color: var(--mint); }
.licSub { color: var(--cloud); font-size: 14px; line-height: 1.6; margin-top: 6px; }
.licNote { color: var(--dim); font-size: 12px; margin-top: 6px; }
.licBtn { cursor: pointer; margin-top: 12px; border: none; border-radius: 999px; padding: 11px 30px;
          font: 600 14px var(--font-body); color: #3a2c05; background: var(--sun);
          box-shadow: 0 5px 14px rgba(8, 30, 45, 0.45); transition: filter .12s, transform .08s; }
.licBtn:hover { filter: brightness(1.06); }
.licBtn:active { transform: scale(0.97); }
.licBtn.ghost { background: rgba(143, 214, 243, 0.14); color: var(--sky-soft);
                border: 1px solid rgba(143, 214, 243, 0.32); box-shadow: none; }
.licManageLink { display: inline-block; margin-top: 12px; background: none; border: none;
                 color: var(--sky-soft); font: 500 13px var(--font-body); cursor: pointer;
                 text-decoration: underline; text-underline-offset: 3px; opacity: 0.9; }
.licManageLink:hover { color: #fff; opacity: 1; }
.shopDivider { margin: 18px 0 -10px; text-align: center; }

/* ============================================================================
   v10: the dashboard is a PLACE, not a login screen with a map bolted on.
   Signed in, #splash goes full-window (.shell) and #menu becomes a three-row
   app shell: top bar, then a body holding the drawer and one pane.
   ============================================================================ */
#splash.shell { padding: 0; display: block; overflow: hidden;
                background: linear-gradient(var(--ui-bg-angle, 180deg), var(--ui-bg-top, #0d2b3f), var(--ui-bg-bottom, #071520)); }
/* Footage still plays behind the dashboard, but heavily washed — this is a
   working surface, and type has to win over a moving city. */
#splash.shell.vid { background: linear-gradient(180deg,
                    rgba(6, 20, 30, 0.86) 0%, rgba(6, 20, 30, 0.93) 100%); }
/* the signed-out pitch has no business on the dashboard */
#splash.shell .logo, #splash.shell .tag, #splash.shell #authBox,
#splash.shell .sysNote, #splash.shell #debugRow { display: none !important; }
#splash.shell #menu { display: flex; flex-direction: column; width: 100%; height: 100%;
                      margin: 0; gap: 0; align-items: stretch; }
#splash.summary #menu { display: none !important; }

/* --- top bar --- */
/* Height is explicit so it can be ANIMATED to zero: the ready-to-fly module
   takes the whole window and this slides up out of the way (see the mode block
   further down). auto → 0 does not interpolate; 64px → 0 does. */
.svBar { display: flex; align-items: center; gap: 14px; height: 64px; padding: 0 20px; flex: none;
         background: rgba(11, 36, 52, 0.92); overflow: hidden;
         transition: height .28s ease, opacity .28s ease, transform .28s ease;
         border-bottom: 1px solid rgba(143, 214, 243, 0.18); }
.svBar .svLogo { height: 34px; width: auto; cursor: pointer; filter: drop-shadow(0 2px 6px rgba(0,0,0,0.35)); }
.svBurger { width: 40px; height: 40px; display: flex; flex-direction: column; gap: 4px; padding: 0;
            align-items: center; justify-content: center; cursor: pointer; border-radius: 12px;
            background: rgba(143, 214, 243, 0.14); border: 1px solid rgba(143, 214, 243, 0.32); }
.svBurger:hover { background: rgba(41, 171, 226, 0.24); }
.svBurger i { display: block; width: 17px; height: 2px; border-radius: 2px; background: var(--sky-soft); }
.svTabs { display: flex; gap: 6px; margin-left: 10px; }
.svTab { cursor: pointer; border: none; border-radius: 999px; padding: 9px 20px;
         font: 600 14px var(--font-body); background: rgba(143, 214, 243, 0.12); color: var(--sky-soft); }
.svTab:hover { background: rgba(41, 171, 226, 0.24); color: #fff; }
.svTab.on { background: var(--sky); color: #fff; }
.svSpacer { flex: 1; }
/* #pilotBar is moved in here at boot — keeps its own styling, loses the margin */
.svBarRight { display: flex; align-items: center; gap: 10px; }
.svBarRight #pilotBar { display: flex !important; margin: 0; flex-wrap: nowrap; }
/* sign out lives in the drawer now — two of them in one bar is just clutter */
.svBarRight #signOutBtn { display: none; }

/* --- drawer: overlays, never pushes (pushing gives horizontal scroll) --- */
.svBody { flex: 1; min-height: 0; display: flex; position: relative; }
.svDrawer { position: absolute; top: 0; left: 0; bottom: 0; z-index: 6; width: 0; padding: 0;
            overflow: hidden; transition: width 0.22s ease; background: rgba(9, 30, 44, 0.98);
            border-right: 1px solid rgba(143, 214, 243, 0.16); display: flex;
            flex-direction: column; gap: 3px; box-shadow: 10px 0 30px rgba(6, 22, 33, 0.5); }
.svDrawer.open { width: 238px; padding: 16px 14px; }
.svDrawer a { padding: 10px 12px; border-radius: 12px; font: 500 14.5px var(--font-body);
              color: var(--cloud); text-decoration: none; white-space: nowrap; }
.svDrawer a:hover { background: rgba(143, 214, 243, 0.12); color: #fff; }
.svDrawer a.on { background: rgba(41, 171, 226, 0.2); color: #fff; font-weight: 600; }
.svGroup { font: 600 11px var(--font-body); color: var(--dim); letter-spacing: 1.6px;
           text-transform: uppercase; padding: 12px 10px 6px; white-space: nowrap; }
.svDrawerFoot { margin-top: auto; padding-top: 10px; border-top: 1px solid rgba(143, 214, 243, 0.14); }
.svDrawerFoot a { color: var(--dim); }

/* --- panes --- */
.svPane { display: none; flex: 1; min-width: 0; position: relative; }
.svPane.on { display: flex; }
#paneHome { overflow-y: auto; gap: 22px; padding: 22px 26px 30px; align-items: flex-start; }
.svCol { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 24px; }
.svHead { display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap; }
.svHeadText { flex: 1; min-width: 220px; text-align: left; }
#dashHi { font-family: var(--font-head); font-weight: 400; color: var(--sky);
          font-size: clamp(26px, 3.2vw, 34px); line-height: 1.1; }
#dashSub { color: var(--dim); font-size: 13.5px; margin-top: 4px; }
.svSection { text-align: left; }
/* section headings on the dashboard: the action sits WITH the label, not
   stranded at the far edge of the column */
.svSection .patchTitle { justify-content: flex-start; gap: 12px; }
.svCredit { color: rgba(234, 247, 254, 0.38); font-size: 11.5px; margin-top: 8px; }

/* --- patch cards: photo first, polaroid paper --- */
.pCards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.pCard { display: flex; flex-direction: column; background: #fcfaf4; padding: 11px 11px 0;
         border-radius: 0; box-shadow: 0 10px 26px rgba(8, 30, 45, 0.5); text-align: left; }
.pCard .pcShot { height: 200px; background: linear-gradient(160deg, #2a5f7c, var(--navy));
                 background-size: cover; background-position: center; display: flex;
                 align-items: flex-end; justify-content: space-between; padding: 9px 10px; gap: 8px; }
.pCard .pcTag { font: 600 11px var(--font-body); color: #fff; letter-spacing: 1px;
                text-transform: uppercase; text-shadow: 0 1px 6px rgba(6, 22, 33, 0.9); }
.pCard .pcMeta { padding: 11px 3px 14px; display: flex; flex-direction: column; gap: 3px; flex: 1; }
.pCard .pcName { font-family: var(--font-body); font-weight: var(--pname-weight); font-size: 17px; color: var(--ink);
                 letter-spacing: var(--pname-track); }
.pCard .pcRules { font-size: 12.5px; color: #7d7669; }
.pCard .pcLoc { font-size: 12px; color: #7d7669; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pCard .pcLoc:empty { display: none; }
.pCard .pcBest { font: 600 12.5px var(--font-body); color: var(--sky-deep); }
.pCard .pcGo { cursor: pointer; margin-top: auto; border: none; border-radius: 999px;
               background: linear-gradient(180deg, var(--sky) 0%, var(--sky) 45%, var(--sky-deep) 100%);
               color: #fff; font: 600 14px var(--font-body); padding: 10px 0;
               text-shadow: 0 1px 2px rgba(0, 0, 20, 0.35);
               box-shadow: 0 4px 10px rgba(8, 30, 45, 0.4);
               transition: transform 0.08s, filter 0.15s, box-shadow 0.15s; }
.pCard .pcGo:hover { filter: brightness(1.07); box-shadow: 0 6px 14px rgba(8, 30, 45, 0.5); transform: translateY(-1px); }
.pCard .pcGo:active { transform: translateY(1px); box-shadow: 0 2px 6px rgba(8, 30, 45, 0.35); }
/* the card selects (its board opens in the rail); the button flies */
.pCard { cursor: pointer; outline: 2px solid transparent; outline-offset: 3px; transition: outline-color .15s; }
.pCard.on { outline-color: var(--sky); }
.pCardEmpty { grid-column: 1 / -1; color: var(--dim); font-size: 13.5px;
              border: 1px dashed rgba(143, 214, 243, 0.28); border-radius: var(--r-md); padding: 22px; }

/* --- right rail --- */
.svRail { width: 300px; flex: none; display: flex; flex-direction: column; gap: 14px; }
.svRail .svCard { padding: 20px; text-align: left; max-width: none; }
.svRail .svCard:empty { display: none; }
.svCardLabel { font: 600 11.5px var(--font-body); color: var(--sky-soft);
               letter-spacing: 1.6px; text-transform: uppercase; }
.svStatBig { font-size: 44px; font-weight: 700; color: var(--sky-soft); line-height: 1;
             font-variant-numeric: tabular-nums; margin-top: 10px; }
.svStatWhere { color: var(--cloud); font-size: 13.5px; margin-top: 4px; }
/* the rail leaderboard card's patch name is a label under "Leaderboard", not
   prose like the standing card's "your best, on X" — same base class, own case */
.svStatWhere.bcName { font-weight: var(--pname-weight); letter-spacing: var(--pname-track); }
.svGap { height: 8px; border-radius: 999px; background: rgba(8, 28, 40, 0.65);
         overflow: hidden; margin-top: 14px; }
.svGap i { display: block; height: 100%; border-radius: 999px;
           background: linear-gradient(90deg, var(--sky), var(--sun)); }
.svGapNote { color: var(--dim); font-size: 12px; margin-top: 7px; text-wrap: pretty; }
.svStatStrip { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 18px;
               padding-top: 16px; border-top: 1px solid rgba(143, 214, 243, 0.14); }
.svStatStrip b { display: block; font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.svStatStrip span { font-size: 11.5px; color: var(--dim); }
.svStatStrip div:nth-child(1) b { color: #fff; }
.svStatStrip div:nth-child(2) b { color: var(--sun); }
.svStatStrip div:nth-child(3) b { color: var(--mint); }
/* board rows on the dashboard — same language as the summary's #lbList */
.lbRows { list-style: none; display: flex; flex-direction: column; gap: 3px; margin-top: 10px; }
.lbRows li { display: flex; align-items: baseline; gap: 10px; padding: 6px 11px;
             border-radius: var(--r-sm); background: rgba(143, 214, 243, 0.08);
             font-size: 13.5px; font-variant-numeric: tabular-nums; }
.lbRows li .r { flex: 0 0 20px; color: var(--dim); font-weight: 700; text-align: right; }
.lbRows li .nm { flex: 1; color: var(--cloud); overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
.lbRows li .sc { color: var(--sky-soft); font-weight: 700; }
.lbRows li.me { background: rgba(255, 206, 74, 0.18); border: 1px solid rgba(255, 206, 74, 0.5); }
.lbRows li.me .r, .lbRows li.me .nm, .lbRows li.me .sc { color: var(--sun); }
/* podium chips for the top three ranks — small tinted circle behind the digit,
   read after .me above so a podium row (rank 1-3 AND yours) keeps its medal
   tone instead of flattening to the plain .me gold (equal selector specificity,
   later wins). */
.lbRows li:nth-child(1) .r, .lbRows li:nth-child(2) .r, .lbRows li:nth-child(3) .r {
  flex-basis: 20px; height: 20px; line-height: 20px; text-align: center;
  border-radius: 50%; align-self: center; font-weight: 800;
}
.lbRows li:nth-child(1) .r { background: rgba(255, 206, 74, 0.22);
  box-shadow: inset 0 0 0 1px rgba(255, 206, 74, 0.45); color: var(--sun); }
.lbRows li:nth-child(2) .r { background: rgba(180, 202, 212, 0.18);
  box-shadow: inset 0 0 0 1px rgba(180, 202, 212, 0.42); color: #c9d8e0; }
.lbRows li:nth-child(3) .r { background: rgba(184, 138, 94, 0.16);
  box-shadow: inset 0 0 0 1px rgba(184, 138, 94, 0.4); color: #c99b70; }

/* --- explore: the map IS the page --- */
#paneExplore { padding: 0; }
/* z-index:0 makes the map its own stacking context — the map's own controls
   and markers would otherwise bury the floating panels. */
#paneExplore #menuMap { position: absolute; inset: 0; height: auto; border-radius: 0; z-index: 0; }
/* floating over a bright vector map, the flat single --shadow read pasted-on —
   layer a tight contact shadow under a wide soft one so the panel visibly
   lifts off the tiles instead of sitting flush against them */
.mapPanel { position: absolute; z-index: 4; background: rgba(9, 30, 44, 0.92);
            border: 1px solid rgba(143, 214, 243, 0.24); border-radius: var(--r-lg);
            padding: 14px 16px; text-align: left;
            box-shadow: 0 2px 8px rgba(8, 30, 45, 0.4), 0 18px 40px rgba(8, 30, 45, 0.6); }
/* top offset clears .mapSearch, now floating above it in the same corner.
   Zero padding ON PURPOSE: the group headers are sticky and need to span the
   full panel width with their own background; each group padd its body. */
.mpList { top: 72px; left: 16px; width: min(340px, 34vw); max-height: calc(100% - 88px);
          overflow-y: auto; padding: 0; }
.mpList .mpStanding { padding: 12px 16px 2px; margin-bottom: 0; }
.mpGroup { border-top: 1px solid rgba(143, 214, 243, 0.10); }
.mpGroup:first-of-type { border-top: none; }
/* sticky group header: pins under the panel's top edge while its group
   scrolls beneath; near-solid background so rows never ghost through */
.mpList .mpHead { position: sticky; top: 0; z-index: 2; margin: 0;
                  padding: 11px 16px 9px; background: rgba(9, 30, 44, 0.97); }
.mpFold { display: flex; flex: 1; align-items: center; gap: 7px; cursor: pointer;
          background: none; border: none; padding: 0; text-align: left;
          font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit; }
.mpCount { color: rgba(143, 214, 243, 0.55); font-weight: 500; letter-spacing: 0; }
.mpCount:empty { display: none; }
.mpFold .chev { width: 14px; height: 14px; margin-left: auto; flex: none;
                transition: transform .18s ease; }
.mpGroup.folded .chev { transform: rotate(-90deg); }
.mpGroupBody { padding: 2px 16px 12px; }
.mpGroup.folded .mpGroupBody { display: none; }
/* the OS scrollbar reads as a white slab against the night panels */
.mpList, .mpBoard { scrollbar-width: thin; scrollbar-color: rgba(143, 214, 243, 0.3) transparent; }
.mpList::-webkit-scrollbar, .mpBoard::-webkit-scrollbar { width: 8px; }
.mpList::-webkit-scrollbar-track, .mpBoard::-webkit-scrollbar-track { background: transparent; }
.mpList::-webkit-scrollbar-thumb, .mpBoard::-webkit-scrollbar-thumb {
  background: rgba(143, 214, 243, 0.3); border-radius: 999px; }
/* the polaroid cards, single file inside the panel (they were a 3-up grid on
   the old Home pane) */
.mpList .pCards { grid-template-columns: 1fr; gap: 12px; margin-top: 2px; }
.mpList .pCard .pcShot { height: 130px; }
.mpStanding:empty { display: none; }
/* the centre panel a picked patch pops up — the ready-to-fly moment with all
   the UI still around it */
.mpPick { top: auto; bottom: 20px; left: 50%; right: auto; transform: translate(-50%, 0);
          width: min(400px, 44vw); text-align: center; z-index: 5; }
.mpPick:empty { display: none; }
/* the picked patch's leaderboard, its own panel on the right */
.mpBoard { top: 72px; right: 16px; width: min(300px, 26vw); max-height: calc(100% - 88px);
           overflow-y: auto; }
.mpBoard:empty { display: none; }
.mpBoard .bcName { font-family: var(--font-body); font-weight: var(--pname-weight);
                   letter-spacing: var(--pname-track); font-size: 15px; color: #fff; margin-top: 8px; }
.mpBoard .bcRules { font-size: 12.5px; color: var(--dim); margin-top: 2px; }
.mpBoard .bcRules .lucide { width: 13px; height: 13px; vertical-align: -2px; }
/* Quiet close control, same muted family as .mpRules meta text. Larger hit
   area than the glyph itself (~32px) and no border/background until hover,
   so it doesn't compete with the card's actual content. */
.mpClose { position: absolute; top: 8px; right: 8px; width: 32px; height: 32px;
           display: flex; align-items: center; justify-content: center;
           cursor: pointer; border: none; border-radius: var(--r-sm);
           background: transparent; color: var(--dim); font-size: 20px; line-height: 1; }
.mpClose:hover { background: rgba(143, 214, 243, 0.12); color: var(--cloud); }
.mapBtns { position: absolute; z-index: 4; right: 16px; bottom: 16px; display: flex; gap: 8px; }
/* legibility pass 2026-08-20: label lightened sky-soft -> cloud, see
   #apLocate above. */
.mapBtns button { cursor: pointer; border-radius: 999px; padding: 11px 20px;
                  font: 600 13.5px var(--font-body); background: rgba(9, 30, 44, 0.92);
                  color: var(--cloud); border: 1px solid rgba(143, 214, 243, 0.32);
                  box-shadow: 0 4px 10px rgba(8, 30, 45, 0.4);
                  transition: transform 0.08s, filter 0.15s, box-shadow 0.15s; }
.mapBtns #exDraw { border-color: transparent; color: #fff;
                    text-shadow: 0 1px 2px rgba(0, 0, 20, 0.35);
                    background: linear-gradient(180deg, var(--sky) 0%, var(--sky) 45%, var(--sky-deep) 100%); }
.mapBtns button:hover { filter: brightness(1.1); box-shadow: 0 6px 14px rgba(8, 30, 45, 0.5); transform: translateY(-1px); }
.mapBtns button:active { transform: translateY(1px); box-shadow: 0 2px 6px rgba(8, 30, 45, 0.35); }
/* phone-only chip that shows/hides .mpList — desktop always shows the list,
   so the chip has nothing to do there. Positioned inside the phone media
   query below, where the list itself starts collapsed. */
#mpToggle { display: none; }
.mpStanding { color: var(--dim); font-size: 12.5px; margin-bottom: 8px; }
.mpRow { display: block; width: 100%; text-align: left; cursor: pointer; margin-top: 7px;
         background: rgba(143, 214, 243, 0.08); border: 1px solid rgba(143, 214, 243, 0.16);
         border-radius: var(--r-sm); padding: 9px 11px; color: var(--cloud); }
.mpRow:hover, .mpRow.on { border-color: var(--sky); background: rgba(41, 171, 226, 0.18); }
.mpRow b { display: block; font-size: 14px; font-weight: var(--pname-weight); color: #fff;
           letter-spacing: var(--pname-track); }
.mpRow span { font-size: 12px; color: var(--dim); }
.mpPick .mpTitle { font-family: var(--font-body); font-weight: var(--pname-weight); font-size: 20px;
                    color: #fff; padding: 0 28px; letter-spacing: var(--pname-track); }
.mpPick .mpRules { font-size: 13px; color: var(--dim); margin: 4px 0 0; }
.mpPick .mpRules .lucide { width: 13px; height: 13px; vertical-align: -2px; }
.mpPick .mpLoc { font-size: 12px; color: var(--dim); margin-top: 2px; }
.mpPick .mpLoc:empty { display: none; }
.mpPick .mpFly { width: 100%; cursor: pointer; border: none; border-radius: 999px; margin-top: 12px;
                 background: linear-gradient(180deg, var(--sky) 0%, var(--sky) 45%, var(--sky-deep) 100%);
                 color: #fff; font: 600 14px var(--font-body); padding: 11px 0;
                 text-shadow: 0 1px 2px rgba(0, 0, 20, 0.35);
                 box-shadow: 0 4px 10px rgba(8, 30, 45, 0.4);
                 transition: transform 0.08s, filter 0.15s, box-shadow 0.15s; }
.mpPick .mpFly:hover { filter: brightness(1.07); box-shadow: 0 6px 14px rgba(8, 30, 45, 0.5); transform: translateY(-1px); }
.mpPick .mpFly:active { transform: translateY(1px); box-shadow: 0 2px 6px rgba(8, 30, 45, 0.35); }
.mpPick .mpActs { display: flex; justify-content: center; gap: 8px; margin-top: 9px; }
.mpPick .mpMini { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
                  border: 1px solid rgba(143, 214, 243, 0.32); border-radius: 999px;
                  background: rgba(143, 214, 243, 0.08); color: var(--sky-soft);
                  font: 600 12.5px var(--font-body); padding: 7px 14px; }
.mpPick .mpMini svg { width: 14px; height: 14px; }
.mpPick button.mpMini:hover { border-color: var(--sky); color: #fff; }
/* the lit globe of an already-published patch: a state, not a button */
.mpPick .mpPubbed { cursor: default; color: var(--sun);
                    border-color: rgba(255, 206, 74, 0.4); background: rgba(255, 206, 74, 0.08); }
.mpPick .mpCost { color: var(--dim); font-size: 11.5px; text-align: center; margin-top: 8px; }

/* solo / with-friends, on the confirm sheet where the choice belongs */
.apMode { display: flex; gap: 10px; margin-top: 16px; }
/* legibility pass 2026-08-20: unselected border raised 0.24 -> 0.4 alpha
   (same sky-soft hue) — the hairline was reading as invisible. */
.apModeBtn { flex: 1; cursor: pointer; border-radius: var(--r-md); padding: 11px 10px;
             background: rgba(143, 214, 243, 0.08); border: 1.5px solid rgba(143, 214, 243, 0.4);
             color: var(--cloud); font: 600 14.5px var(--font-body); }
.apModeBtn span { display: block; font-size: 11.5px; font-weight: 500; color: var(--dim); margin-top: 2px; }
.apModeBtn:hover { border-color: var(--sky); }
/* the chosen mode reads chosen at a glance, not just a border color change —
   a soft accent glow is the one place a colored glow is allowed at rest */
.apModeBtn.on { background: rgba(41, 171, 226, 0.22); border-color: var(--sky); color: #fff;
                box-shadow: 0 0 0 3px rgba(41, 171, 226, 0.16), 0 4px 16px rgba(41, 171, 226, 0.35); }
.apModeBtn.on span { color: var(--sky-soft); }

/* ============================================================================
   v11: ONE map context. Explore, drawing a fence and ready-to-fly are three
   MODES of #paneExplore (mode-browse / mode-draw / mode-confirm, also mirrored
   onto #menu because the top bar is a sibling of the pane). The map underneath
   never re-mounts and the view is never yanked, which is the whole point.
   ============================================================================ */
/* browse chrome: on screen in mode-browse, swept out of the way in the others */
.mpList, .mpPick, .mpBoard { transition: opacity .22s ease, transform .22s ease, visibility .22s; }
.mapBtns { transition: opacity .22s ease, transform .22s ease, visibility .22s; }
#paneExplore.mode-draw .mpList, #paneExplore.mode-confirm .mpList,
#paneExplore.mode-draw .mpBoard, #paneExplore.mode-confirm .mpBoard {
  opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(-16px); }
/* the centre panel keeps its translateX while receding, or it jumps sideways */
#paneExplore.mode-draw .mpPick, #paneExplore.mode-confirm .mpPick {
  opacity: 0; visibility: hidden; pointer-events: none; transform: translate(-50%, 30px); }
#paneExplore.mode-draw .mapBtns, #paneExplore.mode-confirm .mapBtns {
  opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(20px); }
/* the top bar stays for drawing (same context) and stands down for the module */
#menu.mode-confirm .svBar { height: 0; opacity: 0; transform: translateY(-100%);
                            border-bottom-color: transparent; pointer-events: none; }

/* both bottom modules ride up from the edge of the map */
.drawBar, .acPanel { display: none; position: absolute; z-index: 5; left: 50%; bottom: 16px;
                     transform: translateX(-50%); }
#paneExplore.mode-draw .drawBar { display: grid; animation: mapRise .28s ease both; }
#paneExplore.mode-confirm .acPanel { display: flex; animation: mapRise .28s ease both; }
@keyframes mapRise {
  from { transform: translate(-50%, 130%); opacity: 0; }
  to   { transform: translate(-50%, 0); opacity: 1; }
}

/* .svPin has no other base rule (its size/color are inline, JS) — this is
   just the fade so the mode-draw/mode-confirm opacity:0 below eases in and
   out instead of snapping. */
.svPin { transition: opacity .22s ease; }
/* selection glow for the picked patch's pin. Two box-shadow layers (a tight
   ring right at the edge, a soft blurred halo) — box-shadow never touches the
   `transform` MapLibre keeps rewriting inline for marker position, so it can't
   fight that. `zoom` (not `transform: scale`, which JS's inline transform
   would silently win over every time) gives the slight size bump — Chromium
   resolves the marker's percentage-based centering translate against the
   pin's current (zoomed) box, so it stays correctly centered. */
.svPin.picked { zoom: 1.15;
  box-shadow: 0 0 0 3px rgba(41, 171, 226, 0.65), 0 0 16px 7px rgba(41, 171, 226, 0.4); }
/* existing patch pins and the pilot's own position icon fully disappear while
   a fence is being drawn or confirmed. A dim recede (the old build kept them
   as quiet amber dots) still overlapped the vertex handles pixel-for-pixel at
   typical draw zooms and read as a rendering glitch, not as context — so they
   go instead, with the fade each already carries (.svPin above, .svMeIcon in
   the fence-editor block). The pins are DOM markers whose size and color are
   set INLINE per pin (renderMenuMap has to read the ⚙ UI tab's color vars in
   JS), so !important is the only way to override them from here. */
#paneExplore.mode-draw .svPin, #paneExplore.mode-confirm .svPin,
#paneExplore.mode-draw .svMeIcon, #paneExplore.mode-confirm .svMeIcon {
  opacity: 0 !important;
  /* Fully inert, not just invisible: these are DOM markers stacked over the
     GL canvas, so without this an existing patch's pin still eats the hover
     (cursor flips off the new fence's own vertex handle) and the click, even
     though pickPatch itself already no-ops via the browsing() guard.
     cursor:pointer is set inline per-pin in JS, so !important here too. */
  pointer-events: none !important;
  cursor: default !important;
}

/* draw: a single row now that the place search lives on the map itself
   (.mapSearch) rather than in here — what you are outlining, its rules,
   the tools, left to right. */
.drawBar { width: min(880px, calc(100% - 32px));
           grid-template-columns: minmax(0, 1fr) auto auto; gap: 12px 18px; align-items: center; }
.drawBar .dbInfo { grid-area: 1 / 1; }
.drawBar .apSettings { grid-area: 1 / 2; justify-content: flex-end; }
.drawBar .apBtns { grid-area: 1 / 3; justify-content: flex-end; }

/* ready to fly: bottom-anchored panel, not a centred modal — the fence it names
   is framed on the map directly above it */
.acPanel { width: min(560px, calc(100% - 32px)); bottom: 20px;
           flex-direction: column; align-items: stretch; padding: 18px 22px 20px; }
.acEyebrow { font: 600 11.5px var(--font-body); color: var(--sky-soft);
             letter-spacing: 2.2px; text-transform: uppercase; }
.acName { margin-top: 6px; color: #fff; font-family: var(--font-body);
          font-weight: var(--pname-weight, 600); font-size: clamp(21px, 2.4vw, 26px);
          line-height: 1.15; letter-spacing: var(--pname-track, 0.05em); }
.acRules { display: flex; align-items: center; gap: 20px; margin-top: 10px;
           color: var(--cloud); font: 600 15px var(--font-body); }
.acRules span { display: inline-flex; align-items: center; gap: 6px; }
.acRules .lucide { width: 16px; height: 16px; flex: none; color: var(--sky-soft);
                   margin-right: 1px; }
.acRules b { font-weight: 600; font-variant-numeric: tabular-nums; color: #fff; }
.acRules i { font-style: normal; font-weight: 500; color: var(--dim); }
.acPanel .apMode { margin-top: 14px; }
.acPanel .apCost { align-self: flex-start; margin-top: 12px; }

@media (max-width: 1080px) {
  .svRail { width: 100%; }
  #paneHome { flex-direction: column; }
  .pCards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .svTabs { display: none; }          /* on phones the tabs live in the drawer */
  .svBar { height: 56px; padding: 0 12px; gap: 10px; }
  .svBar .svLogo { height: 28px; }
  /* the flight count is the first thing to go: the name and the token chip
     both have to fit, and this bar has no room to wrap */
  #pilotBar .pStat { display: none; }
  .svBarRight #pilotBar { padding: 6px 8px; gap: 6px; }
  .svBarRight #pilotBar .pName { max-width: 96px; }
  /* The chip opens the shop too, so the second button is only taking room, and
     without nowrap the flex line shrinks the pills below their own text until
     the name is crushed to three characters. !important because account.js
     show() writes an inline display, which no stylesheet rule can outrank. */
  .svBarRight #pilotBar .pBuy { display: none !important; }
  .svBarRight #pilotBar .pTokens { white-space: nowrap; flex-shrink: 0; }
  #paneHome { padding: 16px 14px 24px; }
  .pCards { grid-template-columns: 1fr; }
  .pCard .pcShot { height: 150px; }
  /* The list and the pick card between them left the map a 100px sliver, which
     is the one thing this pane is for. Both are capped and scroll internally so
     the map keeps roughly a third of the screen with both panels open. */
  .mpList { width: auto; left: 12px; right: 12px; max-height: 30%; }
  /* the list starts collapsed on landing so it never covers the map — the
     chip below it toggles this class */
  .mpList.collapsed { display: none; }
  /* same corner as the list it controls, just under the search pill — above
     the list's own z-index (4) so it stays clickable when the list is open
     and overlapping it, not just when collapsed */
  #mpToggle { display: block; position: absolute; z-index: 6; top: 72px; left: 12px;
              cursor: pointer; border-radius: 999px; padding: 8px 16px;
              font: 600 12.5px var(--font-body); background: rgba(9, 30, 44, 0.92);
              color: var(--sky-soft); border: 1px solid rgba(143, 214, 243, 0.32); }
  /* bottom offset clears the toolbar below it (button row + its own 12px
     inset) plus a 12px gap, so the pick card's Fly button never crowds
     "My location" / "Draw a new patch" */
  .mpPick { top: auto; bottom: 88px; left: 12px; right: 12px; width: auto;
            transform: none; max-height: 40%; overflow-y: auto; }
  /* A pick REPLACES the list rather than sharing the strip with it — there's
     no room for both on a phone. The card gets the space the list gave up. */
  #paneExplore.hasPick .mpList { display: none; }
  #paneExplore.hasPick .mpPick { max-height: 60%; }
  /* No room for a standing board panel on a phone. Part of the open mobile
     design question (see the menu-merge item in tasks/todo.md). */
  .mpBoard { display: none !important; }
  /* bottom-anchored here, so the inherited .mapPanel shadow (offset downward)
     falls off-screen and the sheet reads flat against the map. Flip it: a
     tight contact shadow hugging the top edge plus a soft wide one climbing
     further up, so the sheet visibly lifts off the tiles below it. */
  .mpPick, .drawBar, .acPanel {
    box-shadow: 0 -3px 10px rgba(8, 30, 45, 0.4), 0 -10px 30px rgba(2, 10, 16, 0.45);
  }
  #paneExplore.mode-draw .mpPick, #paneExplore.mode-confirm .mpPick { transform: translateY(30px); }
  .mapBtns { left: 12px; right: 12px; bottom: 12px; }
  .mapBtns button { flex: 1; }
  /* both map modules go full width and stack; the map keeps whatever is left.
     Clear of the home indicator / gesture bar on notched phones, on top of the
     usual 16px breathing room. */
  .drawBar, .acPanel { width: calc(100% - 24px); bottom: 12px; max-height: 72%; overflow-y: auto;
                        margin-bottom: 16px; margin-bottom: calc(16px + env(safe-area-inset-bottom)); }
  /* one column on a phone: what you are outlining, the rules, the tools
     (search lives on the map itself now, not in this bar) */
  .drawBar { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .drawBar .dbInfo { grid-area: 1 / 1; }
  .drawBar .apSettings { grid-area: 2 / 1; justify-content: flex-start; }
  /* My location / Reset shape / Cancel share a row; Fly is the full-width
     row under them, not stranded bottom-right with dead space beside it */
  .drawBar .apBtns { grid-area: 3 / 1; }
  .drawBar .apBtns > button:not(#apPlay) { flex: 1 1 0; }
  .drawBar #apPlay { flex: 1 1 100%; margin-left: 0; width: 100%; }
  .acPanel { padding: 14px 16px 16px; }
  .acPanel .apMode { flex-direction: column; }
  .acPanel .apConfirmBtns { justify-content: stretch; }
  .acPanel .apConfirmBtns button { flex: 1; justify-content: center; }
  /* the row's own Fly button, the share and globe icons: keep them on one line
     without crushing the patch name next to them */
  .pFly { padding: 0 12px; font-size: 12.5px; }
}

/* ===== v10: developing poster — narrow screens =====
   Same id-scoped selectors as the block above, or these would lose to it. */
@media (max-width: 1000px) {
  #dev .devMain { flex-direction: column; gap: 22px; padding: 18px 0 14px; }
  #dev .devLeft { flex: none; }   /* stacked: don't let it eat the spare height */
  #dev .devSide { width: 100%; }
  #dev h2.devName { font-size: clamp(34px, 9vw, 60px); }
  #dev .devWrap { padding: 16px 16px 16px; }
}
@media (max-width: 620px) {
  #dev .devKeys { display: none; }        /* phones fly on sticks, not keys */
  #dev .devFoot { flex-wrap: wrap; gap: 10px 16px; }
  #dev .devFoot .credit { margin-left: 0; text-align: left; }
  #dev .devBarWrap { height: 46px; }
  #dev .devBarWrap #devGo { height: 46px; font-size: 16px; }
  #dev .devScrim { background: linear-gradient(180deg,
                   rgba(6, 22, 33, 0.72) 0%, rgba(6, 22, 33, 0.86) 55%, rgba(6, 22, 33, 0.94) 100%); }
}

/* ===== v11: full-bleed flight HUD (desktop) =====
   Modules overlaid on the 3D view; the old #side rail is retired. z-index sits
   at 11-13: above the canvas and finder dim, below #toast (62) and #flash (40). */
@media (min-width: 921px) {
  #side { display: none !important; }
  .hudBrand { position: absolute; top: 36px; left: 22px; height: 40px; width: auto;
              z-index: 12; pointer-events: none;
              filter: drop-shadow(0 2px 8px rgba(0,0,0,0.45)); }
  #compass { width: 430px; padding: 9px 22px 11px; border-radius: 999px;
             background: rgba(11,35,50,0.82); border-color: rgba(143,214,243,0.24); }
  #compass .cbar { height: 24px; }
  #compass .cdeg { font-size: 11px; letter-spacing: 1.2px; margin-top: 2px; }
  #compass .carea { font-size: 15px; margin-top: 2px; }
  #compass .cstreet { font-size: 12.5px; }
  .hudPatch { position: absolute; top: 14px; right: 22px; width: 420px; height: 84px;
              box-sizing: border-box; z-index: 12; display: flex; align-items: center;
              justify-content: space-between; gap: 24px; padding: 0 18px;
              background: rgba(11,35,50,0.82); border: 1px solid rgba(143,214,243,0.24);
              border-radius: 14px; }
  .hudPatch .hpL { min-width: 0; text-align: left; }
  .hudPatch .hpName { color: #fff; font-size: 17px; font-weight: var(--pname-weight);
                      letter-spacing: var(--pname-track); line-height: 1.25;
                      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hudPatch .hpPlace { color: var(--sky-soft); font-size: 13px; margin-top: 3px;
                       white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hudPatch .hpR { text-align: right; flex: none; font-variant-numeric: tabular-nums; }
  .hudPatch .hpRound { color: var(--sky-soft); font: 600 11.5px var(--font-body);
                       letter-spacing: 1.8px; line-height: 1.25; text-transform: uppercase; }
  .hudPatch .hpPts { color: var(--sun); font-size: 14px; font-weight: 600; margin-top: 3px; }
  .hudTimer { position: absolute; bottom: 26px; left: 50%; transform: translateX(-50%);
              width: 210px; box-sizing: border-box; z-index: 12; padding: 9px 18px 12px;
              text-align: center;
              background: rgba(11,35,50,0.82); border: 1px solid rgba(143,214,243,0.24);
              border-radius: 14px; box-shadow: 0 8px 22px rgba(8,30,45,0.45); }
  .hudTimer #timer { font-size: 56px; }
  .hudTimer #timerBarWrap { margin-top: 8px; height: 7px; }
  /* the photo laid over the scene: deliberately overlaps the finder edge */
  .hudRef { position: absolute; bottom: 30px; right: 26px; width: 408px; z-index: 13;
            transform: rotate(1.8deg); transform-origin: bottom right; }
  .hudRef:hover { transform: rotate(0.3deg) scale(1.45) translateY(-4%); }
  .hudKeys { position: absolute; bottom: 34px; left: 20px; z-index: 12; margin: 0;
             color: rgba(234,247,254,0.75); font-size: 12.5px; line-height: 1;
             background: rgba(11,35,50,0.82); border: 1px solid rgba(143,214,243,0.24);
             border-radius: 999px; padding: 7px 16px; }
  #stage.low #finder .fc { border-color: var(--coral); }
  #stage.low::after { content: ''; position: absolute; inset: 0; pointer-events: none;
                      z-index: 11; box-shadow: inset 0 0 160px 30px rgba(255,116,136,0.55);
                      animation: pulse 0.8s ease-in-out infinite; }
}
@media (max-width: 920px) {
  /* the HUD modules are desktop chrome; phones keep #mtop/#refbox/#touchCtl.
     !important because showPlayUI sets inline display on these ids. */
  .hudBrand, .hudPatch, .hudTimer, .hudRef, .hudKeys { display: none !important; }
}
