:root {
  --bg: #050810;
  --ink: #e8ecf2;
  --ink-dim: rgba(232, 236, 242, 0.86);   /* lifted for readability (Tony 2026-09-15) */
  --ink-faint: rgba(232, 236, 242, 0.70);   /* 4.6:1 on the scene ground */
  --accent: #7fe3ff;
  --serif: "Big Shoulders Display", "Oswald", "Arial Narrow", sans-serif;   /* Tony 2026-10-16: option C, was Cormorant Garamond */
  --sans: "Barlow", "Inter", -apple-system, "Helvetica Neue", Arial, sans-serif;
  --ease: cubic-bezier(0.22, 0.8, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --safe-b: env(safe-area-inset-bottom, 0px);
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  user-select: none;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
[hidden] { display: none !important; }

/* intro: the world fades up from black once the city texture is in */
body::after { content: ""; position: fixed; inset: 0; z-index: 60; background: #03050a; pointer-events: none; opacity: 1; transition: opacity 2.4s cubic-bezier(0.4, 0, 0.2, 1); }
body.ready::after { opacity: 0; }

/* ---------- scene ---------- */
#scene {
  position: fixed; inset: 0; width: 100%; height: 100%;
  display: block; touch-action: none; cursor: default;
}
#scene.dragging { cursor: default; }
/* 2026-10-20: a tall window (iPad standing up) swipes the map sideways; the browser must not take the swipe */
html.pan #scene, html.pan #world, html.pan #world * { touch-action: none; }
html.pan #scene { cursor: grab; }
body.panning, body.panning * { cursor: grabbing !important; user-select: none; }
#bats { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; opacity: 0.78;
  filter: blur(0.55px); }   /* bats only slightly soft; the witch gets her own blur in main.js (Tony 2026-09-15) */

#world { position: fixed; inset: 0; pointer-events: none; overflow: hidden; }
#fx { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 12; }   /* wisps: above the ghosts and portal, below the panel */

/* ghosts */
.ghost {
  position: absolute; left: 0; top: 0;
  width: 68px; height: 88px; margin: -79px 0 0 -34px;   /* 10 % smaller (Tony 2026-09-15) */
  pointer-events: auto; cursor: pointer;
  transform-origin: 50% 100%;
  transition: opacity 0.6s var(--ease);
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--c) 70%, transparent)) drop-shadow(0 0 20px color-mix(in srgb, var(--c) 35%, transparent));
}
.ghost::before {            /* larger hit area */
  content: ""; position: absolute; inset: -18px; border-radius: 50%;
}
.ghost svg { width: 100%; height: 100%; display: block; animation: hover var(--bob, 4.2s) ease-in-out infinite; transform-origin: 50% 100%; scale: var(--gscale, 1); }
.ghost .body { }   /* fill is a per-ghost gradient in the svg (Tony's ghost, 2026-09-15) */
.ghost svg { opacity: 0.94; }
.ghost svg { overflow: visible; }
.ghost .sway { transform-origin: 107px 200px; animation: sway var(--sway-d, 6s) ease-in-out var(--sway-delay, 0s) infinite; }
.ghost .eyes { transform-origin: 107px 50px; animation: gaze var(--gaze-d, 11s) ease-in-out var(--gaze-delay, 0s) infinite, blink 6s var(--blink-delay, 0s) infinite; transition: transform 0.6s var(--ease); }
.ghost .mouth { fill: #0a1220; transform-origin: 107px 88px; transform: scale(1); opacity: 1; transition: transform 0.45s var(--ease-out), opacity 0.3s; }
@keyframes sway { 0%,100% { transform: rotate(-1.6deg) } 50% { transform: rotate(1.6deg) } }
@property --look { syntax: "<length>"; inherits: true; initial-value: 0px; }
@keyframes gaze { 0%,18% { transform: translate(0, var(--look)) } 26%,44% { transform: translate(6px, calc(1px + var(--look))) } 52%,70% { transform: translate(-5.5px, calc(1.4px + var(--look))) } 78%,100% { transform: translate(0, var(--look)) } }   /* scaled to the 214-unit box (Tony 2026-09-15) */
@keyframes blink { 0%,92%,100% { transform: scaleY(1) } 95% { transform: scaleY(0.1) } }
.ghost .eye { fill: #0a1220; transform-box: fill-box; transform-origin: center; }
.ghost .light {           /* ground light contact */
  position: absolute; left: 50%; bottom: -6px; width: 140px; height: 34px;
  margin-left: -70px; border-radius: 50%;
  background: radial-gradient(ellipse at center, var(--c) 0%, transparent 70%);
  opacity: 0.7; filter: blur(4px);
}
.ghost .card {
  position: absolute; left: 50%; bottom: calc(100% + 14px); transform: translate(-50%, 6px);
  display: flex; flex-direction: column; gap: 3px; min-width: 170px; padding: 12px 14px 11px;
  background: rgba(6, 9, 16, 0.78); backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--c) 45%, transparent); border-radius: 3px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.45), 0 0 24px color-mix(in srgb, var(--c) 18%, transparent);
  text-align: left; white-space: nowrap; opacity: 0; pointer-events: none;
  transition: opacity 0.28s var(--ease), transform 0.28s var(--ease);
}
.ghost .card::after {                /* thin connector down to the ghost */
  content: ""; position: absolute; left: 50%; top: 100%; width: 1px; height: 12px;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--c) 70%, transparent), transparent);
}
.ghost .name {                    /* always-on name under the ghost, in the world's own serif */
  position: absolute; left: 50%; top: calc(100% + 6px); transform: translateX(-50%);
  font-family: var(--serif); font-size: 22px; font-weight: 500; letter-spacing: 0.05em; color: var(--ink); opacity: 0.92;
  white-space: nowrap; text-shadow: 0 2px 14px rgba(0,0,0,1), 0 0 4px rgba(0,0,0,0.9), 0 0 24px rgba(0,0,0,0.7);
  transition: opacity 0.3s var(--ease);
}
.ghost:hover .name, body.kbd .ghost:focus-visible .name { opacity: 1; }
.ghost .card-place { font-family: var(--serif); font-size: 22px; line-height: 1.1; color: var(--ink); }
.ghost .card-sub { font-size: 14.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--c); }
.ghost .card-teaser { font-size: 16.5px; color: var(--ink-dim); margin-top: 3px; }
.ghost .card-cta { font-size: 14.5px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--ink-faint); margin-top: 6px; }
.ghost:hover .card, body.kbd .ghost:focus-visible .card { opacity: 1; transform: translate(-50%, 0); }   /* 2026-09-22: only a real keyboard visitor gets the card from focus */
.ghost svg { transition: filter 0.25s var(--ease); }
.ghost:hover svg, .ghost:focus-visible svg { filter: brightness(1.25); }
.ghost .eyes { --look: 0px; transition: --look 0.4s var(--ease-out); }
/* hover: the ghost notices you. The eyes lift to the card, the sheet rises, the mouth opens a little
   and the glow swells. -26 units in the 214-unit box is about the same lift the old ghosts had. */
.ghost:hover .eyes, .ghost:focus-visible .eyes, .ghost.lit .eyes { --look: -22px; translate: 0 -19.5px; }
/* the eyes move with the own `translate` property: `transform` is taken by the gaze+blink
   animations and the last one (blink) overwrote the look every frame (Tony 2026-09-15) */
.ghost .eyes { translate: 0 0; transition: translate 0.32s var(--ease-out); }
.ghost.selected .eyes { translate: 0 -10px; }
/* the whole ghost also leans back, so the look up reads from far away (Tony 2026-09-15: "valdigt mkt mer") */
.ghost:hover svg, .ghost.lit svg, .ghost:focus-visible svg { rotate: var(--tilt, -5.2deg); }
.ghost:hover .mouth, .ghost.lit .mouth { transform: scale(1.18) translateY(3px); }
.ghost:hover svg, .ghost.lit svg { translate: 0 -5px; }
.ghost svg { transition: translate 0.45s var(--ease-out), rotate 0.45s var(--ease-out), filter 0.35s var(--ease); transform-origin: 50% 100%; }
.ghost:hover .name, .ghost.lit .name { opacity: 1; letter-spacing: 0.075em; }
.ghost .name { transition: opacity 0.35s var(--ease), letter-spacing 0.45s var(--ease-out); }
/* the chosen ghost keeps looking toward the panel on the right */
.ghost.selected .eyes { --look: -14px; }
.ghost .light { transition: opacity 0.3s var(--ease), transform 0.3s var(--ease); }
.ghost:hover .light { opacity: 1; transform: scale(1.25); }
@media (hover: hover) and (pointer: fine) {   /* mouse and keyboard only, never a touch screen */
  body.kbd .ghost:focus-visible { outline: 2px solid var(--ink); outline-offset: 6px; border-radius: 10px; }   /* 2026-09-22: the box Tony photographed. Kept for keyboard visitors, gone after a mouse click */
}
@media not all and (hover: hover) { .ghost:focus-visible { outline: none; } }
/* 2026-09-22: the same rule as for the ghosts. This generic ring covers every link and
   button on the page, so scoping only .ghost left the outline still drawn. A visitor who
   navigates with Tab must always see where they are; a visitor with a mouse must not get a
   ring left behind on something they clicked three seconds ago. */
body.kbd :where(a, button):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }
body:not(.kbd) :focus-visible { outline: none; }   /* 2026-09-22: Chrome draws its own ring when ours is gone; the mouse must not leave one behind */
.pager:focus-visible, .close:focus-visible { outline-offset: 2px; }
.ghost.dim { opacity: 0.32; }
.ghost.dim:hover, .ghost.dim:focus-visible { opacity: 1; }
.ghost.selected { opacity: 1; pointer-events: none; z-index: 2; }
.ghost.selected .sway { animation: none; transform: rotate(0deg) translateY(-3px); }
.ghost.selected .eyes { animation: none; transform: translateY(-1.6px); }   /* looks up */
.ghost.selected.open .mouth { transform: scale(1.25); opacity: 1; transition-delay: 0.15s; }
.ghost.selected .name { opacity: 1; }   /* the name stays when chosen (Tony 2026-09-15) */
@keyframes hover { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-6px) } }

/* portal: pool, beam, wisps, image, one world anchor */
.portal { position: absolute; left: 0; top: 0; width: 0; height: 0; --c: var(--accent); }
.portal-pool {
  position: absolute; left: -70px; top: -16px; width: 140px; height: 32px; border-radius: 50%;
  background: radial-gradient(ellipse at center, var(--c) 0%, color-mix(in srgb, var(--c) 40%, transparent) 30%, transparent 70%);
  filter: blur(6px); opacity: 0.9;
}
.portal-beam {                       /* the straight beam Tony approved; the cone was rejected 2026-09-14 */
  position: absolute; left: -14px; bottom: 0; width: 28px; height: var(--beam, 300px);
  background: linear-gradient(to top,
    color-mix(in srgb, var(--c) 70%, white) 0%,
    color-mix(in srgb, var(--c) 62%, transparent) 25%,
    color-mix(in srgb, var(--c) 55%, transparent) 100%);   /* 2026-10-20: stays visible up to the caption plate, no gap */
  -webkit-mask: linear-gradient(to right, transparent, black 30%, black 70%, transparent);
  mask: linear-gradient(to right, transparent, black 30%, black 70%, transparent);
  filter: blur(3px);
  transform-origin: 50% 100%;
}
/* 2026-10-20 (Tony): the circle (.portal-image, a map crop) and its wisps are removed; the caption plate sits on the beam. */

/* portal enter/leave */
.portal.enter .portal-beam  { animation: beamIn 0.7s var(--ease-out) both; }
.portal.enter .portal-caption { animation: capIn 0.5s 0.3s var(--ease-out) both; }
.portal.enter .portal-pool  { animation: fade 0.4s var(--ease-out) both; opacity: 0.35; transform: scale(0.6); }
.portal.leave .portal-caption { animation: capIn 0.35s var(--ease-out) reverse both; pointer-events: none; }
.portal.leave .portal-beam, .portal.leave .portal-pool { animation: fade 0.35s var(--ease-out) reverse both; }
@keyframes beamIn { from { transform: scaleY(0); opacity: 0 } to { transform: scaleY(1); opacity: 1 } }
/* 2026-09-22: the last frame used to be clip-path: circle(75%), and `both` made it stick
   forever. Together with this element's drop-shadow that left a hard square edge around
   the portal (Tony saw it in Chrome; the old handoff guessed wrongly that it was only a
   screenshot artifact). Ending on clip-path: none keeps the reveal and drops the square. */
@keyframes capIn { from { opacity: 0; transform: translate(-50%, 12px) } to { opacity: 1; transform: translate(-50%, 0) } }
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }

/* ---------- chrome ---------- */
.chrome {
  position: fixed; left: 0; z-index: 20;
  /* 2026-09-22: the bar used to stay full width when the side panel opened, so its
     right-hand item slid underneath it. "Back to overview" sat at x 1229-1406 while the
     panel covered 960-1440: visible, but every click hit the panel instead. The bar now
     ends where the panel begins. --panel-open is 0..1 and follows the panel's own glide. */
  right: calc(var(--panel-open, 0) * var(--panel-w)); z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  padding: 26px 34px; pointer-events: none;
}
.chrome > * { pointer-events: auto; }
.chrome-top { top: 0; }
.chrome-bottom { bottom: 0; padding-bottom: calc(22px + var(--safe-b)); align-items: flex-end; }
.chrome-bottom .overview { position: absolute; right: 34px; bottom: calc(26px + var(--safe-b)); }
.textlink { display: inline-flex; align-items: center; gap: 10px; padding: 10px 0; font-size: 14.5px; letter-spacing: 0.26em; text-transform: uppercase; color: var(--ink-dim); border-bottom: 1px solid transparent; transition: color 0.25s, border-color 0.25s; }
.textlink svg { width: 14px; height: 14px; }
.textlink:hover { color: var(--ink); border-bottom-color: rgba(232,236,242,0.5); }
.summary-actions { display: flex; align-items: center; justify-content: center; gap: 14px; }
.summary-cta { display: inline-block; padding: 15px 34px; }
.pager { width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(232,236,242,0.22); color: var(--ink-dim); display: inline-flex; align-items: center; justify-content: center; transition: border-color 0.25s, color 0.25s, background 0.25s; }
.pager svg { width: 16px; height: 16px; }
.pager:hover { color: var(--ink); border-color: rgba(232,236,242,0.6); background: rgba(232,236,242,0.06); }
#explore.away { opacity: 0; pointer-events: none; }
#explore { transition: opacity 0.4s var(--ease); }

/* Wordmark: big and centred under the menu (Tony's placement in the design canvas 2026-09-15: left 439, top 75, 70 px at 1440) */
.brand { position: fixed; left: 50%; top: var(--brand-top, 98px); transform: translateX(calc(-50% - var(--panel-open, 0) * var(--panel-w) / 2)); z-index: 21; font-family: var(--serif); font-size: var(--brand-size, 80px);   /* Tony 2026-09-15: bigger, centred, more room under the menu */ letter-spacing: 0.32em; text-transform: uppercase; font-weight: 500; white-space: nowrap; pointer-events: auto; }
.chrome-top { justify-content: center; right: calc(var(--panel-open, 0) * var(--panel-w)); transition: none; }   /* shrinks to the visible map while the panel is open */
.chrome-top .sound { position: absolute; right: 34px; top: 26px; }
.nav { display: flex; gap: 30px; font-size: 15.5px; letter-spacing: 0.28em; text-transform: uppercase; color: var(--ink-dim); }
.nav a { padding: 12px 6px; }        /* 44 px tall targets */
.nav a:hover { color: var(--ink); }
.sound { display: flex; align-items: center; gap: 10px; padding: 12px 0 12px 12px; font-size: 14.5px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--ink-dim); }
.sound-dot { width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--ink-dim); }
.sound[aria-pressed="true"] .sound-dot { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 10px var(--accent); }

.ghostbtn {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 14px 22px; border: 1px solid rgba(232,236,242,0.28); border-radius: 2px;
  font-size: 14.5px; letter-spacing: 0.26em; text-transform: uppercase;
  backdrop-filter: blur(6px); background: rgba(5,8,16,0.25);
  transition: border-color 0.3s, background 0.3s;
}
.ghostbtn:hover { border-color: rgba(232,236,242,0.7); background: rgba(5,8,16,0.45); }
.ghostbtn-icon { font-size: 16px; opacity: 0.8; }
.hint { position: absolute; right: 34px; bottom: calc(22px + var(--safe-b)); font-size: 14.5px; color: var(--ink-faint); letter-spacing: 0.06em; margin: 0; text-align: right; line-height: 1.6; transition: opacity 0.5s; opacity: 0; }   /* the headline says it now; the placement tool still writes here */

