:root {
  --accent: #ffc86e;
  --panel: rgba(10, 11, 28, 0.58);
  --line: rgba(255, 255, 255, 0.09);
  --text: #eceeff;
  --muted: #9ba0cc;
  --ph: #ff6154;                                 /* Product Hunt's brand colour, for the billboard's launch link */
  --live: 114, 230, 162;                         /* the live count's green (rgb parts): the pill and the picker's cards */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --jobs-h: 26px;                                /* the Help wanted strip across the top (#jobs) */
  --top: calc(env(safe-area-inset-top) + var(--jobs-h) + 12px);   /* top edge of the title, links and pills, under it */
  --bottom: max(14px, env(safe-area-inset-bottom));   /* bottom edge of the dock */
  --edge: 12px;                                  /* min side gutter of the bottom dock */
  --gutter: max(var(--edge), env(safe-area-inset-left), env(safe-area-inset-right));   /* mirrored: keeps the dock centred between notch insets */
  --btn: 36px;                                  /* icon button size (44px on touch) */
  color-scheme: dark;
}
* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%; overflow: hidden; background: #05060f; color: var(--text);
  font: 14px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;   /* no pull-to-refresh or rubber-banding when swiping the scene */
}
/* one-finger drags on the scene are the app's (look around, swipe to change city, site/js/app.js); a pinch still zooms */
#screen { position: fixed; inset: 0; display: block; image-rendering: pixelated; touch-action: pinch-zoom; }
button { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Only parsed as elements when scripting is disabled. Hide inactive controls from keyboard navigation too. */
body:has(.no-script) > :not(noscript) { display: none !important; }
.no-script {
  position: fixed; inset: 0; overflow: auto;
  padding: max(24px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(24px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left));
  background: #05060f; color: #eceeff; font-size: 16px; line-height: 1.6;
}
.no-script > * { max-width: 38rem; margin: 0 auto 1rem; }
.no-script h2 { font-size: 1.5rem; line-height: 1.3; }
.no-script a { color: #ffc86e; text-decoration: underline; }

/* auto-hiding chrome */
.ui { transition: opacity .6s ease, transform .6s ease; }
body.idle, body.idle * { cursor: none; }
body.idle .ui, body.ui-off .ui { opacity: 0; pointer-events: none; }
body.idle .hud-bottom, body.ui-off .hud-bottom { transform: translate(-50%, 8px); }
/* ?obs (OBS / embed mode): the scene only, for good. No interface, pills or toasts. */
.obs .ui, .obs .pill { display: none !important; }

/* city title */
.hud-city {
  position: fixed; top: var(--top); left: max(18px, env(safe-area-inset-left));
  max-width: calc(100vw - 360px - var(--ph-room, 0px) - var(--coffee-room, 0px));   /* clear of the links, with one of them opened by a mouse (see #shopLink) and coffee's words */
  text-shadow: 0 1px 14px rgba(0, 0, 0, .7); pointer-events: none;
}
#cityName { margin: 0 0 2px; font-size: 30px; font-weight: 650; letter-spacing: -.01em; line-height: 1.1; }
.sub { margin: 0; color: var(--muted); font-size: 13px; }
.sub time { color: var(--accent); font-family: var(--mono); white-space: nowrap; }
/* " · tonight's city" after the time (site/js/app.js renderTonight), where the title has room for it on the same line */
@media (max-width: 600px), (max-height: 500px) { .tonight-note { display: none; } }
/* a city's looks (site/js/app.js renderLooks): a chip per look under the tagline, the one on screen lit. The title lets
   presses through to the scene; the chips take theirs while the interface shows. */
.looks {
  display: flex; gap: 2px; width: fit-content; margin: 9px 0 0; padding: 2px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--panel);
  -webkit-backdrop-filter: blur(12px) saturate(1.3); backdrop-filter: blur(12px) saturate(1.3);
  text-shadow: none; pointer-events: auto;
}
.looks[hidden] { display: none; }
.looks button {
  padding: 4px 10px; border: 0; border-radius: 8px; background: transparent;
  font: 500 11.5px var(--mono); color: var(--muted); cursor: pointer;
}
.looks button:hover { color: var(--text); }
.looks button[aria-pressed="true"] { background: color-mix(in srgb, var(--accent) 24%, transparent); color: var(--text); }
body.idle .looks, body.ui-off .looks { pointer-events: none; }
@media (pointer: coarse) { .looks button { padding: 9px 14px; font-size: 12.5px; } }
/* live count (site/js/live/live.js): a glowing pill with a blinking pixel, "23 here now · 5 in Tokyo · 480 today · 12,345 all
   time". Frosted like the other panels so it reads over any scene. One row; on phones it is the city card's last row,
   without today and all time (see max-width: 600px). */
.live {
  position: relative; display: grid; grid-auto-flow: column; align-items: center; column-gap: 6px; width: fit-content;
  margin: 10px 0 0; padding: 5px 12px 5px 10px; border-radius: 14px;
  border: 1px solid rgba(var(--live), .32); background: var(--panel);
  -webkit-backdrop-filter: blur(12px) saturate(1.3); backdrop-filter: blur(12px) saturate(1.3);
  box-shadow: 0 0 14px rgba(var(--live), .2), inset 0 0 10px rgba(var(--live), .08);
  color: var(--text); font-size: 12px; line-height: 1.5; text-shadow: none;
}
.live[hidden] { display: none; }
.live::before {
  content: ""; flex: none; width: 6px; height: 6px; margin-right: 2px;
  background: rgb(var(--live)); box-shadow: 0 0 8px rgba(var(--live), .9); animation: live 2.4s ease-in-out infinite;
}
.live::after {   /* the glow breathes with the pixel (opacity only, so it stays off the main thread) */
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 22px rgba(var(--live), .35); animation: live 2.4s ease-in-out infinite;
}
.live span { white-space: nowrap; }
.live span + span { color: var(--muted); }
.live span + span::before { content: "·"; margin-right: 6px; }   /* as far from the dot as the gap before it */
@keyframes live { 50% { opacity: .35; } }

/* author links */
#links {
  position: fixed; top: var(--top); right: max(18px, env(safe-area-inset-right));
  display: flex; gap: 2px; padding: 4px; border-radius: 14px; border: 1px solid var(--line); background: var(--panel);
  -webkit-backdrop-filter: blur(12px) saturate(1.3); backdrop-filter: blur(12px) saturate(1.3);
}
#links :is(a, button) { color: var(--muted); transition: color .2s, background .2s; }
#links :is(a, button):hover, #links :is(a, button):focus-visible { color: var(--text); }
#accountBtn { border: 0; background: none; cursor: pointer; }
#accountBtn.signed-in svg { fill: var(--stamp); }    /* signed in: the person in the passport's ink */
/* the Product Hunt (its launch week only, site/js/ui/launch.js), shop, coffee and the site's own X links are icons like the rest (coffee is filled, below); a mouse over one
   (or the keyboard on it) slides its words out to the right of the icon (the row is pinned to the right edge, so it
   grows to the left). Touch screens keep the icons. */