/* Hero: bottom-left, just above the Explore button (Tony's placement in the design canvas 2026-09-15) */
.hero { position: fixed; left: 34px; bottom: calc(22px + 56px + 28px + var(--safe-b)); z-index: 25; max-width: min(1100px, calc(100vw - 68px)); pointer-events: none; transition: opacity 0.45s var(--ease-out), transform 0.6s var(--ease-out); }
.hero.away { opacity: 0; transform: translateX(-24px); }
.hero-title { font-family: var(--serif); font-weight: 500; font-size: clamp(38px, 4.6vw, 66px); line-height: 1.02; margin: 0; letter-spacing: -0.01em; }
.hero-sub { font-size: 14.5px; letter-spacing: 0.18em; text-transform: uppercase; color: #ffb060; opacity: 0.9; margin: 14px 0 0; max-width: 480px; line-height: 1.8; }   /* same voice as the badge (Tony 2026-09-15) */
.landing-count {
  display: flex; gap: 1.1rem; margin: 0; pointer-events: none; flex: none;
}
.landing-count div { min-width: 3.2rem; }
.landing-count strong {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: clamp(1.6rem, 3vw, 2.2rem); line-height: 1; letter-spacing: 0.04em;
}
.landing-count span {
  font-family: var(--sans); font-size: 0.62rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-faint);
}
/* Circular Chat + Portal orbs — size = countdown + Explore; hang down, don't grow the hero */
.landing-plaque { pointer-events: none; }
.landing-dock-row {
  --count-h: 3.4rem;
  --orb-size: calc(3.4rem + 3.1rem); /* countdown + #explore */
  display: flex; align-items: flex-start; gap: 16px; flex-wrap: nowrap;
  margin-top: 14px; width: max-content; max-width: 100%;
  /* Layout height = countdown only; orbs overflow downward beside Explore */
  height: var(--count-h);
  overflow: visible;
}
.landing-count { height: var(--count-h); }
.landing-orbs {
  display: flex; align-items: flex-start; gap: 12px; margin: 0;
  pointer-events: auto; flex: none;
  height: var(--orb-size); /* taller than the row → hangs down, no empty lines above */
  transform: translate(20px, 20px);
}
.landing-orb {
  position: relative; flex: none;
  width: var(--orb-size); height: var(--orb-size);
}
.landing-orb-label {
  position: absolute; left: 50%; top: calc(100% + 0.35rem);
  transform: translateX(-50%); white-space: nowrap;
  font-family: var(--sans); font-size: 0.62rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: #fff; line-height: 1;
  pointer-events: none;
  text-shadow: 0 1px 3px rgba(0,0,0,0.85);
}
.landing-skull { display: block; width: 100%; height: 100%; margin: 0; line-height: 0; }
.landing-skull-fallback,
.landing-skull .skull-widget,
.landing-xr {
  display: grid; place-items: center;
  width: 100%; height: 100%; border-radius: 50%;
  border: 1px solid rgba(232, 236, 242, 0.22);
  background: rgba(13, 11, 9, 0.55);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
  overflow: hidden; text-decoration: none; color: #e8ecf2;
  font-family: var(--sans); font-size: 0.62rem; letter-spacing: 0.14em;
  text-transform: uppercase;
}
.landing-skull-fallback:hover,
.landing-skull-fallback:focus-visible,
.landing-skull .skull-widget:hover,
.landing-skull .skull-widget:focus-visible,
.landing-xr:hover,
.landing-xr:focus-visible {
  outline: 1px solid #ffb060;
  outline-offset: 1px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
}
.landing-skull .skull-widget {
  position: relative !important; right: auto !important; bottom: auto !important; z-index: 1;
  width: 100% !important; height: 100% !important;
  padding: 0; cursor: pointer; margin: 0;
  background: rgba(13, 11, 9, 0.55) !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35) !important;
}
/* 2026-10-19 perf: the build's copy of the phone programme (scripts/prerender-phone.mjs) never shows on a wide screen */
@media (min-width: 721px) { main.mob-page[data-snap] { display: none; } }
/* until the map is ready, the phone programme (and its sheet) sit above the black fade-in (body::after, z 60) */
@media (max-width: 720px) {
  main.mob-page[data-snap], body.snap-early:not(.ready) .mob-page { z-index: 61; }
  body.snap-early:not(.ready) .panel { z-index: 62; }
}
/* 2026-10-19 perf (Tony's "go"): on a phone Hector's 3D skull (3.8 MB) is not loaded until the visitor opens him;
   the orb shows one still frame of him instead (3.7 KB). The tap goes to chat/, as it did. See src/landing/orbs.js.
   2026-10-20 perf: the same still frame on desktop until the visitor points at, focuses or touches Hector,
   and under the 3D canvas while the skull loads (.skull-loading). */
.landing-skull-fallback { background: url(assets/hector-orb.webp) center / cover no-repeat, rgba(13, 11, 9, 0.55); font-size: 0; }
.landing-skull .skull-widget.skull-loading { background: url(assets/hector-orb.webp) center / cover no-repeat, rgba(13, 11, 9, 0.55) !important; }
.landing-skull .skull-widget:hover,
.landing-skull .skull-widget:focus-visible {
  outline: 1px solid #ffb060 !important;
  outline-offset: 1px !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45) !important;
}
.landing-skull .skull-widget:disabled { cursor: wait; opacity: 0.72; }
.landing-skull .skull-widget canvas { display: block; width: 100%; height: 100%; pointer-events: none; }
.landing-xr-art { width: 100%; height: 100%; display: block; }
.xr-spin { transform-origin: 60px 60px; animation: xrSpin 7.5s linear infinite; }
.xr-spin-slow { transform-origin: 60px 60px; animation: xrSpin 18s linear infinite reverse; }
.xr-lens-glow { animation: xrPulse 2.4s ease-in-out infinite; transform-origin: 44px 58px; }
.xr-lens-glow.xr-lens-r { transform-origin: 76px 58px; animation-delay: -1.1s; }
.xr-pupil { animation: xrLook 5.6s ease-in-out infinite; }
.xr-pupil-r { animation-delay: -0.35s; }
.xr-wisp { animation: xrWisp 4.2s ease-in-out infinite; }
.xr-wisp-2 { animation-delay: -2s; }
.xr-headset { transform-origin: 60px 62px; animation: xrBob 3.8s ease-in-out infinite; }
.xr-mark {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 9px; letter-spacing: 0.28em; fill: #e4d7c3; opacity: 0.82;
}
@keyframes xrSpin { to { transform: rotate(360deg); } }
@keyframes xrPulse {
  0%, 100% { opacity: 0.45; transform: scale(0.92); }
  50% { opacity: 1; transform: scale(1.12); }
}
@keyframes xrLook {
  0%, 18%, 100% { transform: translate(0, 0); }
  30% { transform: translate(2.2px, 1px); }
  58% { transform: translate(-2px, 0.6px); }
  76% { transform: translate(0.4px, -1.4px); }
}
@keyframes xrWisp {
  0%, 100% { opacity: 0.2; transform: translateY(0); }
  50% { opacity: 0.7; transform: translateY(-3px); }
}
@keyframes xrBob {
  0%, 100% { transform: translateY(0) rotate(-1.2deg); }
  50% { transform: translateY(-2px) rotate(1.4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .xr-spin, .xr-spin-slow, .xr-lens-glow, .xr-pupil, .xr-wisp, .xr-headset { animation: none; }
}
@media (max-width: 900px) {
  .landing-dock-row {
    --count-h: 3rem;
    --orb-size: calc(3rem + 2.75rem);
    gap: 12px;
  }
  .landing-orbs { gap: 10px; }
}

.places { position: absolute; left: -9999px; }  /* visually hidden, still reachable */

.example-badge {
  position: fixed; bottom: calc(24px + var(--safe-b)); left: 50%; transform: translateX(calc(-50% - var(--panel-open, 0) * var(--panel-w) / 2)); z-index: 30; margin: 0; white-space: nowrap;   /* bottom centre of the map, clear of Globen's name */
  font-size: 14.5px; letter-spacing: 0.18em; color: #ffb060; opacity: 0.9;
}

/* ---------- summary ---------- */
.summary {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 15; pointer-events: none;
  padding: 64px 0 calc(28px + var(--safe-b));
  background: linear-gradient(to top, rgba(4,7,14,0.78) 0%, rgba(4,7,14,0.42) 50%, transparent 100%);
  text-align: center;
  animation: summaryIn 0.6s 0.2s var(--ease) both;
}
.summary-inner { pointer-events: auto; max-width: 620px; margin: 0 auto; padding: 0 20px; }
@keyframes summaryIn { from { opacity: 0; transform: translateY(14px) } to { opacity: 1; transform: none } }
.summary-label { font-size: 14.5px; letter-spacing: 0.3em; text-transform: uppercase; color: var(--ink-dim); margin: 0 0 8px; }
.summary-title { font-family: var(--serif); font-weight: 500; font-size: clamp(30px, 3.4vw, 46px); margin: 0; line-height: 1.05; }
.summary-meta { font-size: 16.5px; color: var(--ink-dim); margin: 10px 0 18px; letter-spacing: 0.04em; }
.summary-choice { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 0 0 18px; }
.summary-choice button {
  padding: 9px 14px; border: 1px solid rgba(232,236,242,0.22); border-radius: 2px; font-size: 15.5px; color: var(--ink-dim);
}
.summary-choice button[aria-pressed="true"] { color: var(--ink); border-color: var(--accent); }
.outline {
  padding: 15px 34px; border: 1px solid rgba(232,236,242,0.5); border-radius: 2px;
  font-size: 14.5px; letter-spacing: 0.3em; text-transform: uppercase; transition: border-color 0.3s, background 0.3s;
}
.outline:hover { border-color: var(--ink); background: rgba(232,236,242,0.06); }

/* ---------- details ---------- */
.details {                            /* centred card on desktop (Tony 2026-09-14); phone rules below */
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(760px, 92vw); max-height: 86vh; z-index: 40;
  display: flex; flex-direction: column;
  background: rgba(7,10,18,0.82); backdrop-filter: blur(22px);
  border: 1px solid color-mix(in srgb, var(--c, var(--accent)) 35%, transparent); border-radius: 6px;
  box-shadow: 0 30px 90px rgba(0,0,0,0.6), 0 0 60px color-mix(in srgb, var(--c, var(--accent)) 12%, transparent);
  animation: cardIn 0.35s var(--ease-out) both;
  user-select: text;
}
@keyframes cardIn { from { opacity: 0; transform: translate(-50%, -50%) scale(0.96) } to { opacity: 1; transform: translate(-50%, -50%) scale(1) } }
.details.info { width: min(560px, 92vw); }
.details.info .details-scroll { display: block; }
.details.info .facts a { font-family: var(--serif); font-size: 22px; color: var(--ink); border-bottom: 1px solid rgba(232,236,242,0.35); }
.details.info .facts a:hover { border-bottom-color: var(--accent); }
.details.info .details-long p { margin: 0 0 14px; font-size: 19px; color: var(--ink); }
.details .esc-hint {
  position: absolute; left: 50%; top: calc(100% + 18px); transform: translateX(-50%);
  font-size: 14.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-dim); white-space: nowrap;
}
.details .esc-hint kbd { font: inherit; padding: 3px 7px; border: 1px solid rgba(232,236,242,0.35); border-radius: 3px; margin: 0 4px; }
.details-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 40px 48px 44px; overscroll-behavior: contain; }
.close {
  position: absolute; top: 16px; right: 16px; z-index: 2; width: 44px; height: 44px; border-radius: 50%;
  font-size: 26px; line-height: 1; color: var(--ink); border: 1px solid rgba(232,236,242,0.35); background: rgba(7,10,18,0.6);
}
.close:hover { color: var(--ink); border-color: rgba(232,236,242,0.5); }
.details-figure { margin: 0 0 26px; }
.details .details-scroll { display: grid; grid-template-columns: 260px 1fr; column-gap: 36px; align-content: start; }
.details .details-figure { grid-row: 1 / span 6; }
.details .back-map, .details .solid, .details .fine { grid-column: 2; }
.details-figure img { width: 100%; aspect-ratio: 4/3; object-fit: cover; border-radius: 3px; display: block; }
.details-figure figcaption { font-size: 14.5px; letter-spacing: 0.2em; text-transform: uppercase; color: #ffb060; margin-top: 8px; }
.details-label { font-size: 14.5px; letter-spacing: 0.3em; text-transform: uppercase; color: var(--ink-dim); margin: 0 0 8px; }
.details h2 { font-family: var(--serif); font-weight: 500; font-size: 36px; line-height: 1.05; margin: 0 0 14px; }
.details-short { font-size: 19.5px; line-height: 1.55; margin: 0 0 24px; color: var(--ink); }
.facts { display: grid; grid-template-columns: 110px 1fr; gap: 10px 16px; margin: 0 0 24px; padding: 20px 0; border-top: 1px solid rgba(232,236,242,0.1); border-bottom: 1px solid rgba(232,236,242,0.1); font-size: 18.5px; }
.facts dt { color: var(--ink-dim); letter-spacing: 0.1em; text-transform: uppercase; font-size: 15px; padding-top: 4px; }
.facts dd { margin: 0; }
/* 2026-10-20 (Tony): running text 1.7-1.75 -> 1.6, close to the short texts (1.55) */
.details-long { font-size: 19px; line-height: 1.6; color: var(--ink-dim); margin: 0 0 28px; }
.solid {
  display: block; text-align: center; padding: 16px; background: color-mix(in srgb, var(--c, var(--accent)) 82%, white); color: var(--bg);
  box-shadow: 0 6px 24px color-mix(in srgb, var(--c, var(--accent)) 30%, transparent);
  border-radius: 2px; font-size: 14.5px; letter-spacing: 0.3em; text-transform: uppercase; font-weight: 500;
}
.fine { font-size: 16.5px; color: var(--ink-dim); margin: 12px 0 0; text-align: center; }
.back-map { margin: 34px auto 0; display: flex; justify-content: center; }

.prog { list-style: none; margin: 20px 0 0; padding: 0; }
.prog li { border-top: 1px solid rgba(232,236,242,0.1); }
.prog button { display: block; width: 100%; text-align: left; padding: 16px 0; }
.prog .cat { font-size: 14.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-dim); }
.prog .t { font-family: var(--serif); font-size: 22px; margin: 4px 0 2px; }
.prog .w { font-size: 15.5px; color: var(--ink-dim); }

.veil { position: fixed; inset: 0; z-index: 35; background: rgba(4,7,14,0.55); backdrop-filter: blur(2px); }


/* caption inside the portal circle: the event's title and time on a dark plate, always readable (Tony 2026-09-15) */
.portal-caption {
  /* 2026-09-22: this box used to live INSIDE .portal-image, which carries a round mask.
   The mask applies to children too, so a wide caption had its ends cut off, and with the
   old auto width it came out 135 px wide with the date over four lines on the picture.
   It now sits in .portal instead: outside the mask, its own width, below the circle. */
  position: absolute; left: 0; bottom: calc(var(--beam, 300px) - 4px);   /* 2026-10-20: the beam runs into the plate, no circle */
  transform: translateX(-50%); z-index: 2; cursor: pointer;
  width: max-content; max-width: min(78vw, 420px);
  display: flex; flex-direction: column; align-items: center; gap: 5px; text-align: center; pointer-events: auto;
  padding: 11px 14px 12px; border-radius: 3px; background: rgba(6,9,16,0.72); backdrop-filter: blur(8px);
  border: 1px solid color-mix(in srgb, var(--c) 35%, transparent); box-shadow: 0 -6px 18px rgba(0,0,0,0.35);   /* 2026-10-20: shadow upward, so it no longer darkens the top of the beam */
}
.portal-caption .pc-row { display: flex; align-items: center; gap: 8px; }
.portal-caption .pc-title { font-family: var(--serif); font-size: 21px; line-height: 1.12; color: var(--ink); text-wrap: balance; }
.portal-caption .pc-arrow { pointer-events: auto; cursor: pointer; flex: none; width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: var(--ink-dim); border: 1px solid rgba(232,236,242,0.28); transition: color 0.25s, border-color 0.25s, background 0.25s; }
.portal-caption .pc-arrow svg { width: 13px; height: 13px; }
.portal-caption .pc-arrow:hover { color: var(--ink); border-color: rgba(232,236,242,0.6); background: rgba(232,236,242,0.08); }
.portal-caption .pc-arrow[hidden] { display: none; }
.portal-caption .pc-dots { display: flex; gap: 6px; margin-top: 3px; }
.portal-caption .pc-dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(232,236,242,0.28); transition: background 0.3s, box-shadow 0.3s; }
.portal-caption .pc-dots i.on { background: var(--c); box-shadow: 0 0 8px var(--c); }
.portal-caption .pc-dots:empty { display: none; }
.portal-caption .pc-when { font-family: var(--sans); font-size: 15.5px; letter-spacing: 0.14em; text-transform: uppercase; color: color-mix(in srgb, var(--c) 80%, white); white-space: normal; line-height: 1.5; max-width: 100%; }
.ghost.selected .card { display: none; }   /* the hover card is for browsing; the chosen ghost has the circle and the panel */

/* ---------- side panel (Tony 2026-09-15: one place for all information, three depths) ---------- */
:root { --panel-w: 480px; }   /* Tony 2026-09-15: a little wider */
.thread { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 14; pointer-events: none; overflow: visible; }
.thread path { fill: none; stroke-linecap: round; }
#thread-path { stroke: var(--c, var(--accent)); stroke-width: 7; opacity: 0.16; filter: blur(6px);
  animation: threadBreath 6.5s ease-in-out infinite; }
#thread-core { stroke: var(--c, var(--accent)); stroke-width: 1.1; opacity: 0.55;
  stroke-dasharray: 34 1400; animation: threadPulse 4.6s cubic-bezier(0.37,0,0.2,1) infinite; }
@keyframes threadBreath { 0%,100% { opacity: 0.10 } 50% { opacity: 0.19 } }
@keyframes threadPulse {
  0%   { stroke-dashoffset: 0;     opacity: 0 }
  22%  { opacity: 0.52 }          /* fades in over a longer stretch, so it never snaps on */
  72%  { opacity: 0.40 }
  100% { stroke-dashoffset: -1434; opacity: 0 }
}
.thread { animation: threadIn 0.6s var(--ease-out) both; }   /* draws in when the panel opens */
@keyframes threadIn { from { opacity: 0 } to { opacity: 1 } }
@media (prefers-reduced-motion: reduce) {
  #thread-core, #thread-path, .thread { animation: none; }
  #thread-core { stroke-dasharray: none; opacity: 0.45; }
}
.thread[hidden] { display: none; }

.panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: var(--panel-w); z-index: 40;
  display: flex; flex-direction: column;
  background: linear-gradient(to right, rgba(5,8,16,0.55), rgba(5,8,16,0.80) 30%, rgba(5,8,16,0.86));
  backdrop-filter: blur(10px) saturate(1.1);   /* lighter blur: the strip re-blurs a moving scene every frame */
  border-left: 1px solid color-mix(in srgb, var(--c, var(--accent)) 30%, transparent);
  box-shadow: -30px 0 80px rgba(0,0,0,0.45), inset 1px 0 0 color-mix(in srgb, var(--c, var(--accent)) 10%, transparent);
  transform: translateX(calc((1 - var(--panel-open, 0)) * 100%)); transition: border-color 0.6s;   /* one clock: follows --panel-open from the glide */
  user-select: text;
}
.panel::before {                     /* the place's colour breathes in from the edge */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 40% at 0% 30%, color-mix(in srgb, var(--c, var(--accent)) 14%, transparent), transparent 70%);
  transition: opacity 0.6s;
}
.panel.open { }
.panel[hidden] { display: flex; }   /* stays in the DOM for the slide-out; visibility handled by .open */
.panel:not(.open) { pointer-events: none; }
.panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 26px 22px 10px 34px; }
.panel-tools { display: flex; align-items: center; gap: 6px; flex: none; }
.panel-tools .pager { width: 36px; height: 36px; }
.panel-tools .pager svg { width: 14px; height: 14px; }
.panel-close { position: static; width: 36px; height: 36px; border-color: rgba(232,236,242,0.28); }
.panel-close svg { width: 14px; height: 14px; }
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 4px; min-height: 36px; font-size: 15.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-dim); }
.crumbs button { color: var(--ink-dim); padding: 6px 0; letter-spacing: inherit; text-transform: inherit; font-size: inherit; border-bottom: 1px solid transparent; transition: color 0.25s, border-color 0.25s; }
.crumbs button:hover { color: var(--ink); border-bottom-color: rgba(232,236,242,0.4); }
.crumbs .sep { opacity: 0.45; padding: 0 2px; }
.crumbs .here { color: var(--ink); }
.panel-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 8px 34px 40px; scrollbar-width: thin; scrollbar-color: rgba(232,236,242,0.18) transparent; }
.panel-body::-webkit-scrollbar { width: 6px; } .panel-body::-webkit-scrollbar-thumb { background: rgba(232,236,242,0.18); border-radius: 3px; }
.panel-body > .lvl { animation: lvlIn 0.45s var(--ease-out) both; }
@keyframes lvlIn { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
.panel-body.back > .lvl { animation-name: lvlBack; }
@keyframes lvlBack { from { opacity: 0; transform: translateX(-18px); } to { opacity: 1; transform: none; } }

.lvl h2 { font-family: var(--serif); font-weight: 500; font-size: 36px; line-height: 1.08; margin: 10px 0 6px; text-wrap: balance; }
.lvl .count { font-size: 16px; color: var(--ink-dim); margin: 0 0 22px; letter-spacing: 0.02em; }
.rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid rgba(232,236,242,0.10); }
.rows li { border-bottom: 1px solid rgba(232,236,242,0.10); }
.row { display: grid; grid-template-columns: 1fr auto; column-gap: 14px; align-items: center; width: 100%; text-align: left; padding: 18px 0 18px 12px; margin-left: -12px; width: calc(100% + 12px); border-radius: 3px; transition: background 0.3s; }
.row:hover, .row:focus-visible, .row.active { background: color-mix(in srgb, var(--c, var(--accent)) 8%, transparent); outline: none; }
.row .cat { font-size: 15.5px; letter-spacing: 0.18em; text-transform: uppercase; color: color-mix(in srgb, var(--c, var(--accent)) 85%, white); }
.row .t { font-family: var(--serif); font-size: 24px; line-height: 1.15; margin: 4px 0 3px; color: var(--ink); }
.row .w { font-size: 15.5px; color: var(--ink-dim); line-height: 1.4; }
.row > span { grid-column: 1; }
.row .go { grid-column: 2; grid-row: 1 / span 3; width: 14px; height: 14px; color: var(--ink-dim); transition: transform 0.3s var(--ease-out), color 0.3s; }
.row:hover .go { transform: translateX(4px); color: var(--ink); }
.place-group { margin: 0 0 26px; }
.place-group h3 { display: flex; align-items: center; gap: 10px; font-family: var(--serif); font-weight: 500; font-size: 22px; margin: 0 0 4px; color: var(--ink); }
.place-group h3 button { font: inherit; color: inherit; display: inline-flex; align-items: center; gap: 10px; }
.place-group h3 button:hover { color: var(--pc); }
.place-group .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pc); box-shadow: 0 0 10px var(--pc); flex: none; }
.place-group .rows { border-top: 0; }

.lvl .ev-cat { font-size: 15.5px; letter-spacing: 0.2em; text-transform: uppercase; color: color-mix(in srgb, var(--c, var(--accent)) 85%, white); margin: 0; }
.lvl .ev-short { font-size: 19.5px; line-height: 1.55; margin: 10px 0 20px; color: var(--ink); }
.lvl .facts { margin: 0 0 20px; }
.lvl .details-long { margin: 0 0 24px; }
.lvl .solid { margin-top: 4px; }
.lvl .fine { text-align: left; }

/* ghost lit from the panel (hovering its row) */
.ghost.lit svg { filter: brightness(1.35) drop-shadow(0 0 14px color-mix(in srgb, var(--c) 80%, transparent)); }
.ghost.lit .light { opacity: 1; transform: scale(1.35); }
.ghost.dim.lit { opacity: 1; }

@media (max-width: 720px) {
  .panel { top: auto; left: 0; right: 0; width: 100%; height: 78%; border-left: 0; border-top: 1px solid rgba(232,236,242,0.12); border-radius: 18px 18px 0 0; transform: translateY(100%); }
  .panel.open { transform: translateY(0); }
  .panel-head { padding: 18px 16px 6px 20px; }
  .panel-body { padding: 6px 20px calc(30px + var(--safe-b)); }
  .thread { display: none; }
}

.map-arrow {
  position: fixed; top: 50%; transform: translateY(-50%); z-index: 22; width: 52px; height: 52px; border-radius: 50%;
  border: 1px solid rgba(232,236,242,0.28); color: var(--ink-dim); background: rgba(5,8,16,0.35); backdrop-filter: blur(6px);
  display: inline-flex; align-items: center; justify-content: center; transition: color 0.25s, border-color 0.25s, background 0.25s;
}
.map-arrow svg { width: 18px; height: 18px; }
.map-arrow:hover { color: var(--ink); border-color: rgba(232,236,242,0.6); background: rgba(232,236,242,0.08); }
.map-arrow.left { left: 28px; }
.map-arrow.right { right: 28px; transform: translate(calc(var(--panel-open, 0) * var(--panel-w) * -1), -50%); }
.map-arrow[hidden] { display: none; }
@media (max-width: 720px) { .map-arrow { display: none; } }

/* ---------- phone ---------- */
@media (max-width: 720px) {
  .chrome { padding: 18px 18px; }
  .brand { font-size: 19.5px; }
  .nav { display: none; }
  .hero { left: 18px; right: 18px; top: 16vh; }
  .hero-sub { font-size: 16px; }
  .hint { display: none; }
  .example-badge { top: 62px; right: 18px; }
  .chrome-bottom { padding-bottom: calc(16px + var(--safe-b)); }
  .ghostbtn { padding: 12px 16px; }
  .summary { padding: 80px 0 calc(18px + var(--safe-b)); text-align: left; }
  .summary-inner { padding: 0 18px; }
  .summary-choice { justify-content: flex-start; }
  .outline { display: block; width: 100%; text-align: center; }
  .details { top: auto; left: 0; bottom: 0; transform: none; height: 88%; max-height: none; width: 100%; border-left: 0; border-radius: 18px 18px 0 0;
    display: block; }
  .details .details-scroll { display: block; height: 100%; }
  .details.info { width: min(560px, 92vw); }
.details.info .details-scroll { display: block; }
.details.info .facts a { font-family: var(--serif); font-size: 22px; color: var(--ink); border-bottom: 1px solid rgba(232,236,242,0.35); }
.details.info .facts a:hover { border-bottom-color: var(--accent); }
.details.info .details-long p { margin: 0 0 14px; font-size: 19px; color: var(--ink); }
.details .esc-hint { display: none; }
  .details-old { top: auto; height: 88%; width: 100%; border-left: 0; border-top: 1px solid rgba(232,236,242,0.12); border-radius: 18px 18px 0 0; animation: sheetIn 0.4s var(--ease) both; }
  @keyframes sheetIn { from { transform: translateY(60px); opacity: 0 } to { transform: none; opacity: 1 } }
  .details-scroll { padding: 64px 22px calc(40px + var(--safe-b)); }
  .details h2 { font-size: 30px; }
  .facts { grid-template-columns: 90px 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .ghost .body { filter: none; }   /* no flutter under reduced motion */
  .ghost svg, .ghost .sway, .ghost .eyes, #explore { animation: none; transition: none; }
  body::after { transition-duration: 0.4s; }
  .summary, .details, .portal.leave * { animation-duration: 0.2s !important; animation-delay: 0s !important; }
  .portal.enter * { animation-duration: 0.01s !important; animation-delay: 0s !important; }
  .hero { transition-duration: 0.2s; } .hero.away { transform: none; }
  .panel-body > .lvl { animation: lvlFade 0.2s both; }
  .ghost .mouth { transition: none; }
}
@keyframes lvlFade { from { opacity: 0 } }

.brand.logo img { display: block; height: var(--logo-h, 160px); width: auto;
  /* moonlight glow (Tony 2026-09-15): a tight white core, a wide cold halo and a faint warm ember under it */
  filter: drop-shadow(0 0 2px rgba(255,255,255,0.75)) drop-shadow(0 0 10px rgba(205,225,255,0.55))
          drop-shadow(0 0 30px rgba(150,190,255,0.40)) drop-shadow(0 0 68px rgba(110,160,255,0.28))
          drop-shadow(0 6px 34px rgba(255,150,60,0.16));
  animation: brandBreath 7.5s ease-in-out infinite; }
@keyframes brandBreath {
  0%, 100% { filter: drop-shadow(0 0 2px rgba(255,255,255,0.70)) drop-shadow(0 0 10px rgba(205,225,255,0.50))
                     drop-shadow(0 0 28px rgba(150,190,255,0.34)) drop-shadow(0 0 60px rgba(110,160,255,0.22))
                     drop-shadow(0 6px 30px rgba(255,150,60,0.13)); }
  50%      { filter: drop-shadow(0 0 3px rgba(255,255,255,0.85)) drop-shadow(0 0 14px rgba(205,225,255,0.62))
                     drop-shadow(0 0 40px rgba(150,190,255,0.48)) drop-shadow(0 0 88px rgba(110,160,255,0.34))
                     drop-shadow(0 8px 44px rgba(255,150,60,0.20)); }
}
@media (prefers-reduced-motion: reduce) { .brand.logo img { animation: none; } }   /* Tony's logo test, ?logo */


/* Way back from one event to the list of events at that place (Tony 2026-09-15).
   Full width, big, plain words, so nobody has to find the small breadcrumb. */
.back-up { display: flex; align-items: center; gap: 12px; width: 100%; margin: 0 0 22px;
  padding: 14px 16px; border: 1px solid rgba(232,236,242,0.16); border-radius: 4px;
  background: rgba(255,255,255,0.03); color: var(--ink); text-align: left; cursor: pointer;
  transition: background 0.25s, border-color 0.25s, transform 0.25s var(--ease-out); }
.back-up:hover { background: rgba(255,255,255,0.07); border-color: rgba(232,236,242,0.34); transform: translateX(-3px); }
.back-up .ar { font-size: 26px; line-height: 1; opacity: 0.8; }
.back-up .tx { font-family: var(--serif); font-size: 22px; letter-spacing: 0.01em; }
.back-up .n { margin-left: auto; font-size: 15.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-dim); }
.crumbs button { border-bottom-color: rgba(232,236,242,0.22); }   /* crumbs look clickable before hover */


/* A sleepy ghost opens its eyes when it looks up at you (Tony 2026-09-15).
   The squash sits in the element's own style attribute, so the open state needs !important. */
.ghost .eye.sleepy { transition: transform 0.3s var(--ease-out); }
.ghost:hover .eye.sleepy, .ghost.lit .eye.sleepy, .ghost:focus-visible .eye.sleepy { transform: none !important; }


/* ================================ phone ================================
   Tony's decision 2026-09-15: approach A. The world stays, and the visitor meets
   one place at a time. Everything below sits inside @media (max-width: 720px), so
   no desktop rule is touched. Plan: spec/PLAN-MOBILE-2026-09-15.md */