:is(#phLink, #shopLink, #coffee, #followX, #accountBtn) { height: var(--btn); display: flex; align-items: center; padding: 0 calc((var(--btn) - 16px) / 2); border-radius: 10px; font-size: 13px; text-decoration: none; }
#links :is(#phLink, #shopLink, #coffee, #followX, #accountBtn) { transition: color .2s, background .2s, padding .25s ease; }
:is(#phLink, #shopLink, #coffee, #followX, #accountBtn):hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
:is(#phLink, #shopLink, #coffee, #followX, #accountBtn) svg { flex: none; width: 16px; height: 16px; fill: var(--accent); shape-rendering: crispEdges; }
:is(#phLink, #shopLink, #coffee, #followX, #accountBtn) span { max-width: 0; overflow: hidden; white-space: nowrap; opacity: 0; transition: max-width .25s ease, margin .25s ease, opacity .2s; }
/* not at phone widths (600 px and under), where the city title needs the room */
@media (hover: hover) and (min-width: 601px) {
  :is(#phLink, #shopLink, #coffee, #followX, #accountBtn):hover { padding-right: 12px; }
  :is(#phLink, #shopLink, #coffee, #followX, #accountBtn):hover span { max-width: 12em; margin-left: 8px; opacity: 1; }
}
@media (min-width: 601px) {
  :is(#phLink, #shopLink, #coffee, #followX, #accountBtn):focus-visible { padding-right: 12px; }
  :is(#phLink, #shopLink, #coffee, #followX, #accountBtn):focus-visible span { max-width: 12em; margin-left: 8px; opacity: 1; }
}
:root:has(#phLink:not([hidden])) { --ph-room: 64px; }   /* the city title keeps clear of its longer words too */
/* Buy me a coffee is the one link the site asks for: a filled pill at the head of the row, its words always out where
   the city title has the room for them (from 801 px, --coffee-room); below that, the icon in the same fill. */
#links #coffee { background: var(--accent); color: #12121c; font-weight: 600; }
#links #coffee svg { fill: currentColor; }
#links #coffee:hover, #links #coffee:focus-visible { background: color-mix(in srgb, var(--accent) 80%, #fff); color: #12121c; }
@media (min-width: 801px) {
  :root { --coffee-room: 112px; }
  #links #coffee { margin-right: 4px; padding-right: 12px; }
  #links #coffee span { max-width: 12em; margin-left: 8px; opacity: 1; }
}
/* and it moves, quietly: the cup's steam (drawn in the link, not the sprite) flickers between two pixel frames, and a
   sheen crosses the pill every few seconds. Both stand still while the interface is faded out, and with reduced motion. */
#links #coffee { position: relative; overflow: hidden; }
#coffee .steam { animation: steam 1.4s steps(1) infinite; }
#coffee .steam + .steam { opacity: 0; animation-name: steam-b; }
#links #coffee::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; transform: translateX(-100%);
  background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, .55) 50%, transparent 65%);
  animation: sheen 7s ease-in-out infinite;
}
:is(body.idle, body.ui-off) #coffee :is(.steam, .steam + .steam), :is(body.idle, body.ui-off) #links #coffee::after { animation-play-state: paused; }
@keyframes steam { 50% { opacity: 0; } }
@keyframes steam-b { 50% { opacity: 1; } }
@keyframes sheen { 0%, 80% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }

/* Help wanted (site/js/jobs/jobs-ticker.js): a thin strip across the very top, the jobs (a link to jobs/) and a "Post a job"
   button (jobs/post/); the title, links and pills sit under it (--top). Its jobs run right to left by one run's width (the run is there twice, so it loops without a seam:
   every item carries its own gap) and stand still while it's hovered, focused or faded out, or with reduced motion. */
#jobs {
  position: fixed; top: 0; left: 0; right: 0; height: calc(var(--jobs-h) + env(safe-area-inset-top));
  display: flex; align-items: center; gap: 12px;
  padding: env(safe-area-inset-top) max(14px, env(safe-area-inset-right)) 0 max(14px, env(safe-area-inset-left));
  border-bottom: 1px solid var(--line); background: var(--panel);
  -webkit-backdrop-filter: blur(12px) saturate(1.3); backdrop-filter: blur(12px) saturate(1.3);
  font-size: 12px; line-height: 1; white-space: nowrap;
}
#jobs:is(:hover, :focus-within) { background: color-mix(in srgb, var(--accent) 10%, var(--panel)); }
.jobs-main { flex: 1; min-width: 0; align-self: stretch; display: flex; align-items: center; gap: 12px; color: var(--text); text-decoration: none; }
.jobs-main:focus-visible, .jobs-post:focus-visible { outline-offset: -2px; }
.jobs-post {
  flex: none; padding: 3px 10px; border-radius: 999px; font-weight: 600; font-size: 11px; line-height: 1.3; text-decoration: none;
  color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.jobs-post:hover, .jobs-post:focus-visible { color: var(--text); background: color-mix(in srgb, var(--accent) 28%, transparent); }
.jobs-tag { flex: none; font: 600 10px/1 var(--mono); letter-spacing: .2em; text-transform: uppercase; color: var(--accent); }
.jobs-tag::before {   /* a lit pixel, like the live pill's */
  content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 8px; vertical-align: 1px;
  background: var(--accent); box-shadow: 0 0 8px var(--accent);
}
/* the right edge fades what doesn't fit; the left one only while the jobs run in from it, so a line standing still
   (the call before the list comes, reduced motion) keeps its first letters */
.jobs-run {
  flex: 1; min-width: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
}
#jobs.running .jobs-run {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 24px), transparent);
}
.jobs-track { display: inline-flex; }
.jobs-item { position: relative; padding-right: 34px; }
.jobs-item::after {   /* a pixel between the jobs */
  content: ""; position: absolute; right: 15px; top: 50%; width: 4px; height: 4px; margin-top: -2px; background: var(--accent); opacity: .7;
}
#jobs:not(.running) .jobs-item::after { content: none; }
.jobs-item.posted { color: var(--accent); }                  /* posted here, approved: first in the run */
.jobs-cta { color: var(--muted); }
#jobs:not(.running) .jobs-cta { color: var(--text); }
#jobs.running .jobs-track { animation: jobs-run var(--jobs-s, 60s) linear infinite; }
#jobs.running:is(:hover, :focus-within) .jobs-track, body:is(.idle, .ui-off) #jobs.running .jobs-track { animation-play-state: paused; }
@keyframes jobs-run { to { transform: translateX(-50%); } }
.jobs-all { flex: none; display: inline-flex; align-items: center; gap: 6px; color: var(--muted); }
.jobs-all svg { flex: none; transition: transform .18s ease-out; }
.jobs-main:is(:hover, :focus-visible) .jobs-all { color: var(--text); }
.jobs-main:is(:hover, :focus-visible) .jobs-all svg { transform: translateX(3px); }
#jobs:not(.running) .jobs-all { color: var(--accent); }
#jobs:not(.running) .jobs-main { padding-inline: 6px; }
#jobs:not(.running) .jobs-tag::before { background: none; box-shadow: none; border: 1px solid var(--accent); }
#jobs:not(.running) .jobs-main:is(:hover, :focus-visible) .jobs-tag::before { background: var(--accent); }
#jobs:not(.running) .jobs-item { padding-right: 0; }
/* turned off in Settings (.jobs-off on the html, from its <head> before the first paint, and site/js/app.js): no strip,
   and the title, links and pills go back up to the top */
.jobs-off #jobs { display: none; }
:root.jobs-off { --top: max(16px, env(safe-area-inset-top)); }

/* bottom dock: as wide as the window lets it, up to one row of the 14 buttons with their gaps, the two sliders and the
   padding (256px) and 430px for the track, where the longest title (252px) and line ("Lofi Cities Radio · Liquid DnB,
   picked by vote · 1,234 listening", 424px) show whole. A new button in #player goes into this count and the two-row
   rule's widths (tests/dock.test.mjs). */
.hud-bottom {
  position: fixed; left: 50%; bottom: var(--bottom); transform: translateX(-50%);
  width: min(calc(14 * (var(--btn) + 10px) + 686px), calc(100vw - 2 * var(--gutter))); display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px;
}
/* One row of chips (the city picker, the tour, the weather, the radio): centred while they fit; when they don't (the
   narrowest phones) it bleeds into the gutters and swipes. No edge-fade mask: it would switch off the chips' backdrop blur. */
#cities {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(-1 * var(--gutter)); padding: 2px calc(var(--gutter) + 2px);
  scroll-snap-type: x proximity; scroll-padding-inline: calc(var(--gutter) + 24px); scroll-behavior: smooth;
}
#cities::-webkit-scrollbar { display: none; }
.chip {
  flex: none; display: inline-flex; align-items: baseline; gap: 8px; padding: 7px 12px; cursor: pointer;
  border-radius: 999px; border: 1px solid var(--line); background: var(--panel);
  -webkit-backdrop-filter: blur(12px) saturate(1.3); backdrop-filter: blur(12px) saturate(1.3);
  transition: border-color .2s, background .2s, box-shadow .2s; scroll-snap-align: start;
}
.chip:first-child { margin-left: auto; }   /* auto margins centre without clipping the start (justify-content would) */
.chip:last-child { margin-right: auto; }
.chip small { font: 11px var(--mono); color: var(--muted); }
.chip:hover { border-color: rgba(255, 255, 255, .22); }
/* the city on screen opens the picker, lit in its colour; the tour and weather chips are lit (without the glow) while a
   tour runs or a weather is picked */
.menu-chip svg { align-self: center; width: 16px; height: 16px; fill: var(--muted); shape-rendering: crispEdges; }
.menu-chip.on { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.menu-chip.on svg { fill: var(--accent); }
.menu-chip.on small { color: var(--accent); }
.menu-chip[aria-expanded="true"] { background: color-mix(in srgb, #fff 10%, var(--panel)); }
#cityBtn {
  border-color: color-mix(in srgb, var(--accent) 75%, transparent);
  background: color-mix(in srgb, var(--accent) 16%, var(--panel));
  box-shadow: 0 0 22px -8px var(--accent);
}
#cityBtn[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 28%, var(--panel)); }
#cityBtn { flex: 0 1 auto; min-width: 0; }      /* a long name gives way first (the title shows it whole) */
#cityBtnName { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* its hint (site/js/ui/city-roll.js): other cities' names roll up through it and back, over its own (kept, see-through) */
#cityBtnName.rolling { position: relative; color: transparent; }
.name-roll { position: absolute; top: 0; left: 0; width: 100%; display: flex; flex-direction: column; color: var(--text); }
.name-roll span { flex: none; overflow: hidden; text-overflow: ellipsis; }
#cityBtnTime { transition: opacity .2s; }
#cityBtnName.rolling + #cityBtnTime { opacity: 0; }   /* the time is the city on screen's, not the names rolling by */
#cityBtn svg, #cityBtn small { fill: var(--accent); color: var(--accent); }
/* a thin rule between the city and those two chips */
#tourBtn { position: relative; margin-left: 9px; }
#tourBtn::before {
  content: ""; position: absolute; left: -9px; top: 5px; bottom: 5px; width: 1px;
  background: rgba(255, 255, 255, .24); pointer-events: none;
}

#player {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px 8px 8px;
  border-radius: 16px; border: 1px solid var(--line); background: var(--panel);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
}
.icon-btn {
  flex: none; width: var(--btn); height: var(--btn); display: grid; place-items: center; padding: 0;
  border: 0; border-radius: 10px; background: transparent; cursor: pointer;
}
.icon-btn:hover { background: rgba(255, 255, 255, .08); }
.icon-btn:disabled { opacity: .35; cursor: default; background: transparent; }
.icon-btn svg { width: 16px; height: 16px; fill: currentColor; shape-rendering: crispEdges; }
#play { background: var(--accent); color: #12121c; }
#play:hover { filter: brightness(1.08); }
#play.pulse { animation: pulse 2.2s ease-in-out infinite; }
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  50% { box-shadow: 0 0 0 9px transparent; }
}
.track { flex: 1; min-width: 0; }
#trackTitle, #trackMeta { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#trackTitle { font-weight: 560; }
#trackMeta { font: 11px var(--mono); color: var(--muted); }
/* the first line until the first track: "Press play…", and "Tap to play" on touch screens (a tap anywhere starts it; a longer line cuts off in a 375 px dock) */
.play-hint.tap { display: none; }
@media (pointer: coarse) { .play-hint { display: none; } .play-hint.tap { display: inline; } }
.vols { flex: none; display: flex; align-items: center; gap: 10px; }
.vol { flex: none; display: flex; align-items: center; gap: 6px; color: var(--muted); }
.vol svg { flex: none; width: 14px; height: 14px; fill: currentColor; shape-rendering: crispEdges; }
.vol input { width: 84px; accent-color: var(--accent); }
#queueBtn[aria-expanded="true"], #timerBtn[aria-expanded="true"], #vibeBtn[aria-expanded="true"], #mixerBtn[aria-expanded="true"], #saveBtn[aria-expanded="true"], #togetherBtn[aria-expanded="true"], #chatBtn[aria-expanded="true"], #boardBtn[aria-expanded="true"], #settingsBtn[aria-expanded="true"], #pip[aria-pressed="true"] { background: rgba(255, 255, 255, .1); color: var(--accent); }
#timerBtn.on { color: var(--accent); }   /* a timer is running */
#vibeBtn.on { color: var(--accent); }    /* on the radio */
#boardBtn { color: var(--gold); }        /* the trophy is first place's gold in every city; open, the city's accent like the rest */
#mixerBtn svg, #togetherBtn svg, #chatBtn svg { width: 18px; }   /* the faders, the two people and the bubble are 9 pixels wide */
/* the mini player's source video: kept in the page for Picture-in-Picture, but never seen */
.pip-source { position: fixed; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* popovers: up next, timers, vibe, together, the city sounds mixer and save open over the chips, the cities, tour and weather
   above them */
.dock { position: relative; }
.pop {
  position: absolute; left: 0; bottom: calc(100% + 8px); width: min(360px, 100%);
  display: flex; flex-direction: column; border-radius: 16px; border: 1px solid var(--line);
  background: rgba(10, 11, 28, 0.74);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  box-shadow: 0 18px 40px -20px rgba(0, 0, 0, .8); animation: pop-in .16s ease-out;
}
@keyframes pop-in { from { opacity: 0; transform: translateY(6px); } }
.pop-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 12px 14px 6px; }
.pop-head span { font: 600 11px/1 var(--mono); letter-spacing: .28em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.pop-head small { font: 11px var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* up next, the city tour and the weather: lists of row buttons */
#queue, #tour, #weather, #mixer { max-height: min(380px, 55vh); }
#tour, #weather { width: min(320px, 100%); }   /* above their own chips (site/js/app.js placeOver sets left) */
#mixer { left: auto; right: 0; width: min(320px, 100%); }   /* under its button, on the right of the dock */
/* every city: a search and a grid of previews (img/thumbs), centred over the dock; the grid scrolls on short screens */
#cityPicker {   /* --dock-top (site/js/app.js measureDock, again as it opens): it may cover the city title, never the top of the screen */
  left: 0; right: 0; margin-inline: auto; width: min(760px, 100%); max-height: min(640px, calc(var(--dock-top, 80vh) - var(--top) - 8px));
}
#cityFind {
  margin: 2px 10px 8px; padding: 8px 12px; border-radius: 10px; border: 1px solid var(--line);
  background: rgba(255, 255, 255, .06); font: 13px var(--mono); color: var(--text);
}
#cityFind::placeholder { color: var(--muted); }
/* the region chips under the search (site/js/app.js setRegion): All, then each region and its number of cities. flex: none, or
   the grid's long list squeezes the row (its overflow lets it shrink) */
.region-chips { flex: none; display: flex; gap: 6px; margin: -2px 10px 8px; overflow-x: auto; scrollbar-width: none; }
.region-chips::-webkit-scrollbar { display: none; }
.region-chip {
  flex: none; display: flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--line);
  background: rgba(255, 255, 255, .04); font: 500 11.5px var(--mono); color: var(--muted); cursor: pointer; white-space: nowrap;
}
.region-chip small { font: inherit; opacity: .7; }
.region-chip:hover { color: var(--text); }
.region-chip[aria-pressed="true"] {
  color: var(--text); border-color: color-mix(in srgb, var(--accent) 60%, transparent); background: color-mix(in srgb, var(--accent) 16%, transparent);
}
#cityFind:focus-visible { outline: none; border-color: color-mix(in srgb, var(--accent) 60%, transparent); }
#cityList {
  list-style: none; margin: 0; padding: 0 6px 6px; overflow-y: auto; overscroll-behavior: contain;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 4px;
}
.city-card {
  position: relative; width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 0 8px;
  padding: 4px 4px 6px; border: 1px solid transparent; border-radius: 12px; background: transparent; text-align: left; cursor: pointer;
}
.city-card img {
  grid-column: 1 / -1; display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; margin-bottom: 5px;
  border-radius: 8px; background: rgba(255, 255, 255, .06);
}
.city-card span { padding-left: 3px; font-weight: 560; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.city-card small { padding-right: 3px; font: 11px var(--mono); color: var(--muted); }
.card-country { grid-column: 1 / -1; padding: 1px 3px 0; font: normal 10.5px var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.city-card:hover, .city-card:focus-visible { background: rgba(255, 255, 255, .08); }
.city-card[aria-current="true"] {
  border-color: color-mix(in srgb, var(--accent) 75%, transparent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.city-card[aria-current="true"] small { color: var(--accent); }
/* who's in each city (site/js/live/live.js cityLabels): the live pill's pixel and "5 here", dark on the preview's corner */
.card-live {
  position: absolute; top: 9px; left: 9px; display: flex; align-items: center; gap: 5px;
  padding: 0 7px 0 6px; border-radius: 7px; border: 1px solid rgba(var(--live), .32);
  background: rgba(6, 7, 18, .78); box-shadow: 0 0 10px rgba(var(--live), .18);
  font: 500 10.5px/18px var(--mono); color: var(--text); white-space: nowrap; pointer-events: none;
}
.card-live[hidden] { display: none; }
.card-live::before { content: ""; width: 5px; height: 5px; background: rgb(var(--live)); box-shadow: 0 0 6px rgba(var(--live), .9); }
/* the city with the most people right now (site/js/live/live.js topCity), across the top of the grid: its preview fills the first
   column (a subgrid, so it lines up with the cards under it) and the rest reads "Most people right now", its name and
   its local time, lit green like the live pill (or in the accent, like any card, when it's the city on screen) */
.featured-item { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.featured-item[hidden] { display: none; }
.city-featured {
  grid-column: 1 / -1; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-columns: subgrid;
  align-items: center; margin-bottom: 2px; border-color: rgba(var(--live), .26);
  background: linear-gradient(90deg, rgba(var(--live), .1), rgba(var(--live), .02) 70%);
}
.city-featured img { grid-column: 1; margin: 0; }
.city-featured .feat-text {   /* not the cards' one-line name: "Most people right now" takes two lines on phones */
  grid-column: 2 / -1; display: flex; flex-direction: column; gap: 5px; min-width: 0; padding: 0 8px 0 6px; font-weight: 400; white-space: normal;
}
.feat-text em {
  display: flex; align-items: flex-start; gap: 7px; font: 600 10.5px/1.3 var(--mono); letter-spacing: .16em; text-transform: uppercase;
  color: rgb(var(--live)); text-wrap: balance;
}
.feat-text em::before {
  content: ""; flex: none; width: 5px; height: 5px; margin-top: calc((1.3em - 5px) / 2);   /* the middle of the first line */
  background: rgb(var(--live)); box-shadow: 0 0 6px rgba(var(--live), .9);
  animation: live 2.4s ease-in-out infinite;
}
.feat-text strong { font-size: 22px; font-weight: 600; line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feat-text small { font: 11px var(--mono); color: var(--muted); }
.city-featured:not([aria-current]):hover, .city-featured:not([aria-current]):focus-visible { background: linear-gradient(90deg, rgba(var(--live), .16), rgba(255, 255, 255, .06) 70%); }
@media (prefers-reduced-motion: reduce) { .feat-text em::before { animation: none; } }
/* tonight's city (site/js/ui/tonight.js): "tonight" by a moon-pale pixel, dark on the preview's other corner from the
   live count (the featured card: under its local time), on the globe's card too; on the globe a pixel before its name */
.tonight-tag, .globe-label.tonight { --moon: 255, 226, 160; }
.tonight-tag {
  position: absolute; top: 9px; right: 9px; display: flex; align-items: center; gap: 5px;
  padding: 0 7px 0 6px; border-radius: 7px; border: 1px solid rgba(var(--moon), .3); background: rgba(6, 7, 18, .78);
  font: 500 10.5px/18px var(--mono); color: var(--text); white-space: nowrap; pointer-events: none;
}
.tonight-tag::before, .globe-label.tonight::before { content: ""; flex: none; width: 5px; height: 5px; background: rgb(var(--moon)); box-shadow: 0 0 6px rgba(var(--moon), .9); }
.feat-text .tonight-tag { position: static; align-self: flex-start; }
.globe-stage.small .globe-card .tonight-tag { right: auto; left: 9px; }   /* on its preview, at the card's left */
.globe-label.tonight::before { width: 4px; height: 4px; }
/* the last card: "Request a city" (a link to the site's X account), a lit pixel billboard (site/js/ui/request-board.js) glowing in the city's accent */
a.city-card { color: inherit; text-decoration: none; }
.city-request { grid-template-columns: minmax(0, 1fr); }   /* "on X" goes under the title: beside it, a 150 px card cuts the title */
.city-request small { padding-left: 3px; }
.req-art {
  grid-column: 1 / -1; aspect-ratio: 16 / 9; margin-bottom: 5px; border-radius: 8px; overflow: hidden;
  background: radial-gradient(90% 80% at 50% 35%, color-mix(in srgb, var(--accent) 18%, #0c0e1d), #06070e 75%);
}
.req-art canvas {
  display: block; width: 100%; height: 100%; image-rendering: pixelated;
  --glow: color-mix(in srgb, var(--accent) 55%, #ffd98a);
  filter: drop-shadow(0 0 3px color-mix(in srgb, var(--glow) 70%, transparent));
  animation: req-glow 3.2s ease-in-out infinite;
}
@keyframes req-glow { 50% { filter: drop-shadow(0 0 6px var(--glow)); } }
.city-request:hover .req-art canvas, .city-request:focus-visible .req-art canvas { animation: none; filter: drop-shadow(0 0 7px var(--glow)); }
@media (prefers-reduced-motion: reduce) { .req-art canvas { animation: none; } }
/* Globe | Grid in the picker's head (site/js/app.js setPickerView), kept for the next visit */
#cityPicker .pop-head { align-items: center; padding-right: 10px; }
#cityPicker .pop-head small { flex: 1; }
.view-switch { display: flex; flex: none; padding: 2px; border-radius: 9px; border: 1px solid var(--line); background: rgba(255, 255, 255, .04); }
.view-switch button {
  display: flex; align-items: center; gap: 6px; padding: 4px 10px; border: 0; border-radius: 7px; background: transparent;
  font: 500 11.5px var(--mono); color: var(--muted); cursor: pointer;
}
.view-switch svg { width: 10px; height: 10px; fill: currentColor; shape-rendering: crispEdges; }
.view-switch button:hover { color: var(--text); }
.view-switch button[aria-pressed="true"] { background: rgba(255, 255, 255, .1); color: var(--text); }
#cityList[hidden] { display: none; }
/* the globe (site/js/ui/globe.js): the picker takes all its room and the globe as much of it as fits, in whole pixels */
#cityPicker.globe-on { height: min(600px, calc(var(--dock-top, 80vh) - var(--top) - 8px)); }
.globe-view { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 0 10px 8px; }
.globe-view[hidden] { display: none; }
.globe-room { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; }
.globe-stage { position: relative; flex: none; user-select: none; -webkit-user-select: none; }
.globe-surface { position: relative; border-radius: 50%; cursor: grab; touch-action: none; }
.globe-surface:focus-visible { outline: 2px solid color-mix(in srgb, var(--accent) 60%, transparent); outline-offset: 4px; }
.globe-surface canvas { display: block; border-radius: 50%; image-rendering: pixelated; }
.globe-stage.dragging .globe-surface { cursor: grabbing; }
.globe-stage.over-pin .globe-surface { cursor: pointer; }
.globe-stage::after {   /* zoomed in, the Earth fills a round lens (--lens, 0 to 1) */
  content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; opacity: var(--lens, 0);
  box-shadow: inset 0 0 0 2px rgba(142, 193, 255, .45), inset 0 0 28px rgba(5, 6, 15, .7), 0 0 26px rgba(142, 193, 255, .18);
}
.globe-labels { position: absolute; inset: 0; pointer-events: none; }
.globe-label {   /* a city's name: hovering or tapping it works like its pin */
  position: absolute; left: 0; top: 0; display: flex; align-items: center; gap: 5px; padding: 0 6px; border-radius: 6px;
  background: rgba(6, 7, 18, .72); font: 500 10.5px/17px var(--mono); color: var(--text); white-space: nowrap;
  pointer-events: auto; cursor: pointer;
}
.globe-label[hidden] { display: none; }
.globe-label.here { color: var(--accent); }
.globe-label.top { box-shadow: 0 0 0 1px rgba(var(--live), .4), 0 0 10px rgba(var(--live), .3); }
.globe-label.dim { opacity: .4; }
.globe-label.active { background: rgba(255, 255, 255, .16); box-shadow: 0 0 0 1px rgba(255, 255, 255, .35); }
.globe-label b { display: flex; align-items: center; gap: 4px; font-weight: 500; color: rgb(var(--live)); }
.globe-label b[hidden] { display: none; }
.globe-label b::before { content: ""; width: 4px; height: 4px; background: rgb(var(--live)); box-shadow: 0 0 5px rgba(var(--live), .9); }
.globe-card {   /* the city under the pointer (its pin or name), or tapped: its preview, local time and who's there */
  position: absolute; left: 0; top: 0; z-index: 2; width: 178px; display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline; gap: 0 8px; padding: 4px 4px 7px; border-radius: 12px; border: 1px solid var(--line);
  background: rgba(10, 11, 28, .92); box-shadow: 0 14px 30px -12px rgba(0, 0, 0, .9); color: var(--text); text-align: left; cursor: pointer;
}
.globe-card[hidden] { display: none; }
.globe-card.here { border-color: color-mix(in srgb, var(--accent) 75%, transparent); }
.globe-card img { grid-column: 1 / -1; display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; margin-bottom: 5px; border-radius: 8px; background: rgba(255, 255, 255, .06); }
.globe-card span { padding-left: 3px; font-weight: 560; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.globe-card small { padding-right: 3px; font: 11px var(--mono); color: var(--muted); }
.globe-card i { grid-column: 1 / -1; padding: 1px 3px 0; font: normal 10.5px var(--mono); color: var(--muted); }
.globe-card em { grid-column: 1 / -1; padding: 3px 3px 0; font: normal 10.5px var(--mono); color: var(--muted); }
.globe-live {   /* who's there: "5 here", "just you", or "no one here right now" (site/js/ui/globe.js liveLine) */
  grid-column: 1 / -1; display: flex; align-items: center; gap: 6px; padding: 4px 3px 0;
  font: 500 11px var(--mono); color: rgb(var(--live));
}
.globe-live[hidden] { display: none; }
.globe-live::before { content: ""; flex: none; width: 5px; height: 5px; background: rgb(var(--live)); box-shadow: 0 0 6px rgba(var(--live), .9); }
.globe-live.none { color: var(--muted); }
.globe-live.none::before { background: var(--muted); box-shadow: none; opacity: .6; }
.globe-stage.small .globe-card {   /* a small globe: a short card, the preview beside the name (above or below the pin) */
  width: 236px; grid-template-columns: 100px minmax(0, 1fr) auto; align-items: center; gap: 2px 8px; padding: 4px;
}
.globe-stage.small .globe-card img { grid-column: 1; grid-row: 1 / 5; width: 100px; height: 60px; aspect-ratio: auto; margin: 0; object-fit: cover; }
.globe-stage.small .globe-card span { grid-column: 2; grid-row: 1; align-self: end; }
.globe-stage.small .globe-card small { grid-column: 3; grid-row: 1; align-self: end; }
.globe-stage.small .globe-card i { grid-column: 2 / 4; grid-row: 2; padding: 0 3px; }
.globe-stage.small .globe-card .globe-live { grid-column: 2 / 4; grid-row: 3; padding: 0 3px; }
.globe-stage.small .globe-card em { grid-column: 2 / 4; grid-row: 4; align-self: start; padding: 0 3px; }
.globe-zoom { position: absolute; right: 0; bottom: 0; z-index: 1; display: flex; flex-direction: column; gap: 4px; }
.globe-zoom button {
  width: 30px; height: 30px; border-radius: 9px; border: 1px solid var(--line); background: rgba(22, 24, 48, .92);
  color: var(--text); font: 16px/1 var(--mono); cursor: pointer;
}
.globe-zoom button:hover { background: rgba(40, 43, 80, .95); }
@media (pointer: coarse) { .globe-zoom button { width: 38px; height: 38px; } }
@media (max-width: 420px) { #cityPicker .pop-head small { display: none; } }   /* the Globe | Grid switch needs the room */
/* the city passport (site/js/ui/passport.js): a city with 5 minutes of listening wears its stamp in the stamp's ink, a
   tilted postmark with a tick on its preview in the grid and the globe's card, a pixel one before its name on the globe
   and in the stamp's toast. Passport, before Globe | Grid, opens the Passport view (site/js/app.js renderPassport) */
:root { --stamp: #ff9f8a; --paper: #efe4d2; }
.stamp-icon { flex: none; width: 8px; height: 8px; fill: var(--stamp); shape-rendering: crispEdges; }
.globe-label .stamp-icon { display: none; }
.globe-label.stamped .stamp-icon { display: block; }
#toastText .stamp-icon { display: inline-block; margin-right: 8px; vertical-align: -1px; }
#passportBtn {   /* in the ink's colour, the count of stamps after its name; pressed while its view shows */
  flex: none; display: flex; align-items: center; gap: 6px; margin-right: auto; padding: 4px 10px; border-radius: 9px;
  border: 1px solid color-mix(in srgb, var(--stamp) 38%, transparent); background: color-mix(in srgb, var(--stamp) 7%, transparent);
  font: 500 11.5px var(--mono); color: var(--text); white-space: nowrap; cursor: pointer;
}
#passportBtn b { font-weight: 500; color: var(--stamp); }
#passportBtn:hover { background: color-mix(in srgb, var(--stamp) 15%, transparent); }
#passportBtn[aria-pressed="true"] { border-color: color-mix(in srgb, var(--stamp) 75%, transparent); background: color-mix(in srgb, var(--stamp) 22%, transparent); }
:is(.city-card, .globe-card) .card-stamp {   /* a box over the preview (16:9 inside the card's padding), the postmark in its bottom-right corner */
  position: absolute; grid-area: auto; top: 4px; left: 4px; right: 4px; aspect-ratio: 16 / 9; padding: 6px;   /* no grid area: the card's padding box */
  display: flex; align-items: flex-end; justify-content: flex-end; pointer-events: none;
}
:is(.city-card, .globe-card) .card-stamp[hidden] { display: none; }
.postmark {   /* a round ink stamp, a thin ring inside its rim, tilted; "stamped" under the tick in the Passport view */
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  width: 30px; height: 30px; border-radius: 50%; border: 2px solid var(--stamp); color: var(--stamp);
  background: rgba(6, 7, 18, .82); box-shadow: inset 0 0 0 2px rgba(6, 7, 18, .82), inset 0 0 0 3px color-mix(in srgb, var(--stamp) 60%, transparent);
  transform: rotate(-14deg);
}
.postmark svg { width: 16px; height: 10px; fill: currentColor; shape-rendering: crispEdges; }
.postmark span { display: none; font: 700 7.5px/1 var(--mono); letter-spacing: .16em; text-transform: uppercase; }
.city-card.city-featured .card-stamp { top: 8px; left: auto; right: 8px; aspect-ratio: auto; padding: 0; }   /* the preview is its first column: the text's corner */
.globe-stage.small .globe-card .card-stamp { grid-area: auto; right: auto; width: 100px; height: 60px; aspect-ratio: auto; padding: 4px; }
.globe-stage.small .globe-card .postmark { width: 24px; height: 24px; }
.globe-stage.small .globe-card .postmark svg { width: 12px; height: 7.5px; }
/* the Passport view: what the stamps are across the top, then every city's card as a postage stamp (the picker's
   search and regions still apply), the stamped ones first. Not stamped yet: faded, a bar along the preview's foot and
   "2 of 5 min" where the local time was; stamped: in colour, a bigger postmark and its time there */
.passport-intro {
  grid-column: 1 / -1; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 4px 14px;
  margin: 0 0 6px; padding: 12px 14px; border-radius: 12px; border: 1px dashed color-mix(in srgb, var(--stamp) 45%, transparent);
  background: color-mix(in srgb, var(--stamp) 6%, transparent);
}
.passport-intro[hidden] { display: none; }
.passport-intro svg { grid-row: 1 / 4; width: 32px; height: 32px; fill: var(--stamp); shape-rendering: crispEdges; }
.passport-intro strong { font: 600 11px/1.3 var(--mono); letter-spacing: .22em; text-transform: uppercase; color: var(--stamp); }
.passport-intro p { margin: 0; font-size: 14px; line-height: 1.35; color: var(--text); }
.passport-intro small { font: 11px/1.45 var(--mono); color: var(--muted); }
.passport-save {   /* signed out: sign in to keep the stamps on every device (site/js/live/account.js) */
  grid-column: 2; justify-self: start; margin-top: 6px; padding: 3px 10px; font: 11px var(--mono); color: var(--text); cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--stamp) 50%, transparent); border-radius: 8px; background: color-mix(in srgb, var(--stamp) 12%, transparent);
}
.passport-save:hover { background: color-mix(in srgb, var(--stamp) 24%, transparent); }
.card-pass, .card-bar { display: none; }
.passport-on :is(.card-live, .tonight-tag), .passport-on .city-card > small { display: none; }
.passport-on .card-pass { display: block; grid-column: 2; grid-row: 2; padding-right: 3px; font: normal 11px var(--mono); color: var(--muted); white-space: nowrap; }
.passport-on .city-card.stamped .card-pass { color: var(--stamp); }
.passport-on .city-card img {   /* a postage stamp: a paper rim with a perforated edge round the preview */
  padding: 4px; border-radius: 0; background: var(--paper); object-fit: cover;
  -webkit-mask: radial-gradient(circle 2px, #0000 95%, #000) -3px -3px / 6px 6px round, linear-gradient(#000 0 0) content-box;
  mask: radial-gradient(circle 2px, #0000 95%, #000) -3px -3px / 6px 6px round, linear-gradient(#000 0 0) content-box;
}
.passport-on .city-card:not(.stamped) img { filter: grayscale(1) brightness(.5); }
.passport-on .city-card:not(.stamped):is(:hover, :focus-visible) img { filter: grayscale(.5) brightness(.75); }
.passport-on .city-card:not(.stamped) span { color: var(--muted); }
.passport-on .card-bar { display: block; position: absolute; top: 4px; left: 4px; right: 4px; aspect-ratio: 16 / 9; pointer-events: none; }
.passport-on .card-bar::after {   /* the time so far (--p, 0 to 1) out of the 5 minutes */
  content: ""; position: absolute; left: 10px; right: 10px; bottom: 10px; height: 4px; border-radius: 2px; box-shadow: 0 0 0 1px rgba(6, 7, 18, .7);
  background: linear-gradient(90deg, var(--stamp) calc(var(--p, 0) * 100%), rgba(255, 255, 255, .25) 0);
}
.passport-on .city-card.stamped .card-bar { display: none; }
.passport-on .card-stamp { align-items: center; padding: 0 12px; }
.passport-on .postmark { width: 60px; height: 60px; box-shadow: inset 0 0 0 3px rgba(6, 7, 18, .82), inset 0 0 0 4px color-mix(in srgb, var(--stamp) 60%, transparent); }
.passport-on .postmark span { display: block; }
@media (max-width: 420px) {
  #cityPickerTitle:has(~ #passportBtn:not([hidden])) { display: none; }   /* Passport and Globe | Grid need the row (it still names the dialog) */
  .passport-on .postmark { width: 54px; height: 54px; }
  .passport-on .postmark span { letter-spacing: .08em; }
  .passport-intro svg { width: 24px; height: 24px; }
}
/* the leaderboards (site/js/live/board.js): the trophy in the player opens their panel beside Together and Chat
   (site/js/app.js renderBoard). This week | All time in its head, a chip per board under it (the region chips' look),
   the signed-in listeners in their chat names' colours, the top three's places in gold, silver and bronze, the
   visitor's own row lit (off the top: last, after a gap), then where they are or Sign in */
:root { --gold: #ffd36e; }
#board { width: min(440px, 100%); }   /* the five chips in one row, a long city's fans too */
#board .pop-head { align-items: center; padding-right: 10px; }
.board-tabs { display: flex; flex: none; padding: 2px; border-radius: 9px; border: 1px solid var(--line); background: rgba(255, 255, 255, .04); }
.board-tabs button { padding: 4px 10px; border: 0; border-radius: 7px; background: transparent; font: 500 11.5px var(--mono); color: var(--muted); cursor: pointer; }
.board-tabs button:hover { color: var(--text); }
.board-tabs button[aria-pressed="true"] { background: rgba(255, 255, 255, .1); color: var(--text); }
.board-types { margin: 2px 14px 8px; }
.board-list { list-style: none; margin: 0; padding: 0 8px; max-height: min(320px, 40dvh); overflow-y: auto; overscroll-behavior: contain; }
.board-row {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) auto 70px; align-items: center; gap: 10px;
  padding: 6px 8px 6px 2px; border-radius: 9px; font: 12.5px var(--mono); color: var(--text);
}
.board-row:nth-child(odd) { background: rgba(255, 255, 255, .03); }
.board-row.me { background: color-mix(in srgb, var(--accent) 16%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent); }
.board-row.gap { position: relative; margin-top: 16px; }
.board-row.gap::before { content: "\00B7\00B7\00B7"; position: absolute; left: 0; right: 0; top: -15px; text-align: center; font: 12px/14px var(--mono); color: var(--muted); }
.board-place { font-weight: 600; text-align: right; color: var(--muted); }
.board-place[data-place="1"] { color: var(--gold); }
.board-place[data-place="2"] { color: #d6dcea; }
.board-place[data-place="3"] { color: #e3a46f; }
.board-name { min-width: 0; font-weight: 600; color: hsl(var(--h, 230) 70% 76%); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.board-name small { margin-left: 6px; font: 400 11px var(--mono); color: var(--muted); }
.board-extra { display: flex; align-items: center; gap: 5px; font: 11px var(--mono); color: var(--muted); white-space: nowrap; }
.board-value { display: flex; align-items: center; justify-content: flex-end; gap: 5px; font-style: normal; white-space: nowrap; }
:is(.board-extra, .board-value).stamp { color: var(--stamp); }
.board-empty { margin: 0; padding: 16px 14px; text-align: center; font: 12px/1.5 var(--mono); color: var(--muted); }
.board-empty[hidden] { display: none; }
.board-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 8px 14px 0; padding: 10px 0 12px; border-top: 1px solid var(--line); }
.board-foot p { flex: 1 1 200px; margin: 0; font: 11.5px/1.5 var(--mono); color: var(--muted); }
.board-foot small { display: block; font: 11px/1.5 var(--mono); color: var(--muted); opacity: .75; }   /* when the week starts again, or what keeps a streak */
.board-foot small:empty { display: none; }
.board-foot [hidden] { display: none; }
@media (max-width: 420px) {
  .board-row { grid-template-columns: 22px minmax(0, 1fr) auto 62px; gap: 8px; }
}
@media (max-width: 360px) { .board-tabs button { padding-inline: 7px; } }
.globe-foot { display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 14px; margin: 6px 0 0; font: 11px var(--mono); color: var(--muted); }
.globe-request { color: var(--muted); text-decoration: underline; text-decoration-color: rgba(255, 255, 255, .25); text-underline-offset: 3px; }
.globe-request:hover, .globe-request:focus-visible { color: var(--text); }
.pop-list { list-style: none; margin: 0; padding: 0 6px 6px; overflow-y: auto; overscroll-behavior: contain; }
.pop-item {
  width: 100%; display: flex; align-items: center; gap: 10px; padding: 8px; text-align: left;
  border: 0; border-radius: 10px; background: transparent; cursor: pointer;
}
.pop-item:hover, .pop-item:focus-visible { background: rgba(255, 255, 255, .08); }
.pop-text { flex: 1; min-width: 0; }
.pop-title, .pop-meta { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pop-title { font-weight: 560; }
.pop-meta { font: 11px var(--mono); color: var(--muted); }
.q-n { flex: none; width: 18px; display: grid; place-items: center; font: 11px var(--mono); color: var(--muted); }
.q-n svg { display: none; width: 10px; height: 10px; fill: var(--accent); shape-rendering: crispEdges; }
.pop-item:hover .q-n b, .pop-item:focus-visible .q-n b { display: none; }
.pop-item:hover .q-n svg, .pop-item:focus-visible .q-n svg { display: block; }
li:first-child .q-n { color: var(--accent); }
.q-dur { flex: none; font: 11px var(--mono); color: var(--muted); }
.q-empty, .mix-empty { padding: 10px 8px 12px; font: 12px var(--mono); color: var(--muted); }
.pick-dot { flex: none; width: 18px; display: grid; place-items: center; }
[aria-checked="true"] > .pick-dot::before { content: ""; width: 6px; height: 6px; background: var(--accent); }

/* timers: focus presets and sleep minutes, with skip / stop / off pushed to the right */
.t-row { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 14px 12px; }
.t-row + .pop-head { border-top: 1px solid var(--line); }
.t-opt {
  min-width: 44px; min-height: 32px; padding: 0 10px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--line); background: transparent; font: 12px var(--mono); color: var(--text);
  transition: border-color .2s, background .2s, color .2s;
}
.t-opt:hover { border-color: rgba(255, 255, 255, .22); }
.t-opt[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--accent) 75%, transparent); color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.t-act { border-color: transparent; color: var(--muted); }
.t-act:hover { border-color: transparent; background: rgba(255, 255, 255, .08); color: var(--text); }
.t-opt:not(.t-act) + .t-act { margin-left: auto; }

/* running timers: stay on screen when the UI idles (sliding up into the corner #links leaves), hidden by H */
#timerPill { display: flex; gap: 14px; cursor: pointer; transition: opacity .6s ease, border-color .2s; }
#timerPill span { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
#timerPill span::before { content: ""; flex: none; width: 6px; height: 6px; background: var(--accent); }
#timerPill span:empty { display: none; }
#timerPill:hover { border-color: rgba(255, 255, 255, .22); }
body.idle #corner { top: var(--top); }
body.idle #timerPill { opacity: .6; }
body.ui-off #timerPill { opacity: 0; pointer-events: none; }

.mix-empty { margin: 0; padding-inline: 0; }

/* mixer rows: name · slider (0-200 %, 100 % = as the city was designed) · value */
#mixReset {
  margin: -4px -6px; padding: 4px 6px; border: 0; border-radius: 6px; background: transparent; cursor: pointer;
  font: 11px var(--mono); color: var(--muted);
}
#mixReset:hover:not(:disabled) { color: var(--text); background: rgba(255, 255, 255, .08); }
#mixReset:disabled { opacity: .4; cursor: default; }
#mixList { padding: 2px 14px 10px; overflow-y: auto; overscroll-behavior: contain; }
.mix-row { display: grid; grid-template-columns: 78px minmax(0, 1fr) 36px; align-items: center; gap: 10px; min-height: 32px; }
.mix-sep { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line); }   /* events after the beds */
.mix-name { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mix-row input { width: 100%; min-width: 0; margin: 0; accent-color: var(--accent); }
.mix-val { font: 11px var(--mono); color: var(--muted); text-align: right; }
.mix-row.changed .mix-val { color: var(--accent); }

/* vibe: three segmented radio groups (native radios underneath, so arrow keys move within a group) */
/* never taller than the room above the dock (100% is the dock, 8px the gap under the panel): on short screens it scrolls */
#vibe { padding-bottom: 12px; max-height: calc(100dvh - var(--top) - 100% - 8px - var(--bottom)); overflow-y: auto; overscroll-behavior: contain; }
#vibe fieldset, #settings fieldset { min-width: 0; margin: 0; padding: 6px 14px 4px; border: 0; }
#vibe legend, #settings legend { padding: 0; margin-bottom: 6px; font: 11px var(--mono); color: var(--muted); }
.seg { display: flex; gap: 2px; padding: 3px; border-radius: 12px; border: 1px solid var(--line); background: rgba(255, 255, 255, .05); }
.seg-grid { display: grid; grid-template-columns: repeat(3, 1fr); }   /* three to a row */
.seg label { position: relative; flex: 1; min-width: 0; }
.seg input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.seg label > span {
  display: block; padding: 7px 4px; border-radius: 9px; text-align: center; font-size: 13px; white-space: nowrap;
  color: var(--muted); transition: background .15s, color .15s, box-shadow .15s;
}
.seg label:hover > span { color: var(--text); }
.seg input:checked + span {
  color: var(--text); background: color-mix(in srgb, var(--accent) 20%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 60%, transparent);
}
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }
/* the styles, as the switch beside the legend lays them out (#vibeStyle[data-view], .st-views): grouped, three groups of pixel-icon tiles
   (icon over name); list, one box of two columns (icon beside name); card, the picked style's card over one box of icons
   six to a row (names for screen readers and in the tooltip). The line under the tiles says what the picked one is.
   The switch looks like the city picker's Globe | Grid. */
#vibeStyle { position: relative; }
/* the legend floats, so it sits in the fieldset's box (a rendered legend sits on the border, out of it) with the switch
   level beside it, as tall as the switch */
#vibeStyle legend { float: left; line-height: 20px; }
#vibeStyle > .st-card, #vibeStyle > .st-picker { clear: left; }
.st-views { position: absolute; top: 5px; right: 14px; }
.st-views button { padding: 3px 7px; }
.st-group { margin: 10px 2px 5px; font: 10px var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.st-picker > .st-group:first-child { margin-top: 0; }
.st-grid label > span { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 8px 2px 6px; font-size: 12px; }
#vibeStyle:not([data-view="grouped"]) .st-picker {
  display: grid; gap: 2px; padding: 3px; border-radius: 12px; border: 1px solid var(--line); background: rgba(255, 255, 255, .05);
}
#vibeStyle:not([data-view="grouped"]) .st-group { display: none; }
#vibeStyle:not([data-view="grouped"]) .st-grid { display: contents; }
/* Mix: grouped, one wide tile over the groups (icon beside its name); list and card, the first cell like any style */
#vibeStyle[data-view="grouped"] .st-mix { margin-bottom: 2px; }
#vibeStyle[data-view="grouped"] .st-mix label > span { flex-direction: row; justify-content: center; gap: 9px; padding: 6px 8px; }
#vibeStyle[data-view="list"] .st-picker { grid-template-columns: repeat(2, minmax(0, 1fr)); }
#vibeStyle[data-view="list"] .st-grid label > span { flex-direction: row; gap: 9px; padding: 5px 8px; }
#vibeStyle[data-view="card"] .st-picker { grid-template-columns: repeat(6, minmax(0, 1fr)); margin-bottom: 6px; }   /* no line under it */
#vibeStyle[data-view="card"] .st-grid label > span { padding: 7px 0; }
#vibeStyle[data-view="card"] .st-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
#vibeStyle[data-view="card"] .st-desc, .st-card { display: none; }
#vibeStyle[data-view="card"] .st-card {
  display: flex; align-items: center; gap: 12px; min-height: 76px; margin-bottom: 8px; padding: 10px 12px; border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 12%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.st-card canvas { flex: none; width: 48px; height: 48px; image-rendering: pixelated; }
.st-card p { margin: 0; min-width: 0; font: 11px/1.45 var(--mono); color: var(--muted); }
.st-card b { display: block; margin-bottom: 3px; font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 15px; font-weight: 500; line-height: 1.2; color: var(--text); }
.st-grid canvas { width: 24px; height: 24px; image-rendering: pixelated; opacity: .72; transition: opacity .15s, transform .15s; }
.st-grid label:hover canvas, .st-grid input:checked + span canvas { opacity: 1; }
.st-grid input:checked + span canvas { transform: translateY(-1px); }
.st-desc { margin: 8px 2px 2px; min-height: 3em; font: 11px/1.5 var(--mono); color: var(--muted); }
.st-desc b { font-weight: 500; color: var(--text); }
.vibe-note { margin: 8px 14px 0; font: 11px/1.5 var(--mono); color: var(--muted); }
/* the radio's switch heads the vibe (on the radio the station picks the style and energy: only the band is left) */
#radioSetting { padding-block: 6px 10px; margin-bottom: 4px; border-bottom: 1px solid var(--line); }
#radioName kbd { margin-left: 6px; }
.q-at { color: var(--accent); }                /* up next on the radio: when each track starts */
div.pop-item { cursor: default; }              /* a row to read, not to pick */
/* the radio's vote: over the scene just above the dock (--dock-top), where it stays while the interface idles; the
   share of each option's votes fills its button from the left */
.rv {
  position: fixed; left: 50%; top: calc(var(--dock-top, 100dvh) - 8px); transform: translate(-50%, -100%);
  width: min(360px, calc(100vw - 2 * var(--gutter))); padding: 10px 12px 10px;
  border-radius: 16px; border: 1px solid var(--line); background: rgba(10, 11, 28, 0.74);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  box-shadow: 0 18px 40px -20px rgba(0, 0, 0, .8); animation: pop-in .16s ease-out;
}
body.ui-off .rv, body:has(.pop:not([hidden])) .rv { display: none; }   /* a panel is open */
.rv-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 2px 2px 8px; }
.rv-head span { font: 600 11px/1 var(--mono); letter-spacing: .28em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.rv-time { font: 11px var(--mono); color: var(--accent); }
.rv.closed .rv-time { color: var(--muted); }
.rv-opts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.rv-opt {
  position: relative; isolation: isolate; overflow: hidden; display: flex; align-items: baseline; justify-content: space-between;
  gap: 6px; padding: 8px 10px; border-radius: 10px; border: 1px solid var(--line); background: rgba(255, 255, 255, .05);
  color: var(--text); font: inherit; font-size: 13px; cursor: pointer; transition: border-color .15s, opacity .2s;
}
.rv-opt::before {
  content: ""; position: absolute; inset: 0 auto 0 0; z-index: -1; width: calc(var(--share, 0) * 100%);
  background: color-mix(in srgb, var(--accent) 22%, transparent); transition: width .3s ease-out;
}
.rv-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rv-n { flex: none; font: 11px var(--mono); color: var(--muted); }
/* the vote's options are styles: each one's pixel icon over its name (site/js/ui/style-icons.js), the votes in the corner */
#radioVote .rv-opt { flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 8px 8px 7px; }
#radioVote .rv-name { max-width: 100%; }
#radioVote .rv-n { position: absolute; top: 5px; right: 7px; }
.rv-ico { width: 24px; height: 24px; image-rendering: pixelated; }
.rv-opt:hover:not(:disabled) { border-color: rgba(255, 255, 255, .22); }
.rv-opt[aria-pressed="true"], .rv-opt.won { border-color: color-mix(in srgb, var(--accent) 75%, transparent); }
.rv-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rv-opt:disabled { cursor: default; }
.rv.closed .rv-opt:not(.won) { opacity: .45; }
.rv-note { margin: 8px 2px 0; font: 11px/1.5 var(--mono); color: var(--muted); }
/* the suggestion cards (site/js/ui/nudge.js): the vote card's look and place (.rv), a close button in the head, an icon
   on each pick; the tour's one pick spans the row */
.nudge .rv-head { align-items: center; margin: -6px -8px 4px 2px; }
.nudge-close { color: var(--muted); }
.nudge-close:hover { color: var(--text); }
.nudge .rv-opt { justify-content: center; align-items: center; }
.nudge .rv-opt svg { flex: none; width: 14px; height: 14px; fill: var(--accent); shape-rendering: crispEdges; }
.nudge[data-kind="tour"] .rv-opts { grid-template-columns: minmax(0, 1fr); }
.nudge[data-kind="tour"] .rv-opt { justify-content: flex-start; }
.nudge[data-kind="tour"] .rv-n { margin-left: auto; }
div.pop-item:hover { background: transparent; }
/* a style or energy waiting for the next track (or no music yet): its note and Play button stay pinned to the panel's foot, so on
   phones (where the styles scroll) the button shows beside the tile just picked, not under Energy and Band */
.vibe-note:has(#vibeSkip:not([hidden])) {
  position: sticky; bottom: -12px; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 8px 0 -12px; padding: 10px 14px 12px; border-top: 1px solid var(--line); background: rgba(10, 11, 28, .94);
}
#vibeSkip {
  flex: none; padding: 6px 12px; border: 0; border-radius: 999px; background: var(--accent); font: 600 12px var(--mono);
  color: #12121c; cursor: pointer; white-space: nowrap;
}
#vibeSkip:hover { filter: brightness(1.1); }

/* save this scene: the S and R keys as buttons, so touch screens can reach them */
#saveMenu { left: auto; right: 0; width: min(300px, 100%); }
#saveMenu svg, #together .pop-item > svg { flex: none; width: 16px; height: 16px; fill: var(--accent); shape-rendering: crispEdges; }
#saveMenu a { color: var(--text); text-decoration: none; }

/* settings (the gear): switches for reactions and the job strip, and billboards all, city ads only or off, as the vibe's segmented rows */
#settings {   /* like the vibe: scrolls on short screens (phones held sideways) instead of running off the top */
  left: auto; right: 0; width: min(300px, 100%); padding-bottom: 12px;
  max-height: calc(100dvh - var(--top) - 100% - 8px - var(--bottom)); overflow-y: auto; overscroll-behavior: contain;
}
#settings > * { flex-shrink: 0; }                  /* scrolled, not squeezed: a switch row's min-height (touch) would let it shrink */
#settings legend kbd { margin-left: 6px; }
#settings .vibe-note { margin-inline: 0; }      /* each note sits in its setting's fieldset, already inset */
/* room between two settings shown together (a fieldset's padding would land under its legend) */
#settings .set-switch:not([hidden]) ~ .set-switch { padding-top: 14px; }
#settings .set-switch:not([hidden]) ~ fieldset { margin-top: 10px; }
.set-switch { display: flex; align-items: center; gap: 14px; padding: 4px 14px; cursor: pointer; }
/* the account: what signing in does (or who's signed in), then its buttons, then Delete's question in the row */
.set-account { padding: 4px 14px 12px; margin-bottom: 10px; border-bottom: 1px solid var(--line); }
.set-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.google-btn {   /* Google's sign-in button, light: its G on white, as Google's branding asks */
  display: inline-flex; align-items: center; gap: 10px; height: 34px; padding: 0 14px 0 10px; cursor: pointer;
  font: 500 13px/1 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; color: #1f1f1f;
  border: 1px solid #747775; border-radius: 17px; background: #fff;
}
.google-btn:hover { background: #f2f2f2; }
.google-btn:focus-visible, .set-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.google-btn svg { width: 18px; height: 18px; flex: none; }
.set-btn {
  padding: 5px 12px; font: 11px var(--mono); color: var(--text); cursor: pointer;
  border: 1px solid var(--line); border-radius: 8px; background: rgba(255, 255, 255, .06);
}
.set-btn:hover { background: rgba(255, 255, 255, .12); }
.set-btn:disabled { opacity: .5; cursor: default; }
.set-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 10px 0 0; font: 11px/1.5 var(--mono); color: var(--text); }
.set-confirm span { flex-basis: 100%; }
.set-danger { border-color: color-mix(in srgb, #ff6b6b 60%, transparent); background: color-mix(in srgb, #ff6b6b 16%, transparent); }
.set-danger:hover { background: color-mix(in srgb, #ff6b6b 28%, transparent); }
.set-text { flex: 1; min-width: 0; }
/* the account's chat name: a text box and Save, what it does (or why it wasn't saved) under it, and the way back */
.set-nick { margin-top: 12px; }
.set-label { display: block; margin-bottom: 5px; font: 11px/1.5 var(--mono); color: var(--text); }
.set-nick-row { display: flex; gap: 8px; }
#nickInput {
  flex: 1; min-width: 0; padding: 6px 10px; border-radius: 8px; border: 1px solid var(--line);
  background: rgba(255, 255, 255, .06); font: 13px var(--mono); color: var(--text);
}
#nickInput::placeholder { color: var(--muted); }
#nickInput:focus-visible { outline: none; border-color: color-mix(in srgb, var(--accent) 60%, transparent); }
#nickNote.warn { color: #ffb3a1; }
.set-link {
  margin-top: 4px; padding: 0; border: 0; background: none; cursor: pointer; font: 11px/1.5 var(--mono); color: var(--muted);
  text-decoration: underline dotted; text-underline-offset: 3px;
}
.set-link:hover, .set-link:focus-visible { color: var(--text); }
.set-note { display: block; margin-top: 3px; font: 11px/1.5 var(--mono); color: var(--muted); }
/* the native checkbox drawn as a track and knob: off grey, on in the accent with the knob to the right */
.set-switch input {
  appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 40px; height: 24px; margin: 0;
  border: 1px solid var(--line); border-radius: 12px; background: rgba(255, 255, 255, .08); cursor: pointer;
  transition: background .15s, border-color .15s;
}
.set-switch input::before {
  content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--muted); transition: transform .15s, background .15s;
}
.set-switch input:checked {
  background: color-mix(in srgb, var(--accent) 35%, transparent); border-color: color-mix(in srgb, var(--accent) 70%, transparent);
}
.set-switch input:checked::before { transform: translateX(16px); background: var(--text); }
.set-switch:hover input:not(:checked)::before { background: var(--text); }
.set-switch input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* together (README "Together"): who's here, four reactions and a lantern */
#together .pop-head small { color: var(--text); }
.tg-reacts { display: flex; gap: 6px; padding: 4px 14px 8px; }
.tg-react {   /* each in its own colour, the same as its sprite in site/js/live/reactions.js */
  flex: 1; min-height: 40px; display: grid; place-items: center; padding: 0; cursor: pointer;
  border: 1px solid var(--line); border-radius: 10px; background: rgba(255, 255, 255, .03);
  transition: border-color .2s, background .2s;
}
.tg-react[data-kind="heart"] { --c: #ff6b8b; }
.tg-react[data-kind="note"] { --c: #8fd3ff; }
.tg-react[data-kind="star"] { --c: #ffd95e; }
.tg-react[data-kind="moon"] { --c: #fff0b3; }
.tg-react:hover { border-color: color-mix(in srgb, var(--c) 55%, transparent); background: color-mix(in srgb, var(--c) 10%, transparent); }
/* the picked one (what a tap on the scene drops): lit in its colour, like a picked timer or vibe */
.tg-react[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--c) 75%, transparent); background: color-mix(in srgb, var(--c) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 40%, transparent);
}
.tg-react:active { background: color-mix(in srgb, var(--c) 22%, transparent); }
.tg-react svg { width: 21px; height: 21px; fill: var(--c); shape-rendering: crispEdges; }   /* 7 px art at 3x */
.tg-hint { margin: -2px 14px 8px; font: 11px/16px var(--mono); color: var(--muted); }
.tg-hint svg { width: 14px; height: 14px; vertical-align: -3px; fill: var(--c); shape-rendering: crispEdges; }   /* 7 px art at 2x */

/* chat (README "Chat"): the city's last hour, oldest first, a line to write in, and why a message wasn't sent */
#chatBtn { position: relative; }
#chatBtn.unread::after {   /* said while the panel was closed */
  content: ""; position: absolute; top: calc(50% - 11px); right: calc(50% - 13px); width: 6px; height: 6px;
  background: var(--accent); box-shadow: 0 0 6px var(--accent);
}
#chatBtn.mentioned::after { width: 8px; height: 8px; top: calc(50% - 12px); right: calc(50% - 14px); box-shadow: 0 0 0 2px rgba(255, 200, 110, .35), 0 0 8px var(--accent); }   /* and someone mentioned you */
#chat { width: min(380px, 100%); }
#chat .pop-head small { color: var(--text); }
/* "3 in chat" (site/js/live/chat.js inChat), lit like the live pill when anyone else has it open, then your name, which goes to a
   line of its own when both don't fit (a phone, a long name) */
#chatWho { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: baseline; gap: 2px 10px; min-width: 0; }
#chatWho span {   /* the header's text, not its title's look (.pop-head span) */
  min-width: 0; overflow: hidden; text-overflow: ellipsis; font: inherit; letter-spacing: normal; text-transform: none; color: var(--text);
}
#chatWho .chat-online { display: inline-flex; align-items: baseline; gap: 6px; color: var(--muted); }
#chatWho .chat-online::before { content: ""; flex: none; align-self: center; width: 5px; height: 5px; background: currentColor; opacity: .5; }
#chatWho .chat-online.on { color: rgb(var(--live)); text-shadow: 0 0 8px rgba(var(--live), .55); }
#chatWho .chat-online.on::before { opacity: 1; box-shadow: 0 0 6px rgba(var(--live), .9); animation: live 2.4s ease-in-out infinite; }
.chat-log {
  list-style: none; margin: 0; padding: 2px 14px 4px; display: flex; flex-direction: column; gap: 6px;
  max-height: min(300px, 38dvh); overflow-y: auto; overscroll-behavior: contain;
}
.chat-log:empty { display: none; }
.chat-msg { font-size: 13px; line-height: 18px; overflow-wrap: anywhere; }
.chat-name { margin-right: 6px; font: 600 11px/18px var(--mono); color: hsl(var(--h, 230) 70% 76%); }
.chat-msg.mine .chat-name { color: var(--accent); }
.chat-mark { margin-left: 3px; font-size: 10px; opacity: .8; }   /* after a chosen name (signed in): nobody else has it */
.chat-city { margin-right: 6px; font: 10.5px/18px var(--mono); color: var(--muted); }   /* the city its author was watching */
button.chat-city {   /* another city: a dotted underline says it goes somewhere, which touch screens can't hover to find */
  padding: 0; border: 0; background: none; cursor: pointer;
  text-decoration: underline dotted; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--muted) 60%, transparent);
}
button.chat-city:hover, button.chat-city:focus-visible { color: var(--text); text-decoration-style: solid; }
.chat-at { font-weight: 600; color: var(--text); }   /* "@sleepy fox" in a message */
.chat-at.me { padding: 0 2px; border-radius: 3px; color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent); }
.chat-msg.to-me { margin: 0 -6px; padding: 0 6px 0 4px; border-left: 2px solid var(--accent); background: color-mix(in srgb, var(--accent) 6%, transparent); }   /* a message that mentions you */
.chat-msg time { margin-left: 6px; font: 10.5px var(--mono); color: var(--muted); white-space: nowrap; }
/* one of the site's own posts (site/js/live/chat.js POSTS): set apart on a faint card, with the one link the chat has */
.chat-msg.chat-post { margin: 2px -6px; padding: 4px 6px; border-radius: 8px; background: rgba(255, 255, 255, .05); }
.chat-post .chat-name { color: var(--text); }
.chat-link {
  margin-left: 6px; white-space: nowrap; font: 600 11px/18px var(--mono); color: var(--accent);
  text-decoration: underline dotted; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--accent) 60%, transparent);
}
.chat-link:hover, .chat-link:focus-visible { text-decoration-style: solid; }
.chat-tr {   /* a translated message's "original" (and back to "translate"): always there, in blue, so it's clear it was translated */
  --tr: #8ec5ff;
  margin-left: 6px; padding: 0; border: 0; background: none; cursor: pointer; white-space: nowrap;
  font: 10.5px/18px var(--mono); color: var(--tr);
  text-decoration: underline dotted; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--tr) 60%, transparent);
}
.chat-tr:hover, .chat-tr:focus-visible { color: #c4e1ff; text-decoration-style: solid; }
.chat-reply, .chat-hide {   /* shown on hover and focus with a mouse; always, a little dim, on touch screens */
  margin-left: 4px; padding: 0 5px; border: 0; border-radius: 5px; background: transparent; cursor: pointer;
  font: 10.5px/18px var(--mono); color: var(--muted); opacity: 0;
}
.chat-reply { padding: 0 4px; }
.chat-reply + .chat-hide { margin-left: 0; }
.chat-msg:hover :is(.chat-reply, .chat-hide), .chat-reply:focus-visible, .chat-hide:focus-visible { opacity: 1; }
.chat-reply:hover, .chat-hide:hover { color: var(--text); background: rgba(255, 255, 255, .08); }
@media (hover: none) { .chat-reply, .chat-hide { opacity: .75; } }
.chat-empty { margin: 0; padding: 4px 14px 8px; font: 12px/1.5 var(--mono); color: var(--muted); }
.chat-form { position: relative; display: flex; gap: 6px; padding: 6px 8px 0 14px; }
#chatText {
  flex: 1; min-width: 0; padding: 8px 12px; border-radius: 10px; border: 1px solid var(--line);
  background: rgba(255, 255, 255, .06); font: inherit; font-size: 14px; color: var(--text);
}
#chatText::placeholder { color: var(--muted); }
#chatText:focus-visible { outline: none; border-color: color-mix(in srgb, var(--accent) 60%, transparent); }
#chatText:disabled { opacity: .6; }
#chatSend { color: var(--accent); }
#chatSend:disabled { color: var(--muted); }
.chat-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 0; padding: 6px 14px 10px; font: 11px/1.45 var(--mono); color: var(--muted); }
#chatNote.warn { color: #ffb3a1; }
#chatUnhide { flex: none; padding: 0; border: 0; background: none; font: inherit; color: var(--muted); cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
#chatUnhide:hover { color: var(--text); }
/* emoji (site/js/live/emoji.js): the smiley's popup between the log and the text box, which gives it some of its room, and the
   shortcode list over them, above the text box; drawn by the system's own emoji font */
#chat { --emoji-font: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; }
#chatEmojiBtn { color: var(--muted); }
#chatEmojiBtn:hover:not(:disabled) { color: var(--text); }
#chatEmojiBtn[aria-expanded="true"] { background: rgba(255, 255, 255, .1); color: var(--accent); }
#chat.picking .chat-log { max-height: min(140px, 18dvh); }
.chat-emoji { margin-top: 4px; padding: 0 8px 0 10px; border-top: 1px solid var(--line); }
.emoji-scroll { max-height: min(204px, 28dvh); overflow-y: auto; overscroll-behavior: contain; }
.emoji-sec[hidden] { display: none; }
.emoji-label { margin: 9px 4px 3px; font: 600 10px/1 var(--mono); letter-spacing: .24em; text-transform: uppercase; color: var(--muted); }
.emoji-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(36px, 1fr)); }
.emoji {
  height: 36px; padding: 0; border: 0; border-radius: 9px; background: transparent; cursor: pointer;
  font: 21px/1 var(--emoji-font); color: var(--text);
}
.emoji:hover, .emoji:focus-visible { outline: none; background: rgba(255, 255, 255, .1); }
.emoji:focus-visible { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 60%, transparent); }
.emoji-hint { margin: 0; padding: 6px 4px 2px; border-top: 1px solid var(--line); font: 11px/18px var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-suggest {
  position: absolute; left: 14px; bottom: calc(100% + 6px); z-index: 1; width: min(260px, calc(100% - 22px));
  list-style: none; margin: 0; padding: 4px; border-radius: 12px; border: 1px solid var(--line);
  background: rgba(14, 15, 34, .97); box-shadow: 0 12px 30px -12px rgba(0, 0, 0, .8);
}
.chat-suggest li {
  display: flex; align-items: center; gap: 10px; padding: 3px 8px; border-radius: 8px; cursor: pointer;
  font: 12px/26px var(--mono); color: var(--muted); white-space: nowrap;
}
.chat-suggest li span + span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.chat-suggest li:hover { color: var(--text); }
.chat-suggest li[aria-selected="true"] { background: rgba(255, 255, 255, .1); color: var(--text); }
.emoji-e { flex: none; width: 22px; text-align: center; font: 18px/1 var(--emoji-font); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }


/* status pills */
.pill {
  position: fixed; top: var(--top); padding: 8px 12px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--panel); font: 12px var(--mono); color: var(--text);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
/* top right, below #links: the running timers, then "tuning in…" */
#corner {
  position: fixed; top: calc(var(--top) + var(--btn) + 18px); right: max(18px, env(safe-area-inset-right));
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px; transition: top .6s ease;
}
#corner .pill { position: static; }
#loading { color: var(--muted); }
#toast {   /* its text, a button beside it (#toastAction, the text wraps first), an export's bar under both */
  left: 50%; transform: translateX(-50%); min-width: min(360px, calc(100vw - 32px)); max-width: calc(100vw - 32px);
  display: flex; flex-wrap: wrap; align-items: center; column-gap: 12px;
}
#toastText { flex: 1 1 0; min-width: 0; }
#toast .progress { flex-basis: 100%; display: flex; align-items: center; gap: 10px; margin-top: 6px; }
#toast .bar { flex: 1; height: 3px; border-radius: 2px; overflow: hidden; background: rgba(255, 255, 255, .12); }
#toast .bar i { display: block; height: 100%; width: 0; background: var(--accent); }
#toastCancel {
  flex: none; padding: 3px 10px; font: 11px var(--mono); cursor: pointer;
  border: 1px solid var(--line); border-radius: 8px; background: rgba(255, 255, 255, .06);
}
#toastCancel:hover { background: rgba(255, 255, 255, .12); }
.toast-action {   /* a toast's one step further, such as the stamp's See passport (and the first stamp's Save it) */
  flex: none; padding: 3px 10px; font: 11px var(--mono); color: var(--text); white-space: nowrap; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--stamp) 50%, transparent); border-radius: 8px; background: color-mix(in srgb, var(--stamp) 12%, transparent);
}
.toast-action:hover { background: color-mix(in srgb, var(--stamp) 24%, transparent); }
[hidden] { display: none !important; }

/* the city's billboard: an invisible button over its face (placed by site/js/app.js), lit up with a label on hover; while
   the face says "post a job for free", a link to jobs/post/ in its place; while it says "new cities on X" (and over the
   city's own ad while the pitch is hidden), a link to the site's X account; and during the Product Hunt launch week,
   while the face shows the launch, a link to it, lit in Product Hunt's orange */
:is(#pitchSpot, #phSpot, #jobSpot, #xSpot) { --spot: var(--accent); position: fixed; padding: 0; border: 0; border-radius: 3px; background: transparent; cursor: pointer; transition: box-shadow .2s; touch-action: pinch-zoom; }
:is(#phSpot, #jobSpot, #xSpot) { -webkit-user-drag: none; -webkit-touch-callout: none; }   /* links, yet a drag pans the view */
#phSpot { --spot: var(--ph); }
:is(#pitchSpot, #phSpot, #jobSpot, #xSpot) span {
  position: absolute; left: 50%; top: calc(100% + 8px); transform: translateX(-50%); padding: 5px 10px; border-radius: 10px;
  border: 1px solid var(--line); background: rgba(10, 11, 28, .78); font: 11px var(--mono); color: var(--text); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .2s;
}
@media (hover: hover) {
  :is(#pitchSpot, #phSpot, #jobSpot, #xSpot):hover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--spot) 70%, transparent), 0 0 28px -6px var(--spot); }
  :is(#pitchSpot, #phSpot, #jobSpot, #xSpot):hover span { opacity: 1; }
}
:is(#pitchSpot, #phSpot, #jobSpot, #xSpot):focus-visible span { opacity: 1; }
body.idle :is(#pitchSpot, #phSpot, #jobSpot, #xSpot), body.ui-off :is(#pitchSpot, #phSpot, #jobSpot, #xSpot) { box-shadow: none; }
body.idle :is(#pitchSpot, #phSpot, #jobSpot, #xSpot) span, body.ui-off :is(#pitchSpot, #phSpot, #jobSpot, #xSpot) span { opacity: 0; }
/* the What's new pigeon: an invisible button over it while it sits on the billboard (placed by site/js/app.js, at least
   44 px square); hover or focus makes its letter shine brighter (site/js/ui/pigeon.js) and shows a label above it */
#newsSpot { position: fixed; padding: 0; border: 0; border-radius: 8px; background: transparent; cursor: pointer; touch-action: pinch-zoom; }
#newsSpot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#newsSpot span {
  position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translateX(-50%); padding: 5px 10px; border-radius: 10px;
  border: 1px solid var(--line); background: rgba(10, 11, 28, .78); font: 11px var(--mono); color: var(--text); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .2s;
}
@media (hover: hover) { #newsSpot:hover span { opacity: 1; } }
#newsSpot:focus-visible span { opacity: 1; }
/* with the billboards off it sits on the player: #newsBird, a canvas of scene pixels in the dock (site/js/app.js
   drawDockPigeon), first there so the chips and the player cover the glow under its feet. It idles away with the dock
   and, like the cards over the dock (.rv), is gone while a panel is open; the What's new card it opens lets it fly off.
   Its button (.docked) goes over the row of chips, and with the pigeon. */
#newsBird { position: absolute; pointer-events: none; image-rendering: pixelated; }
body:has(.pop:not([hidden], #news)) #newsBird { visibility: hidden; }
#newsSpot.docked { z-index: 1; }
body:is(.idle, .ui-off) #newsSpot.docked { pointer-events: none; }
body:has(.pop:not([hidden])) #newsSpot.docked { display: none; }

/* What's new: the lines the pigeon brought as bullets under a heading per day (newest first), All updates and, for
   lines that went live after the page loaded, a reload */
#news { width: min(360px, 100%); max-height: min(440px, 60vh); }
#news:focus { outline: none; }                     /* focused as it opens (its button flies off), not a control */
#news .pop-head { align-items: center; padding: 6px 6px 2px 14px; }
#news .pop-head small { flex: 1; }
#newsClose { color: var(--muted); }
#newsClose:hover { color: var(--text); }
.news-days { overflow-y: auto; overscroll-behavior: contain; padding: 4px 14px 10px; }
.news-days section + section { margin-top: 12px; }
.news-days h3 { margin: 0 0 6px; font: 600 10px/1.4 var(--mono); letter-spacing: .2em; text-transform: uppercase; color: var(--accent); }
.news-days ul { margin: 0; padding-left: 16px; display: grid; gap: 7px; }
.news-days li { font-size: 13px; line-height: 1.45; }
.news-days li::marker { color: var(--muted); }
.news-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0; padding: 10px 14px 12px; border-top: 1px solid var(--line); font: 12px var(--mono); }
.news-foot a { color: var(--muted); text-decoration: none; }
.news-foot a:hover, .news-foot a:focus-visible { color: var(--text); }
#newsReload {
  padding: 6px 12px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--accent) 60%, transparent);
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text); font: inherit; cursor: pointer;
}
#newsReload:hover { background: color-mix(in srgb, var(--accent) 28%, transparent); }

/* about panel (the text inside is written by tools/seo/seo.mjs). site/js/app.js makes it tabbed (.tabbed): a fixed-height panel,
   the tabs and close button on top, one section below that scrolls on its own. Without JS the sections simply stack. */
#about {
  width: min(640px, calc(100vw - 24px)); max-height: min(860px, calc(100dvh - 32px)); padding: 0; overflow: auto;
  overscroll-behavior: contain; border: 1px solid var(--line); border-radius: 16px; color: var(--text);
  background: rgba(10, 11, 28, .86); -webkit-backdrop-filter: blur(18px) saturate(1.3); backdrop-filter: blur(18px) saturate(1.3);
  box-shadow: 0 24px 80px -20px rgba(0, 0, 0, .8); scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .18) transparent;
}
#about.tabbed { height: min(820px, calc(100dvh - 32px)); overflow: hidden; }   /* same size on every tab, so nothing jumps */
#about::backdrop { background: rgba(3, 4, 12, .5); }
.about-inner { padding: 22px 24px 26px; }
.tabbed .about-inner { height: 100%; padding: 0; display: grid; grid-template: auto minmax(0, 1fr) / minmax(0, 1fr) auto; }
.about-close { color: var(--muted); }
.about-close:hover { color: var(--text); }
.tabbed .about-close { grid-area: 1 / 2; align-self: center; margin: 4px 10px 0 4px; }
.about-tabs { grid-area: 1 / 1; display: flex; padding: 8px 0 0 12px; overflow-x: auto; scrollbar-width: none; }
.about-tabs::-webkit-scrollbar { display: none; }
#about:not(.tabbed) .about-tabs { display: none; }
.about-tabs [role="tab"] {
  flex: none; position: relative; padding: 13px 10px 14px; border: 0; background: none; cursor: pointer;
  font: 600 11px/1 var(--mono); letter-spacing: .2em; text-transform: uppercase; color: var(--muted); transition: color .2s;
}
.about-tabs [role="tab"]:hover, .about-tabs [aria-selected="true"] { color: var(--text); }
.about-tabs [aria-selected="true"]::after { content: ""; position: absolute; left: 10px; right: calc(10px + .2em); bottom: 0; height: 2px; background: var(--accent); }
.about-tabs [role="tab"]:focus-visible { outline-offset: -4px; border-radius: 8px; }   /* the row clips anything outside it */
.tabbed .about-body {
  grid-area: 2 / 1 / 3 / 3; overflow-y: auto; overscroll-behavior: contain; padding: 20px 24px 20px; border-top: 1px solid var(--line);
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .18) transparent;
}
.tabbed .about-panel > h3 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }   /* the tab says it */
#about h2 { margin: 0 0 8px; font-size: 20px; font-weight: 650; letter-spacing: -.01em; line-height: 1.2; }
#about h3 { margin: 26px 0 10px; font: 600 11px/1 var(--mono); letter-spacing: .28em; text-transform: uppercase; color: var(--muted); }
#about p, #about li, #about dd { color: #cdd0ee; line-height: 1.55; }
#about p { margin: 0 0 10px; }
#about a { color: var(--accent); text-underline-offset: 3px; }
#about b { color: var(--text); font-weight: 600; }
#aboutCity { padding-bottom: 18px; margin-bottom: 22px; border-bottom: 1px solid var(--line); }
/* cities: a card per city in its own accent (--c from the scene); the whole card switches city, the one on screen is lit */
.city-list { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); gap: 8px; }
.city-list li {
  --c-: var(--c, var(--accent)); position: relative; padding: 11px 13px 12px; border-radius: 12px;
  border: 1px solid var(--line); background: rgba(255, 255, 255, .03); transition: border-color .2s, background .2s, box-shadow .2s;
}
.city-list li:hover { border-color: color-mix(in srgb, var(--c-) 45%, transparent); background: color-mix(in srgb, var(--c-) 7%, transparent); }
.city-list li:has([aria-current]) {
  border-color: color-mix(in srgb, var(--c-) 75%, transparent); background: color-mix(in srgb, var(--c-) 13%, transparent);
  box-shadow: 0 0 22px -10px var(--c-);
}
.city-list li:has(a:focus-visible) { outline: 2px solid var(--c-); outline-offset: 2px; }
#about .city-list a { font-weight: 600; text-decoration: none; color: var(--c-); }
.city-list a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.city-list a:focus-visible { outline: none; }
.city-list span { display: block; margin-top: 1px; font: 11px var(--mono); color: var(--muted); }
#about .city-list p { margin: 6px 0 0; font-size: 13px; line-height: 1.5; }
.keys { margin: 0; display: grid; gap: 6px; }
.keys div { display: grid; grid-template-columns: 120px 1fr; gap: 12px; align-items: baseline; }
.keys dt { white-space: nowrap; }
.keys dd { margin: 0; }
kbd {
  display: inline-block; min-width: 22px; padding: 2px 6px; text-align: center; font: 11px/1.4 var(--mono); color: var(--text);
  border: 1px solid rgba(255, 255, 255, .16); border-bottom-width: 2px; border-radius: 6px; background: rgba(255, 255, 255, .06);
}
/* FAQ: questions fold open, one at a time (<details name>) */
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 11px 2px;
  font-weight: 600; cursor: pointer; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; flex: none; font: 15px/1 var(--mono); color: var(--muted); transition: color .2s; }