@media (max-width: 720px) {
  :root {
    /* the phone's own edges, all four of them (plan section 6). Only --safe-b existed. */
    --safe-t: env(safe-area-inset-top, 0px);
    --safe-l: env(safe-area-inset-left, 0px);
    --safe-r: env(safe-area-inset-right, 0px);
    --mob-nav: 62px;      /* the place nav row */
    --mob-sheet: 96px;    /* the collapsed sheet */
    /* The desktop side panel is 480 px wide and the header, the wordmark and the badge all
       slide sideways by that width while it is open. On a 390 px phone that pushed the Sound
       control right off the screen, measured 2026-09-15. The sheet is full width here, so the
       slide is zero. */
    --panel-w: 0px;
  }

  /* the desktop chrome that has no job here */
  .chrome-bottom .overview, .hint, .map-arrow, .thread, .summary { display: none !important; }
  #explore { display: none; }

  /* Tony 2026-09-15: "det zoomas aven konstigt ... beroende pa vart man trycker".
     That is Safari's double-tap-to-zoom. manipulation keeps scrolling and pinching and
     drops the double-tap gesture, so a second tap on a ghost or a row is just a tap. */
  html, body { touch-action: manipulation; }
  .panel, .panel-body { touch-action: pan-y; }
  .chrome-top { padding: calc(var(--safe-t) + 14px) calc(var(--safe-r) + 16px) 10px calc(var(--safe-l) + 16px); }
  .chrome-top .sound { right: calc(var(--safe-r) + 16px); top: calc(var(--safe-t) + 14px); padding: 10px 0 10px 10px; }
  /* Tony on his iPhone 2026-09-15: "massiva fyrkanter ... man ser avfallskanterna".
     Five stacked drop-shadows animated by brandBreath make iOS Safari give the wordmark its
     own compositing layer, and that layer's edges show as a bright rectangle in the sky.
     One static, smaller glow here: no animation, no layer, no rectangle. */
  .brand.logo img { height: clamp(52px, 17vw, 78px); animation: none;
    filter: drop-shadow(0 0 2px rgba(255,255,255,0.72)) drop-shadow(0 0 14px rgba(160,195,255,0.38)); }
  .example-badge { top: calc(var(--safe-t) + 58px); bottom: auto; left: 50%; right: auto; font-size: 10.5px; letter-spacing: 0.16em; }

  .hero { left: calc(var(--safe-l) + 18px); right: calc(var(--safe-r) + 18px); top: auto;
    bottom: calc(var(--safe-b) + var(--mob-sheet) + var(--mob-nav) + 24px); }
  .hero-title { font-size: 30px; }

  /* ---- the place nav: previous, five named dots, next. Every target is 44 px ---- */
  .mob-nav {
    position: fixed; z-index: 34; left: calc(var(--safe-l) + 8px); right: calc(var(--safe-r) + 8px);
    bottom: calc(var(--safe-b) + var(--mob-sheet) + 6px); height: var(--mob-nav);
    display: flex; align-items: center; justify-content: center; gap: 2px;
  }
  .mob-step, .mob-dot {
    width: 44px; height: 44px; flex: 0 0 44px; display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; color: var(--ink-dim); -webkit-tap-highlight-color: transparent;
  }
  .mob-step svg { width: 17px; height: 17px; }
  .mob-step { border: 1px solid rgba(232,236,242,0.24); background: rgba(5,8,16,0.42); backdrop-filter: blur(6px); }
  .mob-dot i {
    display: block; width: 8px; height: 8px; border-radius: 50%;
    background: rgba(232,236,242,0.34); transition: background 0.3s var(--ease), transform 0.3s var(--ease-out);
  }
  .mob-dot.on i { background: var(--c, #7fe3ff); transform: scale(1.6); box-shadow: 0 0 12px var(--c, #7fe3ff); }
  .mob-dot:focus-visible, .mob-step:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

  /* ---- the collapsed sheet: tap it, or drag it up, to open what is on ---- */
  .mob-handle {
    position: fixed; z-index: 34; left: calc(var(--safe-l) + 12px); right: calc(var(--safe-r) + 12px);
    bottom: calc(var(--safe-b) + 10px); min-height: 72px; padding: 12px 18px 14px;
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    border: 1px solid color-mix(in srgb, var(--c, #7fe3ff) 34%, transparent); border-radius: 16px;
    background: rgba(6,9,16,0.74); backdrop-filter: blur(14px);
    box-shadow: 0 -6px 26px rgba(0,0,0,0.42); -webkit-tap-highlight-color: transparent;
    touch-action: none;
  }
  .mob-grip { width: 38px; height: 4px; border-radius: 2px; background: rgba(232,236,242,0.34); margin-bottom: 4px; }
  .mob-handle-place { font-family: var(--serif); font-size: 21px; color: var(--ink); }
  .mob-handle-count { font-size: 11.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--c, #7fe3ff); }

  /* ---- the way out to all five places, and back in ---- */
  /* top left, opposite the Sound control: down at the bottom it landed on the ghost's
     own name, measured 2026-09-15 at 390x844 */
  .mob-all {
    position: fixed; z-index: 34; left: calc(var(--safe-l) + 10px); top: calc(var(--safe-t) + 10px);
    display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 12px; min-height: 44px; font-size: 11.5px; letter-spacing: 0.22em;
    text-transform: uppercase; color: var(--ink-dim); -webkit-tap-highlight-color: transparent;
  }
  .mob-all svg { width: 15px; height: 15px; }
  .mob-all:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

  /* the place name is spoken, not drawn */
  .mob-live { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

  /* The centred ghost must be tappable. On the desktop the chosen ghost hands its clicks to
     the portal that rises out of it, so it carries pointer-events: none. There is no portal
     here: the ghost itself is what opens the sheet. Measured with a real finger 2026-09-15:
     three taps on the centred ghost did nothing at all before this. */
  .ghost.selected { pointer-events: auto; }

  /* ---- the overview: five ghosts at once, so everything about them steps back ---- */
  .ghost .card { display: none; }               /* a touch screen has no hover: the card only got in the way */
  body.mob-overview .ghost svg { scale: calc(var(--gscale, 1) * 0.8); }
  body.mob-overview .ghost .name { font-size: 16px; letter-spacing: 0.03em; top: calc(100% + 3px); }
  body.mob-overview .ghost.name-up .name { top: auto; bottom: calc(100% + 4px); }
  body.mob-overview .mob-all { color: var(--ink); }
  /* the invitation belongs in the sky here, not on top of the city band */
  /* under the wordmark, never on it: --brand-top and --logo-h are written by main.js
     from the height of the sky, so the two cannot drift apart */
  /* Phone: no countdown; Chat left + Portal right at the bottom corners */
  body.phone .landing-count { display: none; }
  body.phone .landing-dock-row {
    height: 0; margin: 0; overflow: visible; pointer-events: none;
    --orb-size: 4.75rem;
  }
  body.phone .landing-orbs {
    position: fixed; left: 0; right: 0;
    bottom: calc(var(--safe-b) + 14px);
    z-index: 35; display: flex; justify-content: space-between; align-items: flex-end;
    padding: 0 calc(var(--safe-r) + 14px) 0 calc(var(--safe-l) + 14px);
    margin: 0; height: auto; transform: none; pointer-events: none;
  }
  body.phone .landing-orb {
    width: var(--orb-size); height: var(--orb-size);
    pointer-events: auto;
  }
  body.phone .landing-orb-label {
    top: auto; bottom: calc(100% + 0.25rem); font-size: 0.55rem;
  }
  body.mob-overview .hero { bottom: auto; left: 16px; right: 16px;
    top: calc(var(--brand-top, 98px) + var(--logo-h, 64px) + 34px); text-align: center; }
  body.mob-overview .hero-title { font-size: 26px; }
  body.mob-overview .hero-sub { margin: 10px auto 0; }

  /* ---- the sheet itself reuses the panel, with the phone's own edges ---- */
  .panel { height: min(78%, calc(100% - var(--safe-t) - 96px)); padding-left: var(--safe-l); padding-right: var(--safe-r); }
  .panel-head { padding: 16px calc(var(--safe-r) + 14px) 6px calc(var(--safe-l) + 20px); }
  .panel-body { padding: 6px calc(var(--safe-r) + 20px) calc(30px + var(--safe-b)) calc(var(--safe-l) + 20px); }
  .panel .close { min-width: 44px; min-height: 44px; }
  /* the sheet has exactly two depths here, place and event, so the Program crumb that
     leads to a third one is not offered (the overview is the phone's all-places view) */
  .crumbs [data-crumb="programme"], .crumbs [data-crumb="programme"] + .sep { display: none; }
  body.mob-sheet-open .brand, body.mob-sheet-open .example-badge, body.mob-sheet-open .hero { opacity: 0; pointer-events: none; }

  /* a phone that asks for less motion still gets a page it can read */
  @media (prefers-reduced-motion: reduce) {
    .mob-dot.on i { transform: none; }
  }
}


/* The phone chrome is built by main.js and only exists when the page opened below 720 px.
   If that window is then widened, or an iPhone is turned to landscape (844 px across), the
   phone rules stop matching and those buttons would fall back to unstyled boxes in the flow.
   Landscape is out of scope for this plan; it must still not look broken. */
@media (min-width: 721px) {
  .mob-nav, .mob-handle, .mob-all, .mob-live { display: none !important; }
}

/* Budget ladder, rung 4: the ghost sheet stops rippling. Only main.js sets this class,
   and only on a phone that could not hold the frame rate. */
body.mob-no-flutter .ghost .body { filter: none; }

/* ---- Tony's notes from his own iPhone, 2026-09-15 ---- */
@media (max-width: 720px) {
  /* 4: the map used to end on a hard horizontal line above the nav row */
  .mob-scrim {
    position: fixed; left: 0; right: 0; bottom: 0; pointer-events: none;   /* no z-index: DOM order puts it under #world */
    height: calc(var(--safe-b) + var(--mob-sheet) + var(--mob-nav) + 56px);
    background: linear-gradient(to top, #050810 38%, rgba(5, 8, 16, 0.78) 64%, rgba(5, 8, 16, 0));
  }
  /* the overview band stops 96 px above the bottom, so the fade has to start above that
     line, not below it, or the join shows as a straight edge */
  body.mob-overview .mob-scrim { height: calc(var(--safe-b) + 176px);
    background: linear-gradient(to top, #050810 52%, rgba(5, 8, 16, 0.86) 62%, rgba(5, 8, 16, 0.45) 76%, rgba(5, 8, 16, 0)); }

  /* 5 + 7: the menu on top of the programme list */
  .mob-menu-rows { display: grid; gap: 0; margin: 4px 0 26px; border-top: 1px solid rgba(232,236,242,0.12); }
  .mob-menu-row {
    display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
    width: 100%; min-height: 56px; padding: 13px 2px; text-align: left;
    border-bottom: 1px solid rgba(232,236,242,0.12); -webkit-tap-highlight-color: transparent;
  }
  .mob-menu-row .t { font-family: var(--serif); font-size: 22px; color: var(--ink); }
  .mob-menu-row .h { font-size: 14.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); }
  .mob-menu-row:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

  /* the Program crumb is a real destination now, so it is shown again */
  .crumbs [data-crumb="programme"], .crumbs [data-crumb="programme"] + .sep { display: inline; }

  /* the About and Contact cards have to clear the phone's own edges */
  .details.info { width: auto; left: calc(var(--safe-l) + 10px); right: calc(var(--safe-r) + 10px);
    top: auto; bottom: 0; height: 82%; border-radius: 18px 18px 0 0; }
}


/* ================= the phone page, Tony's makeover 2026-09-15 =================
   "man ska kunna skrolla ner for att komma till programmet" and "man har svart att
   hitta och vet inte vad fan man tittar pa". The map is the top of an ordinary page
   now. The menu and the bottom sheet are gone: one place for information, the page. */
@media (max-width: 720px) {
  html { scroll-behavior: smooth; }
  body { overflow-y: auto; overflow-x: hidden; overscroll-behavior-y: none; }
  @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

  /* a finger going up and down belongs to the page; sideways still belongs to the map */
  #scene { touch-action: pan-y; }
  #scene, #bats, #fx, #world { opacity: var(--mob-map-fade, 1); transition: opacity 0.12s linear; }

  /* the chrome over the map leaves as the page arrives */
  .brand, .example-badge, .mob-nav, .mob-all, .hero {
    opacity: var(--mob-chrome, 1);
    transition: opacity 0.18s linear;
  }
  body.mob-scrolled .brand, body.mob-scrolled .example-badge,
  body.mob-scrolled .mob-nav, body.mob-scrolled .mob-all { pointer-events: none; }
  body.mob-scrolled .mob-scrim { opacity: 0; }

  /* the cue that there is more, at the foot of the map */
  .mob-all {
    left: 50%; right: auto; top: auto; transform: translateX(-50%);
    bottom: calc(var(--safe-b) + 14px); flex-direction: row; gap: 8px;
    color: var(--ink); font-size: 14.5px; letter-spacing: 0.2em;
  }
  .mob-all .t { white-space: nowrap; }
  .mob-all svg { width: 14px; height: 14px; animation: mobNudge 2.4s ease-in-out infinite; }
  @keyframes mobNudge { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(3px) } }
  @media (prefers-reduced-motion: reduce) { .mob-all svg { animation: none; } }

  /* the nav row sits above the cue */
  .mob-nav { bottom: calc(var(--safe-b) + 54px); }

  /* ---- the page ---- */
  .mob-page { position: relative; z-index: 16; }
  .mob-hero-space { height: 100svh; pointer-events: none; }
  .mob-paper {
    position: relative; background: #050810; border-radius: 22px 22px 0 0;
    padding: 0 0 calc(40px + var(--safe-b));
    box-shadow: 0 -30px 60px rgba(3, 5, 10, 0.9);
  }
  /* the join with the map is a fade, never a line */
  .mob-paper::before {
    content: ""; position: absolute; left: 0; right: 0; top: -120px; height: 120px;
    background: linear-gradient(to top, #050810, rgba(5, 8, 16, 0.72) 45%, rgba(5, 8, 16, 0));
    pointer-events: none;
  }
  .mob-paper > * { padding-left: calc(var(--safe-l) + 22px); padding-right: calc(var(--safe-r) + 22px); }

  .mob-intro { padding-top: 34px; padding-bottom: 8px; }
  .mob-intro .eyebrow { margin: 0 0 10px; font-size: 11.5px; letter-spacing: 0.26em;
    text-transform: uppercase; color: #ffb060; }
  .mob-intro h1 { margin: 0; font-family: var(--serif); font-weight: 500; font-size: 38px;
    line-height: 1.04; letter-spacing: -0.01em; }
  .mob-intro .lede { margin: 14px 0 0; font-size: 19px; line-height: 1.6; color: var(--ink-dim); }

  .mob-place { padding-top: 30px; }
  .mob-place h2 { display: flex; align-items: center; gap: 10px; margin: 0;
    font-family: var(--serif); font-weight: 500; font-size: 27px; }
  .mob-place h2 .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--c);
    box-shadow: 0 0 12px var(--c); flex: none; }
  .mob-place .count { margin: 5px 0 14px 19px; font-size: 11.5px; letter-spacing: 0.22em;
    text-transform: uppercase; color: var(--ink-faint); }
  .mob-place .rows { list-style: none; margin: 0; padding: 0; }
  .mob-place .rows .row { width: 100%; min-height: 64px; }
  .mob-onmap { margin: 12px 0 0 19px; min-height: 44px; padding: 10px 0;
    font-size: 11.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--c); }
  .mob-onmap:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

  .mob-about { padding-top: 40px; margin-top: 24px; border-top: 1px solid rgba(232,236,242,0.12); }
  .mob-about h2 { margin: 0 0 12px; font-family: var(--serif); font-weight: 500; font-size: 25px; }
  .mob-about h2.second { margin-top: 32px; }
  .mob-about p { margin: 0 0 12px; font-size: 19px; line-height: 1.6; color: var(--ink-dim); }
  .mob-about a { font-family: var(--serif); font-size: 21px; color: var(--ink);
    border-bottom: 1px solid rgba(232,236,242,0.35); }
  .mob-about .fine { margin-top: 18px; font-size: 15.5px; color: var(--ink-faint); }
  .mob-foot { padding-top: 30px; font-size: 10.5px; letter-spacing: 0.18em;
    text-transform: uppercase; color: var(--ink-faint); }

  /* the Sound control keeps floating over the page, so it needs its own ground */
  body.mob-scrolled .chrome-top .sound {
    background: rgba(5, 8, 16, 0.72); backdrop-filter: blur(10px);
    border-radius: 999px; padding: 10px 14px 10px 12px;
  }

  /* the event card is the only overlay left */
  .panel { height: min(86%, calc(100% - var(--safe-t) - 64px)); }
}


/* ============ the phone shows the sky, not the map. Tony 2026-09-15 ~19:15 ============
   "bara himlen och manen och sen ... Shockholm ... och vad det ar for nagonting snabbt
   for att sen kunna ga in pa the program". The city, the ghosts and the carousel are all
   off the phone. The desktop keeps every one of them. */
@media (max-width: 720px) {
  #world, #fx, .mob-nav, .mob-scrim { display: none !important; }
  #scene { touch-action: pan-y; }
  #bats { opacity: 0.55; }                       /* bats still cross the sky */

  /* 2026-09-23: the sky and the moon stretched while scrolling. The phone browser slides its
     own toolbar away as the page moves, and `inset: 0` made these two layers grow and shrink
     with it, several times per scroll. Every change re-allocated the WebGL buffer at a new
     shape, so the picture was squeezed and the moon slid. 100lvh is the height WITH the
     toolbar retracted and does NOT change while scrolling, so the box is now constant.
     `bottom: auto` is required: with `inset: 0` the bottom edge would still win over height. */
  #scene, #bats { top: 0; bottom: auto; height: 100vh; height: 100lvh; }

  /* the first screen: wordmark, one line, Program */
  .hero {
    left: calc(var(--safe-l) + 24px); right: calc(var(--safe-r) + 24px);
    top: auto; bottom: calc(var(--safe-b) + 128px); text-align: center; max-width: none;
  }
  .hero-eyebrow { margin: 0; font-size: 15px; letter-spacing: 0.32em;
    text-transform: uppercase; color: #ffb060; }
  .hero .hero-sub { margin: 0; font-size: 19px; line-height: 1.6; letter-spacing: 0;
    text-transform: none; color: var(--ink-dim); }

  .mob-all {
    bottom: calc(var(--safe-b) + 48px);
    border: 1px solid rgba(232,236,242,0.3); border-radius: 999px;
    padding: 14px 22px; color: var(--ink); background: rgba(5,8,16,0.4);
    backdrop-filter: blur(8px); font-size: 14.5px; letter-spacing: 0.24em;
  }

  /* the wordmark sits in the middle of the sky, not tucked under the menu */
  .brand.logo img { height: clamp(62px, 21vw, 96px); }

  /* the Sound control is fixed in the corner, so the heading has to start below it */
  .mob-intro { padding-top: calc(var(--safe-t) + 78px); }
  .mob-intro h1 { font-size: 42px; }
  .mob-paper { border-radius: 0; }
  body.mob-scrolled .chrome-top .sound { border: 1px solid rgba(232,236,242,0.16); }
}


/* ====== the programme, after the critique. Tony 2026-09-15: "speciellt programmet" ====== */
@media (max-width: 720px) {
  /* the first screen finally says what this is */
  .hero .hero-sub { margin: 14px 0 0; }

  /* the brand survives the scroll: below the fold there was no Shockholm on screen at all,
     for 2114 px of list */
  .mob-intro .mark { margin: 0 0 14px; }
  .mob-intro .mark img { height: 42px; width: auto; display: block;
    filter: drop-shadow(0 0 12px rgba(150,190,255,0.3)); }
  .mob-intro .when { margin: 0 0 20px; font-size: 12px; letter-spacing: 0.3em;
    text-transform: uppercase; color: #ffb060; }

  /* each place is a block, not another paragraph in one long list */
  .mob-place { position: relative; padding-top: 38px; padding-bottom: 8px; }
  .mob-place::before {
    content: ""; position: absolute; inset: 14px 0 0; pointer-events: none;
    background: radial-gradient(120% 60% at 12% 0%,
      color-mix(in srgb, var(--c) 11%, transparent), transparent 62%);
  }
  .mob-place > * { position: relative; }
  .mob-place h2 { font-size: 29px; }
  .mob-place .count { color: var(--c); opacity: 0.85; }

  /* a row a visitor can compare: price, age and fear now live on it */
  /* the desktop row hangs 12 px into the side panel's gutter; the page has no gutter,
     so every row sat 12 px left of every heading. Measured 2026-09-15. */
  .mob-place .rows .row { padding: 15px 0 16px; margin-left: 0; width: 100%; }
  .mob-place .row .meta {
    display: block; margin-top: 7px; font-size: 14.5px; color: var(--ink-dim);
  }
  .mob-place .row .meta i { font-style: normal; opacity: 0.4; margin: 0 7px; }
  .mob-place .row .meta .free {
    font-weight: 500; color: #8cff9a; letter-spacing: 0.04em;
  }

  /* an event with nothing announced looks like a promise, not like a broken row */
  .mob-place .row.tba .t { opacity: 0.72; }
  .mob-place .row.tba .w {
    /* the row is a grid, so an inline-block child still stretches the whole column */
    display: inline-block; width: fit-content; justify-self: start;
    margin-top: 2px; padding: 3px 9px; border-radius: 999px;
    font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--ink-faint); border: 1px solid rgba(232,236,242,0.18);
  }
  .mob-place .row.tba .go { opacity: 0.35; }

  /* and it must not wear a ticket button that leads nowhere */
  .panel.mob-tba a.solid, .panel.mob-tba .fine { display: none; }
  .panel.mob-tba .lvl::after {
    content: "Dates and tickets are announced in October.";
    display: block; margin-top: 22px; padding: 14px 16px; border-radius: 4px;
    background: rgba(255,255,255,0.04); border: 1px solid rgba(232,236,242,0.14);
    font-size: 16px; color: var(--ink-dim);
  }

  /* the card is not a second programme: the page above is the parent, and there is no
     place level on a phone for "Back to ..." to lead to */
  .panel .crumbs, .panel .back-up { display: none; }
  .panel-head { padding-top: 10px; justify-content: flex-end; }
  .panel-body { padding-top: 0; }

  /* a phone number you can actually hit */
  .mob-about a { display: inline-block; padding: 9px 0; }

  /* a tap must not leave a stuck hover behind */
  @media (hover: none) {
    .mob-place .row:hover { background: none; }
  }
}


/* ====== a very big moon, centred, with the wordmark under it. Tony 2026-09-15 ====== */
@media (max-width: 720px) {
  /* the moon is drawn by the shader at MOB.moonX / moonY / moonR; this puts the wordmark
     just under its lower edge instead of letting stepGlide place it from the sky height */
  .brand { top: 55svh; }
  .brand.logo img { height: clamp(54px, 18vw, 84px); }

  /* the date belongs directly under the wordmark, not floating near the bottom edge */
  .hero { top: 66svh; bottom: auto; }
  .mob-all { bottom: calc(var(--safe-b) + 44px); }
}


/* ====== the last pass: Tony's notes plus the measurement findings, 2026-09-15 ====== */
@media (max-width: 720px) {
  /* the date is part of the wordmark now, so it cannot drift */
  /* the block is now as wide as the date line, so the image needs to centre itself in it */
  .brand { white-space: normal; text-align: center; }
  .brand.logo img { margin: 0 auto; }
  .brand-date {
    display: block; margin-top: 14px; font-size: 15px; letter-spacing: 0.32em;
    text-transform: uppercase; color: #ffb060; white-space: nowrap;
  }
  .hero { top: calc(66svh + 18px); }

  /* measured 133.8 x 36; the floor is 44 */
  .chrome-top .sound { min-height: 44px; padding: 12px 14px 12px 12px; }

  /* the separator dot measured 2.30:1 */
  .mob-place .row .meta i { opacity: 0.75; }

  /* a focused row had a 1.24:1 indicator; WCAG wants 3:1 */
  .mob-place .row:focus-visible {
    outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 4px;
  }

  /* the card let 118 px of a DIFFERENT event stay legible above it */
  .panel {
    height: calc(100% - var(--safe-t) - 44px);
    background: #080c15; backdrop-filter: none;
  }
}


/* ====== the measurement pass, closed out. 2026-09-15 ====== */
@media (max-width: 720px) {
  /* Type scale. Thirteen sizes, eight of them within 2 px of a neighbour, is not a scale.
     Below 21 px the programme now uses four steps: 10, 12, 14, 15.5. */
  .mob-foot { font-size: 11.5px; }                        /* was 9 */
  .mob-place .count { font-size: 11.5px; }
  .mob-intro .when, .mob-place .row.tba .w { font-size: 11.5px; }   /* was 10.5 */
  .mob-place .row .cat, .mob-about .fine { font-size: 15.5px; }
  .mob-place .row .meta { font-size: 15.5px; }            /* was 12.5 */
  .mob-place .row .w { font-size: 16px; }               /* was 13.5 */
  .mob-about p, .mob-intro .lede { font-size: 19px; line-height: 1.6; }

  /* Rhythm. Thirteen distinct gaps, with 46/63 and 30/32 and 38/40 doing the same job.
     Four steps: 8, 16, 32, 56. */
  .mob-intro { padding-bottom: 16px; }
  .mob-intro h1 { margin-bottom: 0; }
  .mob-intro .lede { margin-top: 16px; }
  .mob-place { padding-top: 56px; padding-bottom: 0; }
  .mob-place .count { margin: 8px 0 16px 0; }           /* was 19 px in, a third left edge */
  .mob-about { padding-top: 56px; margin-top: 0; }
  .mob-about h2.second { margin-top: 32px; }
  .mob-foot { padding-top: 32px; }

  /* "About Shockholm" at 25/400 read as another event title at 24/300. It is a section
     heading, so it gets the section size. */
  .mob-about h2 { font-size: 29px; }

  /* the row divider measured 1.22:1 against the ground */
  .mob-place .rows .row { border-bottom-color: rgba(232, 236, 242, 0.18); }
  .mob-about { border-top-color: rgba(232, 236, 242, 0.18); }

  /* the only centred block in a left-aligned column */
  .mob-about .fine { text-align: left; }
}


/* A second bats canvas, above the wordmark, so some of them cross in front of SHOCKHOLM
   instead of always passing behind it. Built by main.js, desktop only. Tony 2026-09-16. */
#bats-front {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 23; opacity: 0.74; filter: blur(0.55px);
}
@media (max-width: 720px) { #bats-front { display: none !important; } }

/* ---------- Type option C (Tony 2026-10-16): Big Shoulders Display for titles, Big Shoulders Text for text.
   Titles are set in caps like the shockholm.com logo. Phone and e-mail links keep their case. ---------- */
body { font-weight: 500; }
.ghost .name, .ghost .card-place, .hero-title, .summary-title, .details h2, .prog .t, .portal-caption .pc-title,
.lvl h2, .row .t, .place-group h3, .back-up .tx, .mob-handle-place, .mob-menu-row .t, .mob-intro h1, .mob-about h2 {
  text-transform: uppercase; font-weight: 700; letter-spacing: 0.02em; }
.hero-title { font-weight: 800; font-size: clamp(44px, 5.2vw, 76px); line-height: 0.95; letter-spacing: 0.005em; }
.lvl h2, .details h2 { font-size: 40px; line-height: 1; }
.row .t { font-size: 25px; line-height: 1.05; }
.ghost .name { font-size: 23px; letter-spacing: 0.06em; }
.details.info .facts a, .mob-about a { font-family: var(--sans); font-weight: 600; }
@media (min-width: 900px) { .hero-sub { max-width: 560px; } }   /* caps run wider: keep the headline on one line */

/* 2026-10-16 (Tony: "lite svårt att läsa"): text in Barlow, wider than Big Shoulders Text.
   Small caps labels were spaced 0.2-0.3em, which breaks words apart in a narrow face; tightened. */
body { font-weight: 400; }
.nav, .crumbs, .row .cat, .textlink, .sound, .hero-sub, .portal-caption .pc-when, .summary-label,
.details-label, .ghost .card-sub, .ghost .card-cta, .outline, .ghostbtn, .prog .cat { letter-spacing: 0.1em; font-weight: 500; }
.row .w, .lvl .count, .details-short, .details-long { font-weight: 400; }
/* 2026-10-16 phone check (Tony): the line under the wordmark sat left while everything else is centred
   (its 480 px max-width box hugged the left edge), and the new .com logo looked small under the big moon. */
@media (max-width: 720px) {
  .hero .hero-sub { max-width: 34ch; margin-left: auto; margin-right: auto; text-align: center; }
  .hero, .hero-title { text-align: center; }
  .brand.logo img { height: clamp(70px, 24vw, 108px); }
  .hero .hero-sub { max-width: 30ch; text-wrap: balance; }
  .mob-place h2 { text-transform: uppercase; font-weight: 700; letter-spacing: 0.02em; }   /* same caps as PROGRAM above it */
  /* Tony 2026-10-16: more air around the small logo at the top of the programme */
  .mob-intro { padding-top: calc(var(--safe-t) + 104px); }
  .mob-intro .mark { margin: 0 0 30px; }
  .mob-intro .mark img { height: 50px; }
  .mob-intro .when { margin-bottom: 26px; }
}
/* 2026-10-16 (Tony): arrow keys step through the events of a chosen place; that turned on the keyboard ring
   around the ghost that is already chosen and lit. The chosen ghost needs no ring. */
body.kbd .ghost.selected:focus-visible { outline: none; }
/* ---------- Lights for other organizers' places (Tony 2026-10-16, option B, second pass) ----------
   First pass used warm points: Tony could not find them, they read as street lamps. Now: one cold white
   colour for every "other organizer" place (a category, not a per-place accent), a solid core, a ring
   that ripples outward (city lamps never do that) and the name on a dark label so it reads on any roof. */
.ghost.wisp { width: 34px; height: 34px; margin: -17px 0 0 -17px; filter: none; --w: #f4f6ff; }
.ghost.wisp svg { display: none; }
.ghost.wisp .light { position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%;
  background: var(--w); opacity: 1; transform: none;
  box-shadow: 0 0 0 3px rgba(5,8,16,.55), 0 0 10px 4px rgba(244,246,255,.75), 0 0 28px 10px rgba(200,215,255,.35); }
.ghost.wisp::after { content: ""; position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%; border: 2px solid var(--w); pointer-events: none; }
.ghost.wisp .name { top: 30px; font-size: 15px; letter-spacing: .06em; opacity: 1; color: #fff;
  background: rgba(5,8,16,.78); padding: 3px 8px 2px; border-radius: 3px; text-shadow: none;
  border: 1px solid rgba(244,246,255,.22); }
.ghost.wisp:hover .light, .ghost.wisp.selected .light, .ghost.wisp.lit .light { transform: scale(1.35); }
.ghost.wisp.dim { opacity: .45; }
/* Tony 2026-10-16: "även texten är svår att se". The map labels are scaled down with the overview zoom
   (x0.75 at the widest view), so they need a larger base. Light labels in Barlow, which holds up small. */
.ghost .name { font-size: 28px; }
.ghost.wisp .name { font-family: var(--sans); font-weight: 600; font-size: 19px; letter-spacing: .04em; text-transform: none; padding: 4px 10px 3px; }
/* Tony 2026-10-16: "svart ruta bakom är fult". No box: the same soft dark halo the ghost names use. */
.ghost.wisp .name { background: none; border: 0; padding: 0; font-weight: 600; font-size: 19px;
  text-shadow: 0 0 3px rgba(0,0,0,1), 0 0 8px rgba(0,0,0,.95), 0 2px 16px rgba(0,0,0,.9), 0 0 28px rgba(0,0,0,.7); }
/* Tony 2026-10-16: "har du använt annat typsnitt?" Yes, Barlow; one map, one voice. Light names now use the
   ghost names' face (Big Shoulders Display, caps). Hierarchy by size only: ghosts 28 px, lights 20 px. */
.ghost.wisp .name { font-family: var(--serif); font-weight: 700; font-size: 20px; text-transform: uppercase; letter-spacing: .06em; }
/* ---------- Programme, grouped for fast browsing (Tony 2026-10-16: "grupper upp det snyggt") ---------- */
.jump { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 26px; }
.jump button { font: 600 13px/1 var(--sans); letter-spacing: .04em; color: var(--ink); padding: 8px 12px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--pc) 55%, transparent); background: color-mix(in srgb, var(--pc) 10%, transparent);
  transition: background .2s, border-color .2s; }
.jump button:hover { background: color-mix(in srgb, var(--pc) 24%, transparent); border-color: var(--pc); }
.jump button.other { border-style: dashed; }
.part { font: 600 13px var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-dim);
  margin: 30px 0 14px; display: flex; align-items: center; gap: 12px; }
.part::after { content: ""; flex: 1; height: 1px; background: rgba(232,236,242,.16); }
.lvl > .part:first-of-type { margin-top: 0; }
.place-group { margin: 0 0 22px; scroll-margin-top: 8px; }
.place-group h3 { position: sticky; top: -8px; z-index: 2; margin: 0; padding: 12px 0 10px; justify-content: space-between;
  background: linear-gradient(to bottom, rgba(9,13,22,.98) 78%, rgba(9,13,22,0)); font-size: 26px; color: var(--pc);
  border-bottom: 2px solid color-mix(in srgb, var(--pc) 45%, transparent); }
.place-group h3 button:hover { color: var(--ink); }
.place-group h3 .n { font: 600 13px var(--sans); color: var(--ink-dim); letter-spacing: .06em; }
.place-group .dot { width: 9px; height: 9px; }
.place-group.other .dot { background: transparent; border: 2px solid var(--pc); box-shadow: 0 0 8px var(--pc); }
.place-group .row { padding-top: 13px; padding-bottom: 13px; }
.place-group .row .w { color: var(--ink); font-weight: 500; }   /* the date is what people scan for */
.place-group .row .cat { font-size: 12px; opacity: .85; }
/* phone: the same two parts */
.mob-part { font: 600 13px var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-dim);
  margin: 34px 22px 6px; display: flex; align-items: center; gap: 12px; }
.mob-part::after { content: ""; flex: 1; height: 1px; background: rgba(232,236,242,.16); }
.mob-place.other .dot { background: transparent; border: 2px solid var(--c); }
.place-group h3 button { text-transform: inherit; letter-spacing: inherit; }
.place-group h3 .n { font-size: 14px; }

/* Tony 2026-10-16: "glöd noderna är lite generiska, bör vara mer organiska". A will-o'-wisp instead of a dot
   and a ring: a soft blob that changes shape, flickers like a flame and breathes a wide uneven halo.
   Cold, slightly ghost-green white, so it still never reads as a warm street lamp. Each wisp runs on its
   own phase (the ghosts' --sway-delay / --blink-delay are set per marker in buildGhosts). */
.ghost.wisp { --w: #eafff4; }
.ghost.wisp::after { content: ""; width: 44px; height: 38px; margin: -21px 0 0 -22px; border: 0; border-radius: 48% 52% 44% 56% / 55% 45% 55% 45%;
  background: radial-gradient(closest-side, rgba(200,255,225,.42), rgba(160,230,255,.16) 55%, transparent);
  filter: blur(5px); animation: wispHalo 5.3s ease-in-out infinite; animation-delay: var(--sway-delay, 0s); }
.ghost.wisp .light { width: 11px; height: 14px; margin: -8px 0 0 -5.5px; background: radial-gradient(circle at 50% 62%, #fff 0 30%, var(--w) 52%, rgba(190,255,225,.0) 100%);
  border-radius: 50% 50% 46% 54% / 62% 62% 38% 38%;
  box-shadow: 0 0 6px 2px rgba(225,255,240,.85), 0 0 18px 6px rgba(170,240,215,.38);
  animation: wispFlame 2.9s ease-in-out infinite, wispFlicker 4.1s steps(1, end) infinite; animation-delay: var(--blink-delay, 0s); }
@keyframes wispFlame {
  0%   { border-radius: 50% 50% 46% 54% / 62% 62% 38% 38%; transform: translate(0, 0) rotate(-3deg) scale(1); }
  33%  { border-radius: 44% 56% 52% 48% / 66% 58% 42% 34%; transform: translate(1px, -2px) rotate(4deg) scale(1.08, .96); }
  66%  { border-radius: 55% 45% 50% 50% / 58% 66% 34% 42%; transform: translate(-1px, -1px) rotate(-2deg) scale(.95, 1.07); }
  100% { border-radius: 50% 50% 46% 54% / 62% 62% 38% 38%; transform: translate(0, 0) rotate(-3deg) scale(1); } }
@keyframes wispFlicker { 0%, 100% { opacity: 1 } 12% { opacity: .82 } 14% { opacity: 1 } 47% { opacity: .9 } 49% { opacity: 1 } 78% { opacity: .76 } 80% { opacity: 1 } }
@keyframes wispHalo {
  0%, 100% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: .75; }
  30% { transform: translate(2px, -3px) scale(1.25, 1.1) rotate(12deg); opacity: 1; }
  60% { transform: translate(-2px, -1px) scale(1.05, 1.3) rotate(-8deg); opacity: .6; }
  80% { transform: translate(1px, -4px) scale(1.35, 1.15) rotate(5deg); opacity: .9; } }
.ghost.wisp:hover .light, .ghost.wisp.selected .light, .ghost.wisp.lit .light { filter: brightness(1.3); }
.ghost.wisp:hover::after, .ghost.wisp.lit::after { animation-duration: 2.6s; }
@media (prefers-reduced-motion: reduce) { .ghost.wisp .light, .ghost.wisp::after { animation: none; } }
/* The panel is see-through, so a sticky header needed its own fill and read as a box. Not sticky. */
.place-group h3 { position: static; background: none; padding: 14px 0 10px; }
/* Tony 2026-10-16: more vertical air between the wisp and its name */
.ghost.wisp .name { top: 42px; }
/* Tony 2026-10-16: the category over each event takes its place's colour (Sergels Torg gold, Gamla Stan blue,
   Globen violet ...), so a group reads as one block. Weight 600 keeps the small gold and violet legible. */
.place-group { --c: var(--pc); }
.place-group .row .cat { color: var(--pc); font-weight: 600; font-size: 12.5px; opacity: 1; }
/* Light name sides, chosen by placeWispLabels() so names never sit on a ghost or another name (Tony 2026-10-16) */
.ghost.wisp[data-lbl="r"] .name { top: 50%; left: calc(100% + 10px); transform: translateY(-50%); }
.ghost.wisp[data-lbl="l"] .name { top: 50%; left: auto; right: calc(100% + 10px); transform: translateY(-50%); }
.ghost.wisp[data-lbl="t"] .name { top: auto; bottom: calc(100% + 14px); transform: translateX(-50%); }
.ghost.wisp.lbl-hidden .name { opacity: 0; } .ghost.wisp.lbl-hidden:hover .name, .ghost.wisp.lbl-hidden.lit .name, .ghost.wisp.lbl-hidden.selected .name { opacity: 1; }

/* impeccable audit 2026-10-16: touch targets, screen-reader text, one hidden-image fix */
.panel-close { width: 44px; height: 44px; }
.jump button { min-height: 36px; padding: 9px 14px; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* critique 2026-10-16: price and age on desktop rows too */
.row .meta { display: block; grid-column: 1; margin-top: 4px; font-size: 14px; color: var(--ink-dim); }
.row .meta i { font-style: normal; opacity: .45; margin: 0 7px; }
.row .meta .free { font-weight: 600; color: #8cff9a; letter-spacing: .04em; }
/* audit B: the Esc hint is a sentence, so it is not set in caps */
.esc-hint { text-transform: none; letter-spacing: .02em; }
.row .go { grid-row: 1 / span 4; }
/* Tony 2026-10-16: phone first screen, better spacing. The date sat glued to the logo, the line tight under it,
   then a large empty band down to Program. Logo, date and line now breathe evenly and sit a little lower. */
@media (max-width: 720px) {
  .brand { top: 57svh; }
  .brand-date { margin-top: 22px; }
  .hero { top: calc(70svh + 30px); }
  .hero .hero-sub { line-height: 1.6; }
}
/* short phones (iPhone SE and similar): the same rhythm, measured from the logo instead of the screen height */
@media (max-width: 720px) and (max-height: 740px) {
  .brand { top: 51svh; }
  .brand.logo img { height: 62px; }
  .brand-date { margin-top: 16px; font-size: 14px; }
  .hero { top: calc(51svh + 62px + 16px + 22px + 4px); }
  .hero .hero-sub { font-size: 17px; }
}
/* programme filters (Tony 2026-10-16) */
.filters { display: grid; gap: 8px; margin: 6px 0 18px; }
.frow { display: flex; flex-wrap: wrap; gap: 8px; }
.fchip { font: 600 13.5px/1 var(--sans); letter-spacing: .03em; color: var(--ink); min-height: 38px; padding: 10px 14px;
  border-radius: 999px; border: 1px solid rgba(232,236,242,.28); background: rgba(232,236,242,.04); transition: background .2s, border-color .2s, color .2s; }
.fchip:hover { border-color: rgba(232,236,242,.6); }
.fchip[aria-pressed="true"] { background: #ffb060; border-color: #ffb060; color: #0a0d14; }
.fempty { margin: 8px 0 20px; color: var(--ink-dim); font-size: 15px; }
.ghost.filtered { opacity: .16; }
.ghost.filtered:hover { opacity: .6; }
@media (max-width: 720px) { .mob-intro .filters { margin: 22px 0 6px; } .fchip { min-height: 42px; font-size: 14.5px; } }
/* Tony 2026-10-16: readable line breaks. Titles balance their lines; running text avoids a lone last word. */
.row .t, .lvl h2, .place-group h3, .details h2, .mob-place h2, .portal-caption .pc-title, .hero-title { text-wrap: balance; }
.row .w, .row .meta, .ev-short, .details-short, .details-long, .facts dd, .mob-intro .lede, .about-lines p, #a-lines p, .portal-caption .pc-when { text-wrap: pretty; }
.row .w { line-height: 1.45; }
/* ---------- filters v2 (Tony 2026-10-16) ---------- */
.filters { gap: 12px; margin: 10px 0 12px; }
.fgroup { display: grid; grid-template-columns: 52px 1fr; align-items: start; gap: 10px; }
.flabel { margin: 11px 0 0; font: 600 12px var(--sans); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim); }
.fchip .fn { font-weight: 500; opacity: .6; margin-left: 2px; }
.fchip[aria-pressed="true"] .fn { opacity: .8; }
.fchip:disabled { opacity: .32; cursor: default; }
.fchip:disabled:hover { border-color: rgba(232,236,242,.28); }
.fsum { display: flex; align-items: center; gap: 14px; margin: 0 0 18px; font: 500 14.5px var(--sans); color: var(--ink); }
.fsum[hidden] { display: none; }
.fclear { font: 600 13.5px var(--sans); color: #ffb060; text-decoration: underline; text-underline-offset: 3px; padding: 8px 0; }
/* the map follows the filters: a small count on each place that still has something */
.ghost .mbadge { position: absolute; left: calc(50% + 18px); top: 2px; min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px;
  background: #ffb060; color: #0a0d14; font: 700 13px/22px var(--sans); text-align: center; pointer-events: none; }
.ghost.wisp .mbadge { left: calc(50% + 10px); top: -12px; }
.ghost .mbadge[hidden] { display: none; }
/* phone: one Filter button that sticks while the list scrolls, chips in a sheet */
.mfbar { position: sticky; top: calc(var(--safe-t) + 8px); z-index: 5; display: flex; align-items: center; gap: 12px; margin: 22px 0 4px; }
.mfbtn { font: 600 15px var(--sans); color: #0a0d14; background: #ffb060; border-radius: 999px; padding: 12px 20px; min-height: 46px; box-shadow: 0 6px 18px rgba(0,0,0,.45); }
.mfsum { font: 500 14px var(--sans); color: var(--ink-dim); }
.mfsheet { position: fixed; inset: 0; z-index: 80; }
.mfsheet[hidden] { display: none; }
.mfveil { position: absolute; inset: 0; background: rgba(3,5,10,.6); }
.mfpanel { position: absolute; left: 0; right: 0; bottom: 0; background: #0b0f19; border-radius: 22px 22px 0 0; padding: 18px 20px calc(18px + var(--safe-b));
  border-top: 1px solid rgba(232,236,242,.12); max-height: 85svh; overflow: auto; }
.mfhead { display: flex; justify-content: space-between; align-items: center; }
.mftitle { margin: 0; font: 700 26px var(--serif); text-transform: uppercase; color: var(--ink); }
.mfpanel .fsum, .mfpanel .fempty { display: none; }
.mfpanel .fgroup { grid-template-columns: 1fr; gap: 6px; }
.mfpanel .flabel { margin: 6px 0 0; }
.mfshow { display: block; width: 100%; margin-top: 18px; min-height: 52px; border-radius: 14px; background: #ffb060; color: #0a0d14; font: 700 16px var(--sans); }
body.mf-open { overflow: hidden; }
/* event card navigation (Tony 2026-10-16): obvious Back, and stepping through the events of the same place */
.evnav { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 20px; }
.evback { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 10px 18px; border-radius: 999px;
  font: 700 15px var(--sans); color: #0a0d14; background: var(--ink); transition: transform .2s var(--ease-out), background .2s; }
.evback:hover { transform: translateX(-3px); background: #fff; }
.evstep { display: flex; align-items: center; gap: 6px; font: 500 13.5px var(--sans); color: var(--ink-dim); }
.evstep button { width: 40px; height: 40px; border-radius: 50%; border: 1px solid rgba(232,236,242,.3); color: var(--ink); font-size: 22px; line-height: 1; }
.evstep button:hover { border-color: var(--ink); background: rgba(232,236,242,.08); }
.evpager { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 28px; padding-top: 18px; border-top: 1px solid rgba(232,236,242,.12); }
.evpager button { text-align: left; padding: 12px 14px; border-radius: 10px; border: 1px solid rgba(232,236,242,.16); background: rgba(232,236,242,.03); min-height: 64px; }
.evpager button + button { text-align: right; }
.evpager button:hover { border-color: rgba(232,236,242,.45); background: rgba(232,236,242,.07); }
.evpager small { display: block; font: 600 12px var(--sans); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-dim); margin-bottom: 4px; }
.evpager b { display: block; font: 700 17px/1.15 var(--serif); text-transform: uppercase; color: var(--ink); text-wrap: balance; }
@media (max-width: 720px) {
  .mfbar { margin: 4px 22px 0; padding: 8px 0; }
  body.mob-scrolled .mfbar { background: linear-gradient(to bottom, #050810 70%, rgba(5,8,16,0)); }
}
@media (max-width: 720px) { .mob-place { scroll-margin-top: 78px; } }   /* lands below the sticky Filter bar */
/* Tony 2026-10-16 test: light (non-ghost) place names in a lighter weight, so the ghosts lead */
.ghost.wisp .name { font-weight: 500; letter-spacing: .07em; }
/* filter note in a place panel: the visitor always sees that a filter is on (Tony 2026-10-16) */
.fnote { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin: 4px 0 14px; padding: 10px 14px; border-radius: 10px;
  background: rgba(255,176,96,.1); border: 1px solid rgba(255,176,96,.35); font: 500 14px/1.4 var(--sans); color: var(--ink); }
/* Tony 2026-10-16 test: light (non-ghost) names in the same voice as the hero sub line: Barlow 500 caps, tracked */
.ghost.wisp .name { font-family: var(--sans); font-weight: 500; font-size: 15px; letter-spacing: .12em; text-transform: uppercase; }
/* edge tab: the programme drawer's visible handle on the right (Tony 2026-10-16) */
.edgetab { position: fixed; right: 0; top: 50%; transform: translateY(-50%); z-index: 22; display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 16px 10px 14px; border-radius: 12px 0 0 12px; background: rgba(9,13,22,.82); border: 1px solid rgba(232,236,242,.22); border-right: 0;
  backdrop-filter: blur(6px); color: var(--ink); transition: padding .25s var(--ease-out), background .25s, opacity .3s; }
.edgetab:hover { padding-right: 16px; background: rgba(14,19,30,.92); }
.edgetab .et-ar { font-size: 24px; line-height: 1; color: #ffb060; }
.edgetab .et-tx { writing-mode: vertical-rl; transform: rotate(180deg); font: 700 15px var(--serif); letter-spacing: .14em; text-transform: uppercase; }
.edgetab .et-n { min-width: 26px; height: 26px; border-radius: 13px; background: #ffb060; color: #0a0d14; font: 700 13px/26px var(--sans); text-align: center; }
body:has(#panel.open) .edgetab { opacity: 0; pointer-events: none; }
@media (max-width: 720px) { .edgetab { display: none; } }
.edgetab { padding: 22px 14px 18px; gap: 14px; } .edgetab .et-ar { font-size: 30px; } .edgetab .et-tx { font-size: 19px; } .edgetab .et-n { min-width: 30px; height: 30px; font-size: 14px; line-height: 30px; border-radius: 15px; }
/* audit 2026-10-16: a chosen place is never faded, even if nothing there matches the filter */
.ghost.selected.filtered { opacity: 1; }
/* Tony 2026-10-16 test: menu on the left, the logo gets the centre of the sky to itself (desktop only) */
@media (min-width: 721px) {
  .chrome-top { justify-content: flex-start; }
  .chrome-top .nav { position: absolute; left: 34px; top: 26px; gap: 28px; }
}
/* Tony 2026-10-16: map names in the voice of the date line under the logo (Big Shoulders 500, wide tracking).
   Ghosts larger, lights smaller; size and brightness carry the hierarchy, not a second typeface. */
.ghost.wisp .name { font-family: var(--serif); font-weight: 500; font-size: 16px; letter-spacing: .24em; color: rgba(255,255,255,.88); }
.ghost.wisp .name { font-weight: 600; font-size: 18.5px; letter-spacing: .18em; color: #fff;
  text-shadow: 0 0 2px rgba(0,0,0,1), 0 0 6px rgba(0,0,0,1), 0 2px 14px rgba(0,0,0,.95), 0 0 26px rgba(0,0,0,.8); }
/* map filters on the left (Tony 2026-10-16), desktop only */
#mapfilters { position: fixed; left: 24px; top: 50%; transform: translateY(-50%); z-index: 20; width: 300px; padding: 18px 18px 16px;
  border-radius: 16px; background: rgba(7,10,18,.78); border: 1px solid rgba(232,236,242,.14); backdrop-filter: blur(8px);
  box-shadow: 0 12px 36px rgba(0,0,0,.45); transition: opacity .35s var(--ease-out); }
#mapfilters .mf-title { margin: 0 0 4px; font: 600 15px var(--serif); letter-spacing: .2em; text-transform: uppercase; color: #ffb060; }
#mapfilters .filters { margin: 8px 0 6px; }
#mapfilters .fgroup { grid-template-columns: 1fr; gap: 6px; }
#mapfilters .flabel { margin: 4px 0 0; }
#mapfilters .fchip { min-height: 34px; padding: 8px 12px; font-size: 13px; }
#mapfilters .fempty { margin: 6px 0 0; font-size: 13.5px; }
#mapfilters .fsum { margin: 6px 0 4px; font-size: 13.5px; }
#mapfilters .mf-open { margin-top: 8px; font: 600 14px var(--sans); color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
body:has(.hero.away) #mapfilters { opacity: .92; }
@media (max-width: 720px) { #mapfilters { display: none; } }
@media (max-height: 760px) { #mapfilters { top: 64px; transform: none; } }
@media (min-width: 721px) {
  #mapfilters { width: clamp(300px, 17vw, 440px); padding: clamp(18px, 1.2vw, 26px); left: clamp(20px, 1.6vw, 40px); }
  #mapfilters .mf-title { font-size: clamp(15px, .9vw, 20px); }
  #mapfilters .fchip { font-size: clamp(13px, .78vw, 17px); min-height: clamp(34px, 2.2vw, 46px); padding: 8px clamp(12px, .8vw, 18px); }
  #mapfilters .flabel, #mapfilters .fsum, #mapfilters .mf-open { font-size: clamp(12.5px, .72vw, 16px); }
}
/* Tony 2026-10-16: only the lights' names take the yellow date-line voice; the ghosts keep their own names */
.ghost.wisp .name { font-family: var(--serif); font-weight: 600; font-size: 18.5px; letter-spacing: .26em; color: #ffb060; }
/* Tony 2026-10-16: keep the yellow look, make it easier to read: heavier, a touch larger, brighter, darker halo */
.ghost.wisp .name { font-weight: 700; font-size: 20px; letter-spacing: .2em; color: #ffc27a;
  text-shadow: 0 0 2px #000, 0 0 5px rgba(0,0,0,1), 0 1px 10px rgba(0,0,0,1), 0 0 22px rgba(0,0,0,.9); }
/* filter card fold (Tony 2026-10-16) */
#mapfilters .mf-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
#mapfilters .mf-head .mf-title { margin: 0; }
#mapfilters .mf-min { width: 32px; height: 32px; border-radius: 50%; border: 1px solid rgba(232,236,242,.28); color: var(--ink); font-size: 20px; line-height: 1; }
#mapfilters .mf-min:hover { border-color: var(--ink); }
#mapfilters .mf-pill { display: none; }
#mapfilters.min { width: auto; padding: 0; background: none; border: 0; box-shadow: none; backdrop-filter: none; }
#mapfilters.min .mf-head, #mapfilters.min .mf-body { display: none; }
#mapfilters.min .mf-pill { display: inline-flex; align-items: center; gap: 2px; min-height: 44px; padding: 10px 20px; border-radius: 999px;
  background: #ffb060; color: #0a0d14; font: 700 15px var(--sans); box-shadow: 0 6px 18px rgba(0,0,0,.45); }
/* Tony 2026-10-16 test: yellow light names one weight lighter (700 -> 600) */
.ghost.wisp .name { font-weight: 600; }
/* Tony 2026-10-16: light (non-ghost) names in EXACTLY the hero sub line's type: Barlow 500, caps, 0.1em, #ffb060 at 90 % */
.ghost.wisp .name { font-family: var(--sans); font-weight: 500; font-size: 14.5px; letter-spacing: .1em; text-transform: uppercase;
  color: #ffb060; opacity: .9; }
.ghost.wisp:hover .name, .ghost.wisp.lit .name, .ghost.wisp.selected .name { opacity: 1; }
/* Tony 2026-10-16: the folded Filter pill sat on top of the left map arrow (both mid-left). The filters now live
   top-left, right under the menu, so the arrow keeps the middle of the edge. */
@media (min-width: 721px) {
  #mapfilters { top: 76px; transform: none; max-height: calc(100vh - 96px); overflow: auto; }
}
/* Filter as a menu item (Tony 2026-10-16): no pill under the menu; the card opens with room below the menu */
.nav-filter .mfbtn-n { color: #ffb060; }
.nav-filter[aria-expanded="true"] { color: var(--ink); }
@media (min-width: 721px) {
  #mapfilters.min { display: none; }
  #mapfilters { top: 104px; }
}
@media (max-width: 720px) { .nav-filter { display: none; } }
/* filters as a pull-out from the left edge, mirroring the PROGRAM tab on the right (Tony 2026-10-16) */
.edgetab-left { right: auto; left: 0; top: 34%; border-radius: 0 12px 12px 0; border: 1px solid rgba(232,236,242,.22); border-left: 0; }
.edgetab-left:hover { padding-right: 14px; padding-left: 16px; }
.edgetab-left .et-tx { transform: none; }
.edgetab-left .et-n:empty { display: none; }
@media (min-width: 721px) {
  #mapfilters { top: 34%; left: 0; transform: translateY(-20%); border-radius: 0 16px 16px 0; border-left: 0;
    transition: transform .45s var(--ease-out), opacity .3s; max-height: calc(100vh - 120px); }
  #mapfilters.min { display: block; transform: translate(-105%, -20%); opacity: 0; pointer-events: none; }
  body:has(#mapfilters:not(.min)) .edgetab-left { opacity: 0; pointer-events: none; }
}
@media (max-width: 720px) { .edgetab-left { display: none; } }
/* the left filter tab stays while the programme panel is open (the hide rule is for the right PROGRAM tab only) */
body:has(#panel.open) .edgetab-left { opacity: 1; pointer-events: auto; }
body:has(#panel.open):has(#mapfilters:not(.min)) .edgetab-left { opacity: 0; pointer-events: none; }
/* Tony 2026-10-16: ghost names one step smaller (28 -> 25 px) */
.ghost:not(.wisp) .name { font-size: 25px; }
.ghost:not(.wisp) .name { font-size: 22px; }   /* Tony 2026-10-16: one more step smaller (25 -> 22 px) */
.ghost:hover, body.kbd .ghost:focus-visible { z-index: 5; }   /* Tony 2026-10-16: the hovered card sat under the Nordiska museet name; the hovered place now paints above the others */
/* Tony 2026-10-16: area chips straight in the phone's sticky filter bar, one tap toggles, scrolls sideways */
.mfbar { flex-direction: column; align-items: stretch; gap: 10px; }
.mfrow { display: flex; align-items: center; gap: 12px; }
.mfquick { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; margin: 0 -22px; padding: 0 22px 2px; scroll-padding: 0 22px; }
.mfquick::-webkit-scrollbar { display: none; }
.mfquick .fchip { flex: none; white-space: nowrap; min-height: 38px; padding: 8px 14px; font-size: 14px; background: rgba(5,8,16,.72); }
.mfquick .fchip[aria-pressed="true"] { background: #ffb060; }
/* Burning Heart — Tony's flame-frame sequence (from shockholm-web) */
.ghost.heart {
  filter:
    drop-shadow(0 0 7px rgba(255, 120, 40, 0.65))
    drop-shadow(0 0 18px rgba(255, 60, 20, 0.35));
}
.ghost.heart .heart-mark {
  position: absolute; inset: 0; display: block; overflow: visible; pointer-events: none;
  animation: hover var(--bob, 3.4s) ease-in-out infinite;
  transform-origin: 50% 100%; scale: var(--gscale, 1);
  transition: translate 0.45s var(--ease-out), rotate 0.45s var(--ease-out), filter 0.35s var(--ease);
}
.ghost.heart .heart-frame {
  position: absolute; inset: -6% -4% 0; width: 108%; height: 106%;
  object-fit: contain; opacity: 0; pointer-events: none;
}
.ghost.heart .heart-frame.on { opacity: 1; }
.ghost.heart:hover .heart-mark,
.ghost.heart.selected .heart-mark,
.ghost.heart:focus-visible .heart-mark {
  filter: brightness(1.15); rotate: var(--tilt, -4deg); translate: 0 -6px;
}
.ghost.heart:hover .light,
.ghost.heart.selected .light { opacity: 1; transform: scale(1.35); }
@media (prefers-reduced-motion: reduce) {
  .ghost.heart .heart-mark { animation: none !important; }
}
/* Tony 2026-10-16: ghost names move to a free side when the place below is taken (see placeWispLabels) */
.ghost:not(.wisp)[data-lbl="r"] .name { top: 55%; left: calc(100% + 2px); transform: translateY(-50%); }
.ghost:not(.wisp)[data-lbl="l"] .name { top: 55%; left: auto; right: calc(100% + 2px); transform: translateY(-50%); }
.ghost:not(.wisp)[data-lbl="t"] .name { top: auto; bottom: calc(100% + 4px); }
.ghost:not(.wisp) .name { font-size: 19.4px; }   /* Tony 2026-10-16: ghost and heart names 12 % smaller (22 -> 19.4 px) */
/* Tony 2026-10-16: the ghosts and the heart 12 % smaller too (they shrink toward their feet, names stay put) */
.ghost:not(.wisp) svg { scale: calc(var(--gscale, 1) * 0.88); }
.ghost.heart .heart-mark { scale: calc(var(--gscale, 1) * 0.99); }
.ghost.wisp .name { font-size: 12.8px; }   /* Tony 2026-10-16: light names one step smaller (14.5 -> 12.8 px) */

/* v2 2026-10-19: logo position */
@media (min-width: 721px) {
  .brand.logo { left: var(--brand-x, 50%); transform: translateX(-50%); }
  .brand.logo { opacity: var(--brand-fit, 1); transition: opacity 0.3s; }
  .sound.compact .sound-label { display: none; }   /* panel open: dot only, the title tells what it is */   /* one size, one place: Stefan 2026-10-19 */
}

/* v2 2026-10-19 (Tony: "för aggressivt, gör det väldigt subtilt"): choosing a place dims the other places only slightly.
   A place the filter hides keeps its strong fade, so the filter still reads. */
.ghost.dim { opacity: 0.82; transition: opacity 0.6s ease; }
.ghost.wisp.dim { opacity: 0.8; }
.ghost.dim.filtered { opacity: 0.16; }
.ghost.dim.filtered:hover { opacity: 0.6; }
/* With the others barely dimmed they showed through the event caption. The chosen place's circle and caption sit
   above the other places, and the chosen ghost itself stays on top of its circle. */
.portal { z-index: 3; }
.ghost.selected { z-index: 4; }

/* ---------- 2026-10-19 (Bill, meeting with Tony): the main acts, the grouped costume contest, Fox in a Box ---------- */
/* Start screen (desktop): the three main acts as a compact row beside Hector and Portal, in the countdown's
   type and the date line's orange. The row hangs down like the orbs, so the headline above does not move. */
.mainacts { flex: none; align-self: flex-start; height: var(--orb-size); display: flex; flex-direction: column; justify-content: flex-end;
  gap: 7px; margin-left: 10px; pointer-events: auto; }
.mainacts-label { margin: 0; font: 500 12.5px/1 var(--sans); letter-spacing: .18em; text-transform: uppercase; color: #ffb060; opacity: .9; }
.mainacts-label i, .ma-head i { font-style: normal; opacity: .6; }
.mainacts-row { display: flex; gap: 8px; }
.ma { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 112px; padding: 8px 14px 10px; text-align: left;
  border: 1px solid rgba(232,236,242,.28); border-radius: 2px; background: rgba(5,8,16,.42); backdrop-filter: blur(6px);
  transition: border-color .3s, background .3s, transform .16s var(--ease-out); }
.ma:hover, .ma:focus-visible { border-color: color-mix(in srgb, var(--c) 75%, white); background: rgba(5,8,16,.62); outline: none; }
.ma:focus-visible { box-shadow: 0 0 0 2px #ffb060; }
.ma:active { transform: scale(.97); }
.ma-time { display: flex; align-items: baseline; gap: 5px; font: 700 30px/1 var(--serif); letter-spacing: .04em; color: #ffb060; }
.ma-time small { font: 500 11px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-dim); }
.ma-name { font: 500 12.5px/1.1 var(--sans); letter-spacing: .14em; text-transform: uppercase; color: var(--ink); white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .ma { transition: none; } .ma:active { transform: none; } }
@media (max-width: 720px) { .mainacts { display: none; } }

/* Programme panel and phone page: the same three come first, before the place-by-place list */
.mainacts-block { margin: 18px 0 28px; --c: #ffb060; }
.ma-head { margin: 0 0 4px; display: flex; align-items: center; gap: 10px; font: 600 13px var(--sans); letter-spacing: .16em;
  text-transform: uppercase; color: #ffb060; }
.ma-head::after { content: ""; flex: 1; height: 1px; background: rgba(255,176,96,.3); }
.marows { border-top: 0; }
.marows .row .w { color: var(--ink); font-weight: 500; }
.mob-mainacts { padding-top: 34px; margin: 0; }
.mob-mainacts .rows { list-style: none; margin: 0; padding: 0; }
.mob-mainacts .rows .row { padding: 15px 0 16px; margin-left: 0; width: 100%; min-height: 64px; }
.mob-mainacts .row .meta { display: block; margin-top: 7px; font-size: 15.5px; color: var(--ink-dim); }
.mob-mainacts .row .meta i { font-style: normal; opacity: .75; margin: 0 7px; }
.mob-mainacts .row .meta .free { font-weight: 500; color: #8cff9a; letter-spacing: .04em; }

/* The costume contest is one thing with parts: its parts sit indented under it, on a thin line in its colour */
.rows li.sub { position: relative; }
.rows li.sub::before { content: ""; position: absolute; left: 4px; top: 10px; bottom: 10px; width: 2px; border-radius: 1px;
  background: color-mix(in srgb, var(--c, #ffb060) 45%, transparent); }
.rows li.sub .row { padding-left: 30px; }
.mob-place .rows li.sub .row { padding-left: 20px; }
.mob-place .rows li.sub::before { left: 0; }
.ev-parts-head { margin-top: 28px; }
.ev-parts { border-top: 0; }
/* food trucks: a picture per truck, shown only once a logo or photo has arrived */
.trucks { list-style: none; margin: 20px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
.trucks img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 3px; }
.trucks span { display: block; margin-top: 5px; font-size: 13px; color: var(--ink-dim); }

/* 2026-10-19: at 1440x900 the Globen name sat under 'Back to overview' (bottom right). The button now takes the
   bottom-left corner, where 'Explore the program' rests on the start screen and nothing is drawn in a place view. */
.chrome-bottom .overview { right: auto; left: 34px; }

/* ---------- 2026-10-19 (Tony): Food trucks card, fox marker, Take part removed, smaller headline ---------- */
/* Food trucks: card on desktop, section on the phone page. One truck per line: name (linked only to the truck's
   own website), cuisine beside it in the date line's orange, picture only once data.js has a file for it. */
.details.info#foodtrucks { width: min(600px, 92vw); }
.ft-head { margin-top: 4px; }
.ft-list { list-style: none; margin: 8px 0 0; padding: 0; }
.ft-truck { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 11px 0; border-bottom: 1px solid rgba(232,236,242,0.1); }
.ft-truck.has-img { display: grid; grid-template-columns: 72px 1fr; align-items: center; column-gap: 14px; }
.ft-truck.has-img img { grid-row: span 2; width: 72px; aspect-ratio: 1; object-fit: cover; border-radius: 3px; }
.ft-name { font-family: var(--serif); font-weight: 700; font-size: 22px; line-height: 1.1; letter-spacing: .02em; text-transform: uppercase; color: var(--ink); }
.ft-name a { color: inherit; border-bottom: 1px solid rgba(232,236,242,0.35); }
.ft-name a:hover, .ft-name a:focus-visible { border-bottom-color: #ffb060; }
.ft-ext { font-family: var(--sans); font-weight: 500; font-size: .7em; margin-left: .3em; color: var(--ink-dim); }
.ft-cuisine { font-size: 13px; letter-spacing: .14em; text-transform: uppercase; color: #ffb060; }
.ft-note { text-align: left; margin-top: 18px; font-size: 15.5px; }
/* desktop card: the eight trucks in two columns, so the whole card fits at 1440x900 without scrolling */
@media (min-width: 721px) { .details.info .ft-list { display: grid; grid-template-columns: 1fr 1fr; column-gap: 28px; } .details.info .ft-truck { flex-direction: column; align-items: flex-start; gap: 4px; } }
.details.info .ft-note { grid-column: auto; }
.lvl .ft-open { width: 100%; margin-top: 4px; cursor: pointer; }
.mob-foodtrucks .details-short { font-size: 19px; line-height: 1.6; color: var(--ink-dim); margin: 0 0 18px; }
.mob-foodtrucks .ft-name a { font-family: var(--serif); font-size: inherit; padding: 0; display: inline; }
.mob-foodtrucks .ft-truck { padding: 13px 0; }
.mob-about.mob-foodtrucks p.ft-head { margin: 8px 0 4px; font: 600 13px/1.2 var(--sans); letter-spacing: .16em; color: #ffb060; }   /* the phone About paragraph style must not turn it grey */
/* the photo contest line under About keeps the facts look but sits closer to the partners line */
.about-photo { margin-top: 18px; }

/* Fox in a Box: the fox head's eyes sit lower than a ghost's, so the hover glance lifts them less */
.ghost.icon .eyes { transform-origin: 107px 126px; }
/* 2026-10-19: the moving bus and food truck face the way they drive (main.js moveRoutes) */
/* the flip sits on the inner .pose group: the svg itself runs the "hover" float animation, which owns its transform */
.ghost.icon-bus .pose, .ghost.icon-truck .pose { transform-origin: 107px 170px; transition: transform 0.7s cubic-bezier(0.45, 0, 0.2, 1); }   /* Tony 2026-10-19: the turn glides, never jumps */
.ghost.icon.drive-left .pose { transform: scaleX(-1); }
/* Tony 2026-10-19: no name under the bus and the truck (the hover card and aria-label still name them) */
.ghost.icon-bus .name, .ghost.icon-truck .name { display: none; }
/* Tony 2026-10-19: the Lekia lawn sign at Globen Shopping (data.js decor) */
.decor { position: absolute; left: 0; top: 0; width: 78px; height: 73px; margin: -73px 0 0 -39px; transform-origin: 50% 100%;
  pointer-events: none; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 8px rgba(255, 214, 150, 0.32)); }
.decor svg { width: 100%; height: 100%; display: block; overflow: visible; }
/* a warm pool of light on the ground, like the places have, but quieter */
.decor::before { content: ""; position: absolute; left: 50%; bottom: -5px; width: 74px; height: 18px; margin-left: -37px; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(255, 186, 110, 0.5) 0%, transparent 70%); filter: blur(3px); }
.decor svg { filter: drop-shadow(0 0 4px rgba(255, 170, 80, 0.35)); }   /* the pumpkin and the letters catch a little light */
.decor .sign-sway { transform-origin: 45px 82px; animation: sign-sway 6.5s ease-in-out infinite; }
@keyframes sign-sway { 0%, 100% { transform: rotate(-1.2deg); } 50% { transform: rotate(1.2deg); } }
@media (prefers-reduced-motion: reduce) { .decor .sign-sway { animation: none; } }
/* Tony 2026-10-21: the Lekia sign links to the store's page */
a.decor { pointer-events: auto; cursor: pointer; }
a.decor:hover svg, a.decor:focus-visible svg { filter: drop-shadow(0 0 7px rgba(255, 190, 110, 0.75)); }
/* Tony 2026-10-19: "inte så mkt ljus under sig, kanske bara ett svagt vitt ljus" (bus and truck), "bussen kan vara lite större" */
.ghost.icon-bus .light, .ghost.icon-truck .light { background: radial-gradient(ellipse at center, rgba(255,255,255,0.9) 0%, transparent 70%); width: 110px; margin-left: -55px; opacity: 0.22; }
.ghost.icon-bus:hover .light, .ghost.icon-truck:hover .light, .ghost.icon-bus.lit .light, .ghost.icon-truck.lit .light { opacity: 0.4; }
.ghost.icon-bus svg { scale: calc(var(--gscale, 1) * 0.88 * 1.3); }
.ghost.icon:hover .eyes, .ghost.icon:focus-visible .eyes, .ghost.icon.lit .eyes { --look: -8px; translate: 0 -6px; }
.ghost.icon.selected .eyes { --look: -6px; translate: 0 -4px; }

/* Tony 2026-10-19: "CLICK THE MAP TO EXPLORE behöver vara mindre". Desktop headline at 60 % of its old size
   (old: clamp(44px, 5.2vw, 76px)). The hero is anchored at the bottom, so the date line, countdown, orbs and
   main-acts row keep their places and only the headline's top edge comes down. Phone sizes are untouched. */
@media (min-width: 721px) {
  .hero-title { font-size: clamp(30px, 3.12vw, 46px); line-height: 1; }
  .hero-sub { margin-top: 10px; }
}

/* ---------- 2026-10-19 menu A (Tony: "testa a"): PARADE SHOW COSTUME CONTEST [logo] FOOD TRUCKS PROGRAM ABOUT  Sound ----------
   Left group stays where the old menu was. The right group and Sound sit in one row at the right edge of the
   visible map, so they move with the side panel's edge. main.js (measureMenu / stepGlide) keeps the logo clear of
   both and sets "tight" (narrow windows), "stack" (no room with the panel open) and Sound's "compact". */
@media (min-width: 721px) {
  .chrome-right { position: absolute; right: 34px; top: 26px; display: flex; align-items: center; gap: 28px; }
  .chrome-top .chrome-right .sound { position: static; }
  .chrome-top .nav-right { position: static; gap: 28px; }
  /* narrow windows (about 1280 px and below): smaller gaps and letter spacing, same type size, same logo size */
  .chrome-top.tight .nav { gap: 16px; letter-spacing: .06em; }
  .chrome-top.tight .nav a { padding-left: 4px; padding-right: 4px; }
  .chrome-top.tight .chrome-right { gap: 16px; }
  /* a narrow window with the side panel open: the right group and Sound drop one row, under the left group
     (the logo has faded then, there is no room for it) */
  .chrome-top.stack .chrome-right { right: auto; left: 34px; top: 80px; }
}
@media (max-width: 720px) { .chrome-right { display: contents; } }
/* About: Contact is its last block now */
.about-contact { margin-top: 30px; }
.about-contact .details-short { margin: 10px 0 16px; font-size: 18px; }
.about-contact .fine { text-align: left; }
.brand.logo.unfit { pointer-events: none; }   /* faded logo (no room): clicks go to the menu under it */

/* 2026-10-19 perf, Safari only (html.wk, set in main.js): the logo's breath as a fade between two still glows.
   2026-10-20 (Tony: "Okej, jag godkänner denna lagning"): the two glows are baked into images, drawn by Safari itself
   (brandBreath 0 % and 50 %, bake in .tmp/shockholm/perf-safari-2026-10-21/). A CSS filter on the moving logo cost
   Safari 14-18 missed frames per ghost-click flight; still images cost nothing. The bright one fades in and out over
   the same 7.5 s. The logo image itself stays in place, hidden, so position, size and click area are unchanged. */
@media (min-width: 721px) {   /* the phone keeps its own single still glow (the iPhone rectangle fix above) */
  /* until the baked glow has loaded: the quiet glow as a still filter (nothing moves that early) */
  /* width pinned to the file's ratio (1138 x 407): Safari sometimes laid the image out 1-1.5 px too wide, and the
     baked glow must sit edge to edge with it (measured 2026-10-20, safari_logo_check2.mjs) */
  html.wk .brand.logo img { animation: none; width: calc(var(--logo-h, 160px) * 1138 / 407);
    filter: drop-shadow(0 0 2px rgba(255,255,255,0.70)) drop-shadow(0 0 10px rgba(205,225,255,0.50))
            drop-shadow(0 0 28px rgba(150,190,255,0.34)) drop-shadow(0 0 60px rgba(110,160,255,0.22))
            drop-shadow(0 6px 30px rgba(255,150,60,0.13)); }
  html.wk.wkglow-ready .brand.logo img { filter: none; visibility: hidden; }
  html.wk.wkglow-ready .brand.logo .wkglow { display: block; }
  /* each file is the logo drawn --gb x --gl px with 200 px of glow on every side, at 2x. Sized in % of the logo's own box
     (the .brand link is exactly the image): Safari lays the image out up to 1.5 px wider on some loads, the glow follows */
  html.wk .brand.logo .wkglow { --gb: 98; --gl: 274; position: absolute; pointer-events: none;
    left: calc(-20000% / var(--gl)); top: calc(-20000% / var(--gb));
    width: calc(100% * (var(--gl) + 400) / var(--gl));
    height: calc(100% * (var(--gb) + 400) / var(--gb));
    background: no-repeat 0 0 / 100% 100%; }
  html.wk .brand.logo .wkglow.quiet { background-image: url(assets/logo-glow-98-quiet.webp); }
  html.wk .brand.logo .wkglow.bright { background-image: url(assets/logo-glow-98-bright.webp);
    animation: wkBreath 7.5s ease-in-out infinite; }
}
/* one file pair per band of --logo-h (main.js: window width x 0.0833, 96-150 px), each baked at the band's top, so the
   logo is drawn at 90-100 % of its baked size, never blown up; the glow differs by under 1 level from Safari's own */
@media (min-width: 1177px) { html.wk .brand.logo .wkglow { --gb: 109; --gl: 305; }
  html.wk .brand.logo .wkglow.quiet { background-image: url(assets/logo-glow-109-quiet.webp); }
  html.wk .brand.logo .wkglow.bright { background-image: url(assets/logo-glow-109-bright.webp); } }
@media (min-width: 1309px) { html.wk .brand.logo .wkglow { --gb: 121.5; --gl: 339.5; }
  html.wk .brand.logo .wkglow.quiet { background-image: url(assets/logo-glow-121-5-quiet.webp); }
  html.wk .brand.logo .wkglow.bright { background-image: url(assets/logo-glow-121-5-bright.webp); } }
@media (min-width: 1459px) { html.wk .brand.logo .wkglow { --gb: 135; --gl: 377.5; }
  html.wk .brand.logo .wkglow.quiet { background-image: url(assets/logo-glow-135-quiet.webp); }
  html.wk .brand.logo .wkglow.bright { background-image: url(assets/logo-glow-135-bright.webp); } }
@media (min-width: 1621px) { html.wk .brand.logo .wkglow { --gb: 150; --gl: 419.5; }
  html.wk .brand.logo .wkglow.quiet { background-image: url(assets/logo-glow-150-quiet.webp); }
  html.wk .brand.logo .wkglow.bright { background-image: url(assets/logo-glow-150-bright.webp); } }
@keyframes wkBreath { 0%, 100% { opacity: 0 } 50% { opacity: 1 } }
.brand.logo .wkglow { display: none; }
@media (prefers-reduced-motion: reduce) { html.wk .brand.logo .wkglow.bright { animation: none; opacity: 0; } }

/* 2026-10-20 (Tony): partner logo row under About (desktop card and phone About). Logos white, one even height. */
.partners { margin-top: 22px; }
.partners .partners-h, .mob-about .partners .partners-h { margin: 0 0 12px; font-size: 12px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--ink-faint); line-height: 1.4; }
.partner-row { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 14px 26px; }
.partner-row li { display: flex; }
.partner-row a, .mob-about .partner-row a { display: flex; align-items: center; height: 26px; padding: 6px 0; border: 0;
  opacity: 0.82; transition: opacity 0.2s var(--ease); }
.partner-row a:hover, .partner-row a:focus-visible { opacity: 1; }
.partner-row img { display: block; height: 22px; width: auto; max-width: 118px; object-fit: contain; }
.partner-row img.tint { filter: brightness(0) invert(1); }
.partner-row img[src$="fox-in-a-box.svg"] { height: 30px; margin: -4px 0; }   /* its wordmark is small inside the file */
.portal-caption { transition: border-color 0.25s var(--ease-out), background 0.25s var(--ease-out); }
.portal-caption:hover, .portal-caption:focus-within { border-color: color-mix(in srgb, var(--c) 75%, transparent); background: rgba(6,9,16,0.84); }

/* 2026-10-20 (Tony): photo contest button beside Explore the program; quiet partner strip bottom-right; partner logo in its place panel */
.photobtn { position: absolute; left: var(--photo-left, 330px); bottom: calc(22px + var(--safe-b)); }   /* left set in main.js: right of Hector and the Portal */
.photobtn.away { opacity: 0; pointer-events: none; }
.photobtn { transition: opacity 0.4s var(--ease), border-color 0.3s, background 0.3s; }
.partner-strip { position: absolute; right: 34px; bottom: calc(26px + var(--safe-b)); display: flex; align-items: center; gap: 18px;
  opacity: 0.55; transition: opacity 0.3s var(--ease-out); }
.partner-strip:hover, .partner-strip:focus-within { opacity: 0.95; }
.partner-strip .ps-h { font-size: 11.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-faint); }
.partner-strip a { display: flex; align-items: center; height: 28px; }
.partner-strip img { display: block; height: 14px; width: auto; max-width: 76px; object-fit: contain; }
.partner-strip.no-room { visibility: hidden; }
.partner-strip img.tint { filter: brightness(0) invert(1); }
.partner-strip img[src$="fox-in-a-box.svg"] { height: 21px; margin: -3px 0; }
.place-partner { display: inline-flex; align-items: center; gap: 12px; margin: 2px 0 16px; padding: 7px 0; border: 0; opacity: 0.85; }
.place-partner:hover { opacity: 1; }
.place-partner span { font-size: 11.5px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-faint); }
.place-partner img { display: block; height: 18px; width: auto; max-width: 110px; object-fit: contain; }
.place-partner img.tint { filter: brightness(0) invert(1); }
.mob-place .place-partner, .mob-about .place-partner { border: 0; font-family: var(--sans); }
.mob-photo { padding: 8px 22px 24px; }
.mob-photo .mob-photo-btn { display: flex; align-items: center; justify-content: center; min-height: 52px; padding: 0 18px;
  border: 1px solid rgba(232,236,242,0.4); border-radius: 2px; font-family: var(--sans); font-size: 14.5px; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink); background: rgba(5,8,16,0.35); }
@media (max-width: 720px) { .partner-strip, .photobtn { display: none !important; } }
/* 2026-10-20 perf, Safari desktop only (html.wk): the ghosts' soft edge as a CSS blur on the svg instead of the SVG
   filter #gsoftN on .pose. Safari redrew the SVG blur on every frame while the ghosts change size in a camera flight:
   with the baked logo still 7-34 of ~100 frames over 33 ms; SVG blur off 1-4; this CSS blur 3-6 (real Safari 27,
   2026-10-20, .tmp/shockholm/perf-safari-2026-10-21/ghost_cssblur_raw3.txt). 0.57 px = stdDeviation 1.8 x 68 px / 214. */
@media (min-width: 721px) {
html.wk .ghost .pose { filter: none; }
html.wk .ghost:has(.pose) svg { filter: blur(0.57px); }
html.wk .ghost:has(.pose):hover svg, html.wk .ghost:has(.pose):focus-visible svg { filter: blur(0.57px) brightness(1.25); }
html.wk .ghost.lit:has(.pose) svg { filter: blur(0.57px) brightness(1.35) drop-shadow(0 0 14px color-mix(in srgb, var(--c) 80%, transparent)); }
}

/* 2026-10-23 (Lisbeth): the costume contest's age groups, one block each: age, when, prize. Same type as the facts */
.cats-head { margin-top: 4px; }
.cats { list-style: none; margin: 6px 0 26px; padding: 0; }
.cats li { padding: 14px 0 15px; border-bottom: 1px solid rgba(232,236,242,0.1); }
.cats h3 { margin: 0 0 6px; font-family: var(--serif); font-weight: 700; font-size: 22px; line-height: 1.1; letter-spacing: .02em; text-transform: uppercase; color: var(--ink); }
.cats p { margin: 0; font-size: 17px; line-height: 1.55; color: var(--ink-dim); text-wrap: pretty; }
.cats .prize { margin-top: 8px; color: var(--ink); }
/* the photo contest card (desktop) and section (phone) */
.details.info#photocontest { width: min(600px, 92vw); }
.pc-part { margin-top: 18px; }
.pc-part .details-short { margin-top: 10px; }
/* 2026-10-31 (Tony): the Rent a Ghost sign-up button in its card (desktop) and phone section, same look as the front-page buttons */
.rag-cta { margin: 26px 0 4px; }
.rag-cta a.ghostbtn, .mob-about .rag-cta a.ghostbtn { display: inline-flex; padding: 14px 22px; font-family: var(--sans); font-size: 14.5px; font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink); border: 1px solid rgba(232,236,242,0.5); }
@media (max-width: 720px) { .mob-about .rag-cta a.ghostbtn { display: flex; justify-content: center; min-height: 52px; } }