.faq summary:hover::after { color: var(--text); }
.faq details[open] summary::after { content: "−"; color: var(--accent); }
.faq summary:focus-visible { outline-offset: -2px; border-radius: 6px; }
#about .faq p { margin: -2px 24px 14px 2px; }
.about-foot { margin-top: 16px !important; }
/* What's new: the latest changes at the end of the About tab (tools/seo/seo.mjs), a day and a line each; whats-new/ has them all */
.news { margin: 0; padding: 0; list-style: none; }
.news li { display: grid; grid-template: auto 1fr / 56px 1fr; gap: 2px 10px; align-items: baseline; padding: 8px 0; border-bottom: 1px solid var(--line); }
#about .news li { font-size: 14px; line-height: 1.5; }
.news time { font: 11px var(--mono); color: var(--muted); white-space: nowrap; }
/* its type under the day, in the pigeon's short word: the day's column has room for it, so the line keeps its width
   (the About tab has little height to spare) */
.news li > .news-tag { grid-area: 2 / 1; place-self: start; padding: 0 3px; font-size: 9px; letter-spacing: .02em; }
.news li > :last-child { grid-area: 1 / 2 / 3; }
/* a What's new line's type, in front of it (NEWS_TYPES in site/js/ui/news.js, whose colours the pigeon's badge has too) */
.news-tag {
  display: inline-block; padding: 0 5px; border-radius: 5px; vertical-align: 1px; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--tag) 45%, transparent); background: color-mix(in srgb, var(--tag) 14%, transparent);
  color: var(--tag); font: 600 9.5px/1.6 var(--mono); letter-spacing: .08em; text-transform: uppercase;
}
.news-tag[data-type="city"] { --tag: #f6c177; }
.news-tag[data-type="feature"] { --tag: #7ee0b5; }
.news-tag[data-type="improved"] { --tag: #8ab8ff; }
.news-tag[data-type="fixed"] { --tag: #f2a0c0; }

/* billboard card: what the billboard opens (the text is written by tools/seo/seo.mjs), in the city's accent */
#pitchCard {
  width: min(420px, calc(100vw - 24px)); padding: 0; border: 1px solid var(--line); border-radius: 16px; color: var(--text);
  background: rgba(10, 11, 28, .86); -webkit-backdrop-filter: blur(18px) saturate(1.3); backdrop-filter: blur(18px) saturate(1.3);
  box-shadow: 0 24px 80px -20px rgba(0, 0, 0, .8);
}
#pitchCard::backdrop { background: rgba(3, 4, 12, .5); }
.pitch-inner { position: relative; padding: 22px 24px 20px; }
#pitchCard .about-close { position: absolute; top: 10px; right: 10px; }
.pitch-kicker { margin: 0 40px 12px 0; font: 600 11px/1.4 var(--mono); letter-spacing: .2em; text-transform: uppercase; color: var(--accent); }
#pitchCard h2 { margin: 0 0 8px; font-size: 20px; font-weight: 650; letter-spacing: -.01em; line-height: 1.2; }
#pitchCard p:not([class]) { margin: 0 0 18px; color: #cdd0ee; line-height: 1.55; }
.pitch-cta {
  display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 0 16px; border-radius: 10px;
  font-weight: 600; text-decoration: none; color: #12121c; background: var(--accent); transition: filter .2s;
}
.pitch-cta:hover { filter: brightness(1.1); }
.pitch-note { margin: 12px 0 0; font: 11px/1.5 var(--mono); color: var(--muted); }

/* touch: finger-sized targets */
@media (pointer: coarse) {
  :root { --btn: 44px; }
  .chip { padding: 9px 14px; }
  .vol input, .mix-row input { height: 32px; }
  .mix-row { min-height: 40px; }
  .about-tabs [role="tab"] { padding-block: 17px 18px; }
  .t-opt { min-height: 40px; padding-inline: 12px; }
  .seg:not(.st-grid) > label > span { padding-block: 11px; }   /* the buttons, not the style tiles or their names */
  .set-switch { min-height: 48px; }
  .rv-opt { min-height: 44px; align-items: center; }
  #toastCancel { min-height: 36px; padding-inline: 14px; }
  #saveMenu kbd, #together kbd, #settings kbd, #vibe kbd { display: none; }
  .tg-react { min-height: 48px; }
  #cityFind { font-size: 16px; }                  /* iOS Safari zooms the page into a field with smaller text */
  #chatText { font-size: 16px; }   /* iOS zooms into a text box whose text is smaller */
}
/* mouse and trackpad on wider windows: a mouse needs less room than a thumb, so denser FAQ and shortcut rows keep every
   question and key in view in a 720px panel */
@media (pointer: fine) and (min-width: 601px) {
  #about.tabbed { height: min(720px, calc(100dvh - 32px)); }
  .faq summary { padding-block: 7px; }
  .keys { gap: 4px; }
}
/* tablets and narrow windows (one row would leave the track title under 150px beside the 14 buttons, the sliders and
   the two 12px gutters: below 1074px with a mouse's 36px buttons, below 1186px with a touch screen's 44px): the
   volume sliders get their own row under the track, with the vibe, timer, together, chat, leaderboard and save buttons */
@media (max-width: 1073px), (pointer: coarse) and (max-width: 1185px) {
  #player { flex-wrap: wrap; gap: 2px 6px; }
  .vols { order: 1; flex: 1 0 calc(100% - 6 * (var(--btn) + 6px)); min-width: 0; gap: 18px; padding: 0 6px; }   /* min-width: range inputs have a wide intrinsic minimum */
  #vibeBtn, #timerBtn, #togetherBtn, #chatBtn, #boardBtn, #saveBtn { order: 2; }   /* share the volume row, so the track title keeps its room */
  .vol { flex: 1; min-width: 0; }
  .vol input { flex: 1; width: auto; min-width: 0; }
}
/* phones in portrait */
@media (max-width: 600px) {
  :root { --edge: 8px; --jobs-h: 22px; --top: calc(env(safe-area-inset-top) + var(--jobs-h) + 8px); }   /* the strip nudges the city card down less */
  /* the city card: name, tagline, time and the live count in one frosted card as wide as the dock. #links sit in its
     top right corner without their own panel (the ::before keeps their room beside the name and tagline), the running
     timers under it (--title-h, site/js/app.js). */
  .hud-city {
    --links-w: calc(4 * var(--btn)); --links-h: var(--btn);
    left: var(--gutter); right: var(--gutter); max-width: none;
    display: grid; grid-template-columns: minmax(0, 1fr) var(--links-w); grid-template-rows: auto 1fr auto; align-items: start;
    padding: 11px 14px; border-radius: 16px; border: 1px solid var(--line); background: var(--panel);
    -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3); text-shadow: none;
  }
  .hud-city::before { content: ""; grid-area: 1 / 2 / 3; height: var(--links-h); }
  #cityName { grid-column: 1; margin-bottom: 3px; font-size: 23px; }
  .sub { grid-column: 1; }
  .sub-sep { display: none; }                   /* the tagline and the time on lines of their own */
  #tagline, .sub time { display: block; }
  .sub time { margin-top: 2px; font-size: 12px; }
  .looks { grid-column: 1 / -1; }
  #links {
    top: calc(var(--top) + 6px); right: calc(var(--gutter) + 6px); gap: 0; padding: 0;
    border: 0; background: none; -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  #corner { top: calc(var(--top) + var(--title-h, 150px) + 8px); }
  /* the live count: the card's last row, "23 here now · 5 in Tokyo" ("480 here today · 12,345 all time" while no one
     else is here) */
  .live {
    grid-column: 1 / -1; width: auto; justify-content: start; margin: 10px 0 0; padding: 9px 0 0;
    border: 0; border-top: 1px solid var(--line); border-radius: 0; background: none; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  .live::after { content: none; }
  .live:has(.live-now) :is(.live-today, .live-all) { display: none; }
  .jobs-all { display: none; }                  /* the strip itself is the link to the jobs; they get the room */
  :root:has(#jobs:not(.running)) { --jobs-h: 45px; }
  #jobs:not(.running) .jobs-tag { display: none; }
  #jobs:not(.running) .jobs-all { display: inline-flex; }
  #jobs:not(.running) .jobs-run { mask-image: none; -webkit-mask-image: none; }
  #jobs:not(.running) .jobs-cta { font-size: 11px; }
  .jobs-post { padding: 2px 8px; font-size: 10px; line-height: 1.2; }   /* fits the 22px strip */
  /* four icons: coffee, the video loops, the site's X and Sign in (the person icon; the maker's X where there's no sign-in) */
  #links [data-network="github"] { display: none; }
  #links:has(#phLink:not([hidden])) [data-network="x"] { display: none; }   /* the launch week: Product Hunt takes the maker's X */
  #links:has(#accountBtn:not([hidden])) [data-network="x"] { display: none; }
  #links:has(#phLink:not([hidden])):has(#accountBtn:not([hidden])) #shopLink { display: none; }   /* both: and the video loops' place */
  .chip { padding-inline: 7px; gap: 4px; }     /* the four chips fit one row: city names up to "Singapore" at 375 px */
  #cities { gap: 4px; }
  #timerPill { flex-direction: column; gap: 4px; }   /* one timer per line, clear of the city title */
  /* a third row: the sliders get row 2 (the settings gear at its end, as the third row is full at 320 px), the vibe,
     timer, together, chat, mixer and save buttons go below them */
  .vols { display: contents; }
  #player::before, #player::after { content: ""; flex-basis: 100%; }   /* line breaks around the sliders */
  #player::before { order: 1; }
  .vol { order: 2; padding-inline: 6px; }
  #player::after { order: 3; }
  #settingsBtn { order: 2; }
  #vibeBtn, #timerBtn, #togetherBtn, #chatBtn, #boardBtn, #mixerBtn, #saveBtn { order: 4; }
  .pop, #tour, #weather, #mixer, #saveMenu, #settings, #cityPicker, #chat, #board, #news { width: 100%; }
  #cityBtnTime { display: none; }               /* the title shows the local time: the four chips fit one row */
  #cityList { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }   /* two columns down to 320 px */
  .feat-text strong { font-size: 19px; }
  #vibe fieldset, #settings fieldset { padding-inline: 10px; }
  .seg:not(.st-grid) > label > span { padding-inline: 2px; }   /* every row's labels fit down to 320 px */
  #queue .pop-head small, #tour .pop-head small, #weather .pop-head small { display: none; }
  .about-inner { padding: 18px 16px 22px; }
  #about.tabbed { height: min(860px, calc(100dvh - 32px)); }   /* phones: nearly full height */
  .tabbed .about-inner { padding: 0; }
  .tabbed .about-body { padding: 16px 16px 18px; }
  .about-tabs { padding-left: 6px; }
  .about-tabs [role="tab"] { padding-inline: 5px; letter-spacing: .08em; }   /* all five tabs fit beside the close button from 360px */
  .about-tabs [aria-selected="true"]::after { left: 5px; right: calc(5px + .08em); }
  .tabbed .about-close { margin-right: 6px; }
  .keys div { grid-template-columns: 96px 1fr; }
  .pitch-inner { padding: 18px 16px; }
}
/* short screens (phones in landscape): tighter title and dock so more of the scene shows */
@media (max-height: 500px) {
  :root { --jobs-h: 22px; --top: calc(env(safe-area-inset-top) + var(--jobs-h) + 10px); --bottom: max(8px, env(safe-area-inset-bottom)); }
  :root.jobs-off { --top: max(10px, env(safe-area-inset-top)); }
  .jobs-post { padding: 2px 8px; font-size: 10px; line-height: 1.2; }
  #cityName { margin: 0; font-size: 22px; }
  .sub { font-size: 12px; }
  .live { margin-top: 6px; padding-block: 3px; font-size: 11px; }
  .hud-bottom { gap: 6px; }
  #player { padding-block: 6px; }
}
@media (prefers-reduced-motion: reduce) {
  .jobs-all svg { transition: none; }
  .ui, #corner, #timerPill { transition: none; }
  #play.pulse, .pop, .live::before, .live::after, #jobs.running .jobs-track, #chatWho .chat-online.on::before, #coffee .steam, #coffee .steam + .steam, #links #coffee::after { animation: none; }
  #jobs.running .jobs-run { -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent); }   /* standing still: no left fade */
  #links :is(#phLink, #shopLink, #coffee, #followX, #accountBtn), :is(#phLink, #shopLink, #coffee, #followX, #accountBtn) span { transition: none; }
  .seg span, .set-switch input, .set-switch input::before { transition: none; }
  .rv { animation: none; }
  .rv-opt, .rv-opt::before { transition: none; }
  #cities { scroll-behavior: auto; }
}

/* Below 800px the radio chip drops its state (filled while on, with a toast as it turns on or off; the track line says
   the show and who listens), and below
   500px (phones) the tour and weather chips drop theirs too (still lit while on; their menus say which), so the four
   chips fit one row. */
@media (max-width: 799px) {
  #radioState { display: none; }
  #radioBtn.on { background: color-mix(in srgb, var(--accent) 16%, var(--panel)); color: var(--accent); }   /* no state to read: filled while on */
}
@media (max-width: 499px) {
  #tourState, #weatherState { display: none; }
}
/* On the narrowest phones, keep room for city names beside the four links. */
@media (max-width: 360px) {
  .jobs-tag { font-size: 0; }                   /* the lit pixel alone: the jobs keep room beside Post a job */
  .jobs-tag::before { margin-right: 0; }
  #player { column-gap: 4px; }   /* the third row's six buttons fit beside each other down to 320 px */
  #links { display: grid; grid-template-columns: repeat(2, var(--btn)); }
  .hud-city { --links-w: calc(2 * var(--btn)); --links-h: calc(2 * var(--btn)); }   /* the card keeps room for two rows of them */
}
/* Below 360px (down to 320px) the About tabs tighten again so all five still fit. */
@media (max-width: 359px) {
  .about-tabs [role="tab"] { padding-inline: 3.5px; letter-spacing: 0; }
  .about-tabs [aria-selected="true"]::after { left: 3.5px; right: 3.5px; }
}
