/*
 * Stage one.
 *
 * Light, warm, quiet — the same white-and-orange family as the rest of
 * masirino, so the game does not read as a different product. The candidate
 * has been staring at konkur material for months; the page should feel like a
 * clean desk, not a scoreboard.
 *
 * Structure: tokens, shell, shared atoms (field, chip, button, card), then one
 * block per scene. Every animation is behind prefers-reduced-motion.
 */

/* Two files, one family. Without a unicode-range the second block simply won
   the cascade, so every Persian glyph came from the Latin file — which does not
   have them — and the page fell back to Tahoma while the preloaded Arabic file
   was downloaded and thrown away. The ranges are what make the split work:
   Arabic/Persian letters and Persian digits from one file, Latin from the
   other, and a phone on a slow link downloads only the ranges it paints. */
@font-face {
  font-family: "Vazirmatn Variable";
  src: url("/fonts/vazirmatn-arabic.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF,
    U+200C-200F, U+2010-2011, U+204F, U+2E41, U+FDFC, U+FDFD;
}
@font-face {
  font-family: "Vazirmatn Variable";
  src: url("/fonts/vazirmatn-latin.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

:root {
  /* Ink and ground come straight from the site's own tokens so the game and
     the landing page are visibly the same product. */
  --ink: #211c19;
  --ink-2: #453a34;
  --ink-3: #6a5e57;
  --ground: #f4efe9;
  --ground-2: #fffefd;
  --surface: #fff;
  --surface-2: #f7f1ea;
  --line: rgba(61, 47, 40, .14);
  --line-strong: rgba(61, 47, 40, .28);
  --orange: #ed6d1c;
  --orange-deep: #d7570e;
  /* On a dark ground this was a lighter orange; on white it has to go darker
     to stay readable. This is the text-and-focus orange: 5.4:1 on white and
     still above 4.5:1 on the sand page and the tinted surface. --orange and
     --orange-deep stay for fills, borders and large numerals, where 3:1 is the
     bar and the brand tone matters more. */
  --orange-soft: #b34a06;
  /* And one step darker again, for the places where the *ground* is already a
     translucent orange: on rgba(238,112,32,.14–.24) over white, --orange-soft
     lands between 4.03:1 and 4.18:1, i.e. under the bar. This token is 5.7:1 or
     better on every one of those grounds and 7.6:1 on plain white, which also
     makes it the right colour for a link hover — --orange as text is 3.09:1 and
     the comment above says it is a fill. */
  --orange-ink: #8f3a04;
  --green: #31805c;
  /* Same story on the green side: --green as text on the tinted green chip is
     3.70:1. This is 5.3:1 there and 6.9:1 on white. */
  --green-ink: #276548;
  --danger: #b3391c;
  --radius: 16px;
  --shadow-1: 0 .35rem .9rem rgba(69, 42, 26, .07), 0 0 0 1px rgba(237, 109, 28, .05);
  --shadow-2: 0 1rem 2.6rem rgba(48, 33, 25, .14);
  color-scheme: light;
  font-family: "Vazirmatn Variable", Vazirmatn, Tahoma, Arial, sans-serif;
  font-synthesis: none;
}

* { box-sizing: border-box; }

/* An author `display` beats the user-agent rule for [hidden], so every element
   the game toggles with the attribute needs this guard. Without it the
   shortlist drawer — `position: fixed; inset: 0; display: flex` — sits over
   every scene permanently. The DOM tests could not catch it: they read the
   tree, and in the tree the attribute was there. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100svh;
  background:
    radial-gradient(120% 80% at 80% -10%, rgba(237, 109, 28, .10), transparent 58%),
    radial-gradient(90% 70% at 10% 110%, rgba(49, 128, 92, .05), transparent 62%),
    var(--ground);
  background-attachment: fixed;
  color: var(--ink);
  font-weight: 450;
  line-height: 1.9;
  -webkit-font-smoothing: antialiased;
}

button, input, select, textarea { font: inherit; color: inherit; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Moved out of view with a transform rather than a large inline offset: an
   offset pushes the document's scrollable width out by that distance, which in
   an RTL page shows up as thousands of pixels of empty horizontal scroll. */
.skip-link {
  position: absolute; inset-inline-start: 0; top: 0; z-index: 50;
  transform: translateY(-200%);
  padding: .7rem 1rem; background: var(--orange); color: #211c19;
  border-radius: 0 0 12px 0; font-weight: 800; text-decoration: none;
}
.skip-link:focus { transform: translateY(0); }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--orange-soft);
  outline-offset: 2px;
  border-radius: 8px;
}

/* ---------------------------------------------------------------- *
 * HUD
 * ---------------------------------------------------------------- */

/*
 * Two rows, on purpose.
 *
 * The HUD used to be one wrapping flex line: on a 390px phone the brand, the
 * bar, three stats and six pills reflowed into three ragged rows whose order
 * changed with the alias length. It is a grid now — identity and progress on
 * top, one rail of controls under it — so the chrome has the same shape on a
 * phone, a desktop and an installed app.
 */
.hud {
  position: sticky; top: 0; z-index: 20;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "brand progress stats"
    "tools tools    tools";
  align-items: center;
  gap: .55rem .9rem;
  /* index.html asks for the notch area with viewport-fit=cover, so every fixed
     or sticky edge in this file has to pay for it. Without these the sticky HUD
     sits under the dynamic island and the toasts under the home indicator. */
  padding: calc(.7rem + env(safe-area-inset-top)) calc(clamp(.8rem, 3vw, 2rem) + env(safe-area-inset-right))
           .7rem calc(clamp(.8rem, 3vw, 2rem) + env(safe-area-inset-left));
  background: rgba(252, 249, 245, .88);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}

.brand {
  grid-area: brand;
  display: inline-flex; align-items: center; gap: .5rem;
  color: var(--ink); text-decoration: none; font-weight: 800; letter-spacing: -.01em;
}
/* Same compass the site header wears — /media/logo-mark.svg, drawn once. */
.brand i {
  width: 1.4rem; height: 1.4rem; flex: 0 0 auto;
  background: url("/media/logo-mark.svg?v=20260831") center / contain no-repeat;
}

.hud-progress {
  grid-area: progress;
  min-width: 4rem; height: 5px; border-radius: 99px;
  background: var(--surface-2); overflow: hidden;
}
.hud-progress::after {
  content: ""; display: block; height: 100%; width: var(--progress, 0%);
  background: linear-gradient(90deg, var(--orange-deep), var(--orange));
  transition: width .5s cubic-bezier(.22, 1, .36, 1);
}

.hud-stats { grid-area: stats; display: flex; gap: 1rem; margin: 0; flex-wrap: wrap; justify-content: flex-end; }
.hud-stats div { display: flex; flex-direction: column; line-height: 1.3; }
.hud-stats dt { color: var(--ink-3); font-size: .62rem; font-weight: 700; letter-spacing: .04em; }
.hud-stats dd { margin: 0; font-size: .84rem; font-weight: 800; }
.hud-stats .hud-player {
  display: grid;
  grid-template-columns: 2.35rem minmax(0, auto);
  grid-template-rows: auto auto;
  column-gap: .45rem;
  align-items: center;
}
.hud-avatar {
  grid-row: 1 / 3;
  display: block;
  width: 2.25rem;
  height: 2.25rem;
  object-fit: contain;
  border: 2px solid #fff;
  border-radius: 50%;
  background: linear-gradient(145deg, #fff9f2, #f2dfce);
  box-shadow: 0 0 0 1px rgba(179, 74, 6, .26), 0 .28rem .7rem rgba(99, 55, 24, .14);
}
.hud-avatar[hidden] { display: none; }



/* The control rail: one row of pills under the identity line. Wrap is left on
   as the last resort — a rail that scrolls would clip the two popovers that
   open out of it (the step menu and the music styles). */
.hud-tools {
  grid-area: tools;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .4rem;
  min-width: 0;
}
.hud-label { white-space: nowrap; }

/* On a phone the arc and the credibility give up their room to the player row —
   both are read in full on the map screen and in its summary sheet. The two
   pills keep their words at every width: the word is the whole reason this bar
   can be read at a glance. */
@media (max-width: 34rem) {
  .hud { gap: .5rem .7rem; }
  .hud-stats div:not(.hud-player) { display: none; }
  .hud-stats dt { font-size: .58rem; }
  .hud-tools .hud-list { min-height: 44px; padding: .4rem .7rem; }
}

/*
 * Installed, there is no browser chrome above this bar and no address bar to
 * absorb an overscroll, so the app pays for the notch itself, stops the pull
 * that would otherwise bounce the whole page, and drops the last transparency
 * that only made sense with a toolbar behind it.
 */
@media (display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui) {
  html, body { overscroll-behavior-y: none; }
  .hud {
    padding-top: calc(.85rem + env(safe-area-inset-top));
    background: rgba(252, 249, 245, .96);
  }
}

/* ---------------------------------------------------------------- *
 * Stage and shared atoms
 * ---------------------------------------------------------------- */

#stage {
  display: block;
  padding: clamp(1.2rem, 4vw, 3rem) calc(clamp(.9rem, 4vw, 2rem) + env(safe-area-inset-right))
           calc(5rem + env(safe-area-inset-bottom)) calc(clamp(.9rem, 4vw, 2rem) + env(safe-area-inset-left));
}
#stage:focus { outline: none; }

/* fill "backwards", not "both": a finished fill-both transform computes to an
   identity matrix, and even an identity matrix is a containing block — every
   position:fixed child (the phone band sheet) anchored to the scene forever. */
.scene { max-width: 62rem; margin: 0 auto; animation: rise .5s cubic-bezier(.22, 1, .36, 1) backwards; }
.scene-atlas { max-width: 82rem; }

/* Generated scene art is deliberately decorative: it warms selected emotional
   beats without carrying instructions that must be read by an assistive
   technology user. Interactive maps and data remain real HTML/SVG. */
.scene-with-art { display: grid; align-items: center; gap: clamp(.7rem, 3vw, 2.4rem); }
.scene-with-art--welcome { grid-template-columns: minmax(0, 1fr) minmax(12rem, 20rem); }
.scene-with-art--atlas { grid-template-columns: minmax(0, 1fr) minmax(8rem, 13rem); margin-bottom: .25rem; }
.scene-with-art--feature { grid-template-columns: minmax(0, 1fr) minmax(9rem, 16rem); margin-bottom: .8rem; }
.scene-with-art--compact { grid-template-columns: minmax(0, 1fr) minmax(7rem, 12rem); margin-bottom: .65rem; }
.scene-art { position: relative; margin: 0; pointer-events: none; }
.scene-art::before {
  content: ""; position: absolute; z-index: -1; inset: 18% 12% 8%; border-radius: 50%;
  background: radial-gradient(circle, rgba(237,109,28,.16), rgba(237,109,28,0) 68%);
  filter: blur(8px); transform: scale(1.08);
}
.scene-art img { display: block; width: 100%; height: auto; border-radius: 1.25rem; mix-blend-mode: multiply; animation: mascot-bob 5.6s ease-in-out infinite; }
.scene-art--pals { align-self: start; margin-top: -.9rem; }
.scene-art--scout { max-width: 12rem; justify-self: end; margin-top: -.75rem; }
.scene-art--compass, .scene-art--compass-question { max-width: 13rem; justify-self: end; }
.scene-art--storyteller, .scene-art--companion, .scene-art--complete { max-width: 16rem; justify-self: end; }
/* The stage-two-to-seven openings are one prose column, not the grid above:
   a float lets the text wrap around the art without restructuring six scene
   templates, and collapses to a centred block on a phone. */
.scene-art--stage { float: inline-end; width: min(13rem, 38vw); margin: 0 0 .7rem 1.1rem; }
@keyframes mascot-bob { 0%,100% { transform: translateY(0) rotate(-.45deg); } 50% { transform: translateY(-9px) rotate(.45deg); } }

@keyframes rise { from { opacity: 0; transform: translateY(14px); } }

@media (max-width: 43rem) {
  .scene-with-art--welcome { grid-template-columns: 1fr; }
  .scene-art--pals { width: min(15rem, 58vw); margin: -1rem auto 0; order: -1; }
  .scene-with-art--atlas { grid-template-columns: minmax(0, 1fr) 7.2rem; gap: .4rem; }
  .scene-art--scout { width: 7.2rem; margin-top: 0; }
  .scene-with-art--feature, .scene-with-art--compact { grid-template-columns: minmax(0, 1fr) minmax(6.8rem, 8.5rem); gap: .45rem; }
  .scene-art--compass, .scene-art--compass-question,
  .scene-art--storyteller, .scene-art--companion, .scene-art--complete { width: min(8.5rem, 29vw); }
  .scene-art--stage { float: none; width: min(9.5rem, 34vw); margin: 0 auto .6rem; }
}

@media (prefers-reduced-motion: reduce) {
  .scene-art img { animation: none; }
}

.kicker { margin: 0 0 .3rem; color: var(--orange-soft); font-size: .78rem; font-weight: 800; letter-spacing: .04em; }
.scene h2 { margin: 0 0 .6rem; font-size: clamp(1.35rem, 4vw, 2.1rem); line-height: 1.55; letter-spacing: -.015em; }
.scene h3 { margin: 0 0 .5rem; font-size: 1.05rem; }
.lede { max-width: 42rem; margin: 0 0 1.6rem; color: var(--ink-2); font-size: 1rem; }
.muted { color: var(--ink-3); font-weight: 500; }
.field-note { margin: .45rem 0 0; color: var(--ink-3); font-size: .76rem; line-height: 1.8; }
.field-note[data-tone="good"] { color: var(--green-ink); }
.field-note[data-tone="error"] { color: var(--danger); }
.status { min-height: 1.4rem; margin: .9rem 0 0; color: var(--ink-2); font-size: .84rem; }
.status[data-tone="error"] { color: var(--danger); }
.status[data-tone="success"] { color: var(--green-ink); }

.stack { display: grid; gap: .3rem; max-width: 30rem; }

.field {
  display: flex; align-items: center; gap: .5rem;
  padding: .1rem .85rem; background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px;
  transition: border-color .18s, box-shadow .18s;
}
.field:focus-within { border-color: var(--orange); box-shadow: 0 0 0 4px rgba(238, 112, 32, .14); }
.field input, .field select, .field textarea {
  width: 100%; padding: .8rem 0; background: none; border: 0; outline: none;
}
.field textarea { padding: .8rem 0; resize: vertical; line-height: 1.9; }
.field select { appearance: none; cursor: pointer; padding-inline-end: .5rem; }

button, .button-like {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .78rem 1.4rem; border: 0; border-radius: 12px;
  background: var(--orange); color: #211c19;
  font-weight: 800; text-decoration: none; cursor: pointer;
  transition: transform .16s, filter .16s, background .16s;
}
button:hover, .button-like:hover { filter: brightness(1.08); transform: translateY(-1px); }
button:active { transform: translateY(0); }
/* Press feedback: hover never happens on the phones this audience holds, so a
   tap gets a squeeze instead. The reduced-motion block below zeroes it. */
.choice-card:active, .panel-option:active, .chip-button:active,
.option-list button:active, .explorer-list button:active,
.city-actions button:active, .sure-toggle button:active,
.band-menu button:active, .scene-menu li button:active, .city-tabs button:active,
.scale-option:active, .choice-card:has(input):active {
  transform: scale(.975);
  transition-duration: .07s;
}
/* `wait` was on every disabled button, including #tuesday-done and
   #sure-submit, which start disabled because the form is not finished — not
   because anything is in flight. In-flight is a separate, deliberate state. */
button:disabled { opacity: .55; cursor: not-allowed; transform: none; }
button.is-busy, [aria-busy="true"] { cursor: wait; }
button.is-busy { opacity: .7; }
button.ghost {
  background: none; color: var(--ink-2);
  border: 1px solid var(--line-strong);
}
button.ghost:hover { background: var(--surface); color: var(--ink); }

.scene-actions { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.6rem; }

.chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
/* 24px is WCAG 2.5.8's legal floor, not a usable finger target. These are the
   atlas filters, the report reasons, the topic and kind pickers and the thread
   seeds — the controls a teenager taps most on this page — so they get the 44px
   of 2.5.5 instead. Padding does the work; the type size does not change. */
.chip-button {
  display: inline-flex; align-items: center; justify-content: center;
  min-block-size: 44px; padding: .55rem .9rem;
  background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--line); border-radius: 99px; font-size: .78rem; font-weight: 600;
}
.chip-button:hover { border-color: var(--line-strong); transform: none; filter: none; }
.chip-button[aria-pressed="true"] {
  background: rgba(238, 112, 32, .18); border-color: var(--orange); color: var(--ink);
}

/* The provenance chip. This is the most important element on the page: it is
   how a candidate tells a counted fact from a model estimate at a glance. */
.chip {
  display: inline-block; padding: .1rem .5rem; border-radius: 99px;
  font-size: .64rem; font-weight: 800; white-space: nowrap; vertical-align: middle;
}
.chip[data-tone="good"] { background: rgba(49, 128, 92, .12); color: var(--green-ink); }
.chip[data-tone="soft"] { background: rgba(33, 28, 25, .06); color: var(--ink-3); }
.chip[data-tone="community"] { background: rgba(237, 109, 28, .14); color: var(--orange-ink); }

.escape { margin-top: 1.2rem; }
.escape summary {
  cursor: pointer; color: var(--ink-3); font-size: .82rem;
  padding: .4rem 0; list-style-position: inside;
}
.escape summary:hover { color: var(--ink-2); }
.escape > *:not(summary) { margin-top: .6rem; }

.option-list { list-style: none; margin: .6rem 0 0; padding: 0; max-height: 20rem; overflow-y: auto; }
.option-list li { margin-bottom: .35rem; }
.option-list button {
  display: flex; justify-content: space-between; align-items: baseline; gap: .8rem;
  width: 100%; padding: .65rem .9rem; text-align: start;
  background: var(--surface); color: var(--ink); border: 1px solid transparent; border-radius: 10px;
  font-weight: 500;
}
.option-list button:hover { background: var(--surface-2); border-color: var(--line-strong); transform: none; filter: none; }
.option-list span { font-size: .74rem; color: var(--ink-3); }

.choice-grid { display: grid; gap: .55rem; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); margin-top: .9rem; }
.choice-card {
  display: block; padding: .9rem 1rem; text-align: start;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: 14px; font-weight: 500;
  cursor: pointer;
}
.choice-card:hover { border-color: var(--orange-soft); transform: translateY(-2px); filter: none; }
.choice-card[aria-checked="true"], .choice-card:has(:checked) {
  border-color: var(--orange); background: rgba(238, 112, 32, .12);
}
.choice-card strong { display: block; font-size: .92rem; font-weight: 700; }
.choice-card small { display: block; margin-top: .15rem; color: var(--ink-3); font-size: .74rem; line-height: 1.7; }
.choice-card input { position: absolute; opacity: 0; pointer-events: none; }

/* ---------------------------------------------------------------- *
 * Map
 * ---------------------------------------------------------------- */

.map-wrap { position: relative; margin: 1rem 0; }
.map-loading { color: var(--ink-3); font-size: .86rem; text-align: center; padding: 3rem 0; }

.iran-map { display: block; width: 100%; height: auto; }

/* The viewBox is 760 wide and the element is width:100%, so on a 320px phone
   every user unit renders at about 0.39 CSS px: a .6 stroke became 0.23px and
   the province borders disappeared. `non-scaling-stroke` pins the border to CSS
   pixels at any scale, which is the only thing that makes the map legible on a
   small screen and on a zoomed one. */
.province {
  fill: rgba(33, 28, 25, .05);
  stroke: rgba(61, 47, 40, .34);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  cursor: pointer;
  transition: fill .22s, stroke .22s, opacity .3s;
}
.province:hover, .province:focus-visible { fill: rgba(237, 109, 28, .3); stroke: var(--orange-deep); stroke-width: 2; }
.province.is-selected { fill: var(--orange); stroke: #fff; stroke-width: 2.5; }
.province.is-dimmed { opacity: .34; }

/* Province by province rather than all at once, so the country assembles
   itself. This used to be a d3-transition; it is CSS now, which is how the
   whole of d3-transition left the critical path. --i is the index. */
@keyframes province-in { from { opacity: 0; } to { opacity: 1; } }
.iran-map.is-revealing .province {
  animation: province-in .42s ease-out both;
  animation-delay: calc(var(--i, 0) * 18ms);
}

/* --map-scale is the SVG's rendered scale, written by map.js and kept fresh by
   a ResizeObserver. Dividing by it means a label is 9.5 CSS px on a phone, on a
   desktop and at any zoom level, instead of 3.7px at 320px. */
.province-label {
  fill: var(--ink-2); font-size: calc(9.5px / var(--map-scale, 1)); font-weight: 700;
  pointer-events: none; paint-order: stroke;
  stroke: rgba(255, 255, 255, .92); stroke-width: calc(3px / var(--map-scale, 1)); stroke-linejoin: round;
}
.province-label.is-selected {
  fill: #fff; stroke: rgba(140, 60, 12, .6); font-size: calc(11px / var(--map-scale, 1));
}

/* Choropleth: five discrete steps. A continuous ramp would imply a precision
   the underlying data does not have. */
.province.is-painted[data-band="0"] { fill: rgba(238, 112, 32, .10); }
.province.is-painted[data-band="1"] { fill: rgba(238, 112, 32, .22); }
.province.is-painted[data-band="2"] { fill: rgba(238, 112, 32, .38); }
.province.is-painted[data-band="3"] { fill: rgba(238, 112, 32, .58); }
.province.is-painted[data-band="4"] { fill: rgba(238, 112, 32, .82); }
.province.is-painted[data-band="none"] { fill: rgba(33, 28, 25, .04); }

/* Markers: home is a ring, shortlisted cities are dots. Shape carries the
   distinction as well as colour, so it survives for a reader who cannot
   separate the two hues. */
.marker { pointer-events: none; }
.marker-halo { fill: rgba(238, 112, 32, .22); }
.marker-dot { fill: var(--orange); stroke: #fff; stroke-width: 1.1; }
.marker[data-kind="home"] .marker-halo { fill: rgba(49, 128, 92, .18); }
.marker[data-kind="home"] .marker-dot { fill: none; stroke: var(--green); stroke-width: 2.4; }
.marker-label {
  fill: var(--ink); font-size: calc(9.5px / var(--map-scale, 1)); font-weight: 700; paint-order: stroke;
  stroke: rgba(255, 255, 255, .92); stroke-width: calc(3px / var(--map-scale, 1)); stroke-linejoin: round;
}
.marker[data-kind="home"] .marker-label { fill: var(--green-ink); }

/*
 * The only feedback a keyboard user gets on the map (map.js opens it on focus),
 * and until now it was a leftover from the dark theme: near-black ground with
 * the page's dark ink inherited on top of it, so 1.06:1 — invisible.
 *
 * Positioning: map.js writes inset-inline-start, which in this RTL page is
 * `right`, i.e. the distance from the container's right edge. It now passes
 * `bounds.right - clientX`, so the box's right edge lands exactly on the
 * pointer and translate(50%) re-centres it there. (It used to pass the distance
 * from the *left* edge, which mirrored the tip across the map: finger on
 * خوزستان, tooltip over خراسان.)
 */
.map-tip {
  position: absolute; z-index: 5; pointer-events: none;
  transform: translate(50%, -120%);
  padding: .4rem .7rem;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 10px;
  box-shadow: var(--shadow-1);
  font-size: .74rem; line-height: 1.6;
  max-inline-size: min(15rem, 66vw);
}
.map-tip b { display: block; font-size: .82rem; }
.map-tip span { color: var(--ink-2); }

/* U12: real touch pan and zoom, without d3-zoom.
 * pan-y keeps one-finger vertical scrolling on the page (a map that eats the
 * scroll gesture is a trap); the two-finger pinch and the horizontal drag are
 * handled in map.js against the viewBox. The buttons are the same thing for a
 * keyboard, and the equivalent <select> is still the shortest path of all. */
.iran-map { touch-action: pan-y; }
.map-wrap { overscroll-behavior: contain; }
.map-controls {
  position: absolute; inset-block-start: .4rem; inset-inline-end: .4rem;
  z-index: 4; display: flex; gap: .3rem;
}
.map-controls button {
  min-inline-size: 44px; min-block-size: 44px; padding: 0;
  background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--line-strong); border-radius: 12px;
  font-size: 1rem; font-weight: 800; box-shadow: var(--shadow-1);
}
.map-controls button:hover { filter: none; transform: none; border-color: var(--orange-soft); }
.map-controls button[disabled] { opacity: .45; }
.map-hint { margin: .2rem 0 0; color: var(--ink-3); font-size: .72rem; }

.map-fallback { max-width: 22rem; margin-top: .8rem; }
.map-fallback label { display: block; margin-bottom: .3rem; color: var(--ink-3); font-size: .76rem; }

.legend { display: flex; align-items: center; gap: .3rem; margin-top: .5rem; font-size: .7rem; color: var(--ink-3); }
.legend i { width: 1.6rem; height: .5rem; border-radius: 3px; }
.legend i[data-band="0"] { background: rgba(238, 112, 32, .10); }
.legend i[data-band="1"] { background: rgba(238, 112, 32, .22); }
.legend i[data-band="2"] { background: rgba(238, 112, 32, .38); }
.legend i[data-band="3"] { background: rgba(238, 112, 32, .58); }
.legend i[data-band="4"] { background: rgba(238, 112, 32, .82); }
.legend-title { margin-inline-end: .4rem; font-weight: 700; }

/* ---------------------------------------------------------------- *
 * Narrative
 * ---------------------------------------------------------------- */

.narrative { display: grid; gap: 1.4rem; }
.narrative-rail { display: flex; gap: .4rem; justify-content: center; }
.rail-dot { width: 2.2rem; height: 3px; border-radius: 99px; background: var(--surface-2); transition: background .3s; }
.rail-dot.is-done { background: var(--orange); }

.panel-card {
  display: grid; gap: 1rem; justify-items: center; text-align: center;
  max-width: 34rem; margin: 0 auto; padding: 1.6rem 1.2rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 22px;
}
.panel-card[data-pending] { display: none; }
.panel-card h3 { margin: 0; font-size: clamp(1.15rem, 3.4vw, 1.55rem); line-height: 1.65; }
.panel-body { margin: 0; color: var(--ink-2); font-size: .94rem; }
.art-slot { width: 100%; max-width: 20rem; }
.art { width: 100%; height: auto; }
.narrative-actions { display: flex; gap: .6rem; justify-content: center; }

/* Each panel asks one question; the answer is echoed back, never scored. */
.panel-ask { width: 100%; padding-top: .9rem; border-top: 1px dashed var(--line); }
.panel-question { margin: 0 0 .7rem; font-size: .92rem; font-weight: 700; }
.panel-options { display: flex; flex-wrap: wrap; gap: .45rem; justify-content: center; }
.panel-option {
  padding: .5rem 1rem; background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--line); border-radius: 99px; font-size: .84rem; font-weight: 600;
}
.panel-option:hover { border-color: var(--orange-soft); color: var(--ink); filter: none; }
.panel-option[aria-pressed="true"] {
  background: rgba(238, 112, 32, .18); border-color: var(--orange); color: var(--ink);
}
.panel-echo {
  min-height: 1.6rem; margin: .7rem 0 0;
  color: var(--orange-soft); font-size: .84rem; line-height: 1.8;
}

/* Calendar */
.cal-body { fill: #fff; stroke: var(--line-strong); }
.cal-head { fill: rgba(238,112,32,.32); }
.cal-ring { fill: var(--ink-3); }
.cal-day { fill: rgba(61,47,40,.16); }
.is-animating .cal-day { animation: pop .4s backwards; animation-delay: calc(var(--i, 0) * .022s); }

/* Day grid */
.dot { transition: fill .4s; }
.dot-city { fill: rgba(238,112,32,.62); }
.dot-class { fill: #2f6fd0; }
.is-animating .dot { animation: pop .35s backwards; animation-delay: calc(var(--i, 0) * .0035s); }

/* Day timeline */
.rail { stroke: var(--line-strong); stroke-width: 2; }
.pip circle { fill: #fff; stroke: var(--line-strong); }
.pip text { fill: var(--ink-3); font-size: 11px; }
/* The little sun, route and moon inside the pips. Stroke-drawn so they read at
   16px, in the soft orange the rest of the scene already speaks. */
.pip-glyph { stroke: var(--orange-soft); stroke-width: 1.6; stroke-linecap: round; fill: none; }
.pip-glyph circle { fill: none; stroke: var(--orange-soft); }
.pip-glyph .pip-glyph-fill, .pip-glyph circle.pip-glyph-fill { fill: var(--orange-soft); stroke: none; }
.is-animating .pip { animation: pop .4s backwards; animation-delay: calc(var(--i, 0) * .16s); }
.spark { fill: var(--orange); }
.is-animating .spark { animation: travel 2.6s ease-in-out infinite; }

/* Graph */
.edge { stroke: rgba(238,112,32,.45); stroke-width: 1.6; }
.node { fill: var(--orange-soft); }
.is-animating .edge { stroke-dasharray: 200; stroke-dashoffset: 200; animation: draw .55s forwards; animation-delay: calc(var(--i, 0) * .09s); }
.is-animating .node { animation: pop .4s backwards; animation-delay: calc(.2s + var(--i, 0) * .07s); }

/* Glow */
.spark-dot { fill: var(--orange-soft); }
.is-animating .spark-dot { animation: pop .5s backwards; animation-delay: calc(var(--i, 0) * .03s); }
.is-animating .glow { animation: breathe 4s ease-in-out infinite; }

@keyframes pop { from { opacity: 0; transform: scale(.4); } }
@keyframes draw { to { stroke-dashoffset: 0; } }
@keyframes travel { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-180px); } }
@keyframes breathe { 0%, 100% { opacity: .7; } 50% { opacity: 1; } }

/* ---------------------------------------------------------------- *
 * Companion
 * ---------------------------------------------------------------- */

.whisper { font-style: normal; color: var(--ink-2); border-inline-start: 3px solid var(--orange); padding-inline-start: 1rem; }

.invite-card {
  padding: 1.3rem; margin: 1.4rem 0;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: 20px;
  border-top: 3px solid var(--orange);
  box-shadow: var(--shadow-1);
}
.invite-card__hero {
  display: grid; grid-template-columns: minmax(10rem, 1.1fr) minmax(12rem, .9fr);
  align-items: center; gap: clamp(.7rem, 3vw, 1.4rem); margin-bottom: .9rem;
  overflow: hidden; border-radius: 16px;
  background: linear-gradient(135deg, rgba(237,109,28,.10), rgba(255,255,255,.72));
}
.invite-card__hero img {
  display: block; width: 100%; height: auto; max-height: 15rem; object-fit: contain;
  filter: drop-shadow(0 .7rem 1rem rgba(116, 54, 15, .10));
  animation: invite-float 5.5s ease-in-out infinite;
}
.invite-card__hero > div { padding: 1rem 1rem 1rem 0; }
@keyframes invite-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
.invite-label { margin: 0; color: var(--ink-3); font-size: .74rem; font-weight: 700; }
.invite-code {
  margin: .2rem 0 .9rem; font-size: clamp(1.8rem, 7vw, 2.6rem);
  font-weight: 900; letter-spacing: .22em; color: var(--orange-soft);
}
.invite-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.invite-actions .button-like, .invite-actions button { padding: .6rem 1.05rem; font-size: .84rem; }
.invite-actions .sms-primary { background: var(--orange-deep); color: #fff; }
.invite-link { word-break: break-all; }

.sms-composer {
  margin-top: 1.2rem; padding: clamp(1rem, 4vw, 1.4rem);
  border: 1px solid rgba(237,109,28,.28); border-radius: 18px;
  background: linear-gradient(160deg, #fff, #fff8f2);
  box-shadow: inset 0 1px 0 #fff, 0 .7rem 2rem rgba(88,47,20,.08);
  animation: rise .28s ease-out both;
}
.sms-composer__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  margin-bottom: 1rem;
}
.sms-composer__head h3 { margin: 0; }
.platform-sender {
  display: inline-flex; align-items: center; min-height: 2rem; padding: .25rem .7rem;
  border-radius: 99px; background: rgba(237,109,28,.12); color: var(--orange-ink);
  font-size: .7rem; font-weight: 800; white-space: nowrap;
}
.sms-preview {
  position: relative; padding: 1rem 1.1rem; margin-bottom: 1.1rem;
  border: 1px solid var(--line); border-radius: 15px; background: var(--surface);
}
.sms-preview::after {
  content: ""; position: absolute; inset-inline-end: 1.2rem; bottom: -.48rem;
  width: .85rem; height: .85rem; background: inherit; border-inline-end: 1px solid var(--line);
  border-bottom: 1px solid var(--line); transform: rotate(45deg);
}
.sms-preview > span { color: var(--orange-ink); font-size: .66rem; font-weight: 800; }
.sms-preview p { margin: .35rem 0 0; color: var(--ink-2); font-size: .82rem; line-height: 1.95; }
.sms-preview b { color: var(--ink); font-size: .76rem; }
.recipient-entry__row { display: flex; align-items: stretch; gap: .5rem; }
.recipient-entry__row .field { flex: 1 1 14rem; }
.contact-picker {
  margin-top: .7rem; padding: .6rem .9rem; background: rgba(237,109,28,.10);
  border: 1px dashed rgba(179,74,6,.42); color: var(--orange-ink); box-shadow: none;
  font-size: .8rem;
}
.contact-picker:hover { background: rgba(237,109,28,.16); filter: none; }
.recipient-list {
  display: flex; flex-wrap: wrap; gap: .5rem; margin: .9rem 0 0; padding: 0; list-style: none;
}
.recipient-list li {
  display: inline-flex; align-items: center; gap: .65rem; padding: .45rem .55rem .45rem .8rem;
  border: 1px solid var(--line); border-radius: 99px; background: var(--surface);
}
.recipient-list span { display: grid; line-height: 1.35; }
.recipient-list strong { max-width: 10rem; overflow: hidden; text-overflow: ellipsis; font-size: .76rem; white-space: nowrap; }
.recipient-list small { color: var(--ink-3); font-size: .62rem; }
.recipient-remove {
  min-block-size: 1.75rem; width: 1.75rem; padding: 0; border-radius: 50%;
  background: var(--surface-2); color: var(--ink-3); box-shadow: none; font-size: 1rem;
}
.recipient-remove:hover { background: rgba(179,57,28,.10); color: var(--danger); filter: none; transform: none; }
.sms-consent {
  display: flex; align-items: flex-start; gap: .6rem; margin-top: 1rem;
  color: var(--ink-3); font-size: .72rem; line-height: 1.8; cursor: pointer;
}
.sms-consent input { width: 1rem; height: 1rem; margin-top: .32rem; accent-color: var(--orange-deep); }
.sms-composer__actions { display: flex; flex-wrap: wrap; gap: .55rem; margin-top: 1rem; }

@media (max-width: 38rem) {
  .invite-card__hero { grid-template-columns: 1fr; }
  .invite-card__hero img { max-height: 12rem; }
  .invite-card__hero > div { padding: 0 1rem 1rem; text-align: center; }
  .sms-composer__head { display: grid; }
  .platform-sender { width: fit-content; }
  .recipient-entry__row { flex-wrap: wrap; }
  .recipient-entry__row .field, .recipient-entry__row button { flex: 1 1 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .invite-card__hero img, .sms-composer { animation: none; }
}

.crew-panel:not(:empty) {
  padding: 1rem 1.2rem; margin-bottom: 1.2rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
}
.crew-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.crew-list li { display: flex; align-items: center; gap: .5rem; font-size: .88rem; }
.avatar {
  position: relative;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 2.55rem;
  height: 2.55rem;
  padding: 2px;
  border: 2px solid #fff;
  border-radius: 50%;
  background: linear-gradient(145deg, #fffaf4, #f0dac8);
  box-shadow: 0 0 0 1px rgba(127, 89, 62, .18), 0 .35rem .8rem rgba(99, 55, 24, .13);
  /* The neutral plate remains while a crest loads; a failed request is swapped
     to the local compass mark by installAvatarFallbacks(). */
}
.avatar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 50%;
}
.avatar--mini { width: 1.65rem; height: 1.65rem; padding: 1px; }
.avatar--small, .avatar--comment { width: 2.05rem; height: 2.05rem; }
.avatar--profile img { animation: avatar-idle 5.4s ease-in-out infinite; }
@keyframes avatar-idle {
  0%, 100% { transform: translateY(0) rotate(-.6deg); }
  50% { transform: translateY(-4px) rotate(.6deg); }
}
.avatar[data-avatar-role="you"] {
  box-shadow: 0 0 0 2px var(--orange), 0 .4rem .9rem rgba(179, 74, 6, .18);
}
.avatar[data-avatar-role="captain"] {
  box-shadow: 0 0 0 2px #c58c26, 0 .4rem .9rem rgba(115, 71, 11, .16);
}
.avatar[data-avatar-role="companion"] { box-shadow: 0 0 0 2px #91a77e, 0 .35rem .8rem rgba(56, 80, 49, .14); }
.avatar[data-avatar-role="resident"] { box-shadow: 0 0 0 2px var(--green), 0 .35rem .8rem rgba(49, 128, 92, .16); }
.avatar[data-avatar-role="pending"] {
  border-style: dashed;
  border-color: #b98563;
  filter: saturate(.72);
}

.waiting { display: flex; align-items: center; gap: .7rem; color: var(--ink-2); font-size: .9rem; }
.waiting p { margin: 0; }
.pulse {
  width: .7rem; height: .7rem; border-radius: 50%; background: var(--orange);
  animation: ping 1.8s ease-out infinite;
}
@keyframes ping {
  0% { box-shadow: 0 0 0 0 rgba(238,112,32,.6); }
  100% { box-shadow: 0 0 0 14px rgba(238,112,32,0); }
}

/* ---------------------------------------------------------------- *
 * Compass
 * ---------------------------------------------------------------- */

.compass-progress { height: 4px; border-radius: 99px; background: var(--surface-2); margin: 1rem 0 1.6rem; overflow: hidden; }
.compass-progress span { display: block; height: 100%; background: var(--orange); transition: width .4s cubic-bezier(.22,1,.36,1); }
.question { animation: rise .35s cubic-bezier(.22,1,.36,1) backwards; }
.question-count { margin: 0 0 .3rem; color: var(--ink-3); font-size: .74rem; font-weight: 700; }

/*
 * What the previous answer did.
 *
 * Deliberately quiet: a tinted strip, not a card, and no green tick. It is
 * feedback about the atlas, not praise for the player — the whole stage is
 * built on the finding that engagement-contingent reward costs intrinsic
 * motivation (d = −0.40) while specific verbal feedback earns it (d = +0.33).
 */
.answer-echo {
  display: flex; align-items: flex-start; gap: .45rem; flex-wrap: wrap;
  margin: 0 0 1rem;
  padding: .6rem .75rem;
  border-inline-start: 3px solid var(--orange);
  /* Logical, not `0 12px 12px 0`: the squared corners belong to the edge the
     rule is on, which is the right one in an RTL document. */
  border-radius: 12px;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: .86rem; line-height: 1.8;
  animation: rise .35s cubic-bezier(.22, 1, .36, 1) backwards;
}
.answer-echo > span { flex: 1 1 12rem; min-width: 0; }

/* ---------------------------------------------------------------- *
 * Atlas
 * ---------------------------------------------------------------- */

.atlas-grid { display: grid; gap: 1.4rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 60rem) { .atlas-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); } }
/* Sticky only where there are two columns. A grid item's sticky bounds are the
   grid CONTAINER, not its own row — so in the single-column phone layout the
   map rode down over the entire 12,000px city list, hint text and legend
   included, and every card scrolled through it. */
@media (min-width: 60rem) { .atlas-map { position: sticky; top: 5rem; align-self: start; } }

.filter-panel {
  padding: .9rem 1.1rem; margin-bottom: 1rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
}
.filter-panel summary {
  display: flex; align-items: center; gap: .5rem;
  min-block-size: 44px; cursor: pointer; font-weight: 700; font-size: .86rem;
  list-style: none;
}
.filter-panel summary::-webkit-details-marker { display: none; }
/* One chevron, turned by state, instead of the browser's triangle — the turn
   is the only hint this row folds. Closed points inline-start (left, in RTL). */
.filter-panel summary::before {
  content: ""; flex: none; width: .55rem; height: .55rem;
  border-inline-end: 2px solid var(--orange-soft); border-block-end: 2px solid var(--orange-soft);
  transform: rotate(45deg); transition: transform .2s cubic-bezier(.22, 1, .36, 1);
  margin-block-start: -.2rem;
}
.filter-panel:not([open]) summary::before { transform: rotate(-45deg); margin-block-start: 0; }
.filter-row { margin-top: .9rem; }
.filter-label { color: var(--ink-3); font-size: .74rem; font-weight: 700; }

/* Tiers, not a numbered ranking — see the comment on drawList in game.js. */
.tier { margin-bottom: 1.6rem; }
.tier-label {
  margin: 0 0 .2rem; font-size: .82rem; font-weight: 800;
  color: var(--orange-soft); letter-spacing: .01em;
}
.tier[data-tier="surprise"] .tier-label { color: var(--green); }
.tier .field-note { margin-bottom: .6rem; }

.quota-note {
  padding: .8rem 1rem; margin: 0 0 1rem;
  background: rgba(179, 57, 28, .07);
  border: 1px solid rgba(179, 57, 28, .26);
  border-radius: 12px; color: var(--ink-2); font-size: .84rem; line-height: 1.9;
}

.city-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }
.city-card {
  display: flex; gap: .9rem; padding: 1rem 1.1rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  transition: border-color .2s, background .2s;
}
.city-card:hover { border-color: var(--line-strong); }
.city-card.is-flash { border-color: var(--orange); background: rgba(238,112,32,.1); }
.city-body { flex: 1; min-width: 0; }
.city-body h4 { margin: 0 0 .4rem; font-size: 1.02rem; font-weight: 700; }
.reasons { list-style: none; margin: 0 0 .5rem; padding: 0; display: grid; gap: .25rem; }
.reasons li { color: var(--ink-2); font-size: .82rem; }
.community-note { margin: 0 0 .6rem; font-size: .76rem; color: var(--orange-soft); }
.community-note.muted { color: var(--ink-3); }
.city-actions { display: flex; flex-wrap: wrap; gap: .4rem; }
.city-actions button {
  min-block-size: 44px; padding: .55rem .85rem; font-size: .76rem; font-weight: 700;
  background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line);
}
.city-actions button:hover { color: var(--ink); border-color: var(--line-strong); filter: none; }
.city-actions button[aria-pressed="true"] { background: rgba(238,112,32,.2); border-color: var(--orange); color: var(--ink); }

.why-panel { margin-top: .8rem; padding-top: .8rem; border-top: 1px dashed var(--line); display: grid; gap: .4rem; }
.why-row { display: grid; grid-template-columns: minmax(0, 7rem) minmax(0, 1fr) auto; align-items: center; gap: .5rem; font-size: .76rem; }
.why-label { color: var(--ink-3); }
.why-bar { height: 6px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.why-bar i { display: block; height: 100%; width: var(--w, 0%); background: var(--orange); }

/* ---------------------------------------------------------------- *
 * Hometown survey and city room
 * ---------------------------------------------------------------- */

.manifesto {
  padding: 1.1rem 1.3rem; margin-bottom: 1.6rem;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: 18px;
  border-top: 3px solid var(--green);
  box-shadow: var(--shadow-1);
}
.manifesto p { margin: 0 0 .6rem; color: var(--ink-2); font-size: .92rem; }
.manifesto p:last-child { margin: 0; }

.signal-form { display: grid; gap: 1.2rem; max-width: 40rem; }
.signal-q { margin: 0; padding: 0; border: 0; }
.signal-q legend { padding: 0; margin-bottom: .3rem; font-weight: 700; font-size: .92rem; }
.signal-q .field { max-width: 18rem; }

.scale-row { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
.scale-option {
  padding: .45rem .85rem; background: var(--surface); border: 1px solid var(--line);
  border-radius: 99px; font-size: .8rem; cursor: pointer;
}
.scale-option:has(:checked) { border-color: var(--orange); background: rgba(238,112,32,.16); }
.scale-option input { position: absolute; opacity: 0; pointer-events: none; }

.room-grid { display: grid; gap: 1.2rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 52rem) { .room-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.room-block {
  padding: 1.2rem 1.3rem; margin-bottom: 1.2rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 18px;
}
.room-grid .room-block { margin-bottom: 0; }

.fact-list { margin: .6rem 0 0; display: grid; gap: .5rem; }
.fact-list div { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem; }
.fact-list dt { color: var(--ink-3); font-size: .78rem; min-width: min(9rem, 45%); }
.fact-list dd { margin: 0; font-size: .88rem; font-weight: 600; }

.category-list { list-style: none; margin: .9rem 0 0; padding: 0; display: grid; gap: .25rem; }
.category-list li { display: flex; justify-content: space-between; font-size: .8rem; color: var(--ink-2); }

.quorum-bar { height: 8px; border-radius: 99px; background: var(--surface-2); overflow: hidden; margin: .6rem 0; }
.quorum-bar i { display: block; height: 100%; width: var(--w, 0%); background: var(--orange); transition: width .5s; }

.post-form { display: grid; gap: .6rem; max-width: 34rem; margin: .8rem 0 1.4rem; }
.post-form button[type="submit"] { justify-self: start; }
.post-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }
.post-list li { padding: .8rem 1rem; background: var(--surface-2); border-radius: 14px; }
.post-list li.muted { background: none; padding-inline: 0; font-size: .84rem; }
.post-head { display: flex; align-items: center; gap: .4rem; margin-bottom: .3rem; font-size: .8rem; }
.post-list p { margin: 0; color: var(--ink-2); font-size: .88rem; }

.city-dialog {
  width: min(46rem, 92vw); max-height: 86vh; padding: 0; overflow: auto;
  margin-block: auto; margin-inline: auto;
  background: var(--ground-2); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 22px;
}
.city-dialog::backdrop { background: rgba(10, 7, 6, .72); backdrop-filter: blur(4px); }
/* Direct child only: the city page mounts its own <header class="city-hero">
   inside the body, and the bare `header` selector made that card sticky too —
   on a phone it pinned itself over this bar and the close button, and half the
   dialog scrolled behind an unrelated card. */
.city-dialog > header {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1rem 1.3rem; background: var(--ground-2); border-bottom: 1px solid var(--line);
}
.city-dialog h2 { margin: 0; font-size: 1.1rem; }
.city-dialog [data-close] {
  min-inline-size: 44px; min-block-size: 44px; padding: .2rem .7rem;
  background: none; color: var(--ink-2); font-size: 1.4rem;
}
.dialog-body {
  padding: 1.2rem calc(1.3rem + env(safe-area-inset-right))
           calc(1.6rem + env(safe-area-inset-bottom)) calc(1.3rem + env(safe-area-inset-left));
}

/* ---------------------------------------------------------------- *
 * Completion
 * ---------------------------------------------------------------- */

.score-row { display: flex; flex-wrap: wrap; gap: 1.6rem; margin: 1.6rem 0; }
.score-row div { display: grid; }
.score-row b { font-size: 1.9rem; font-weight: 900; color: var(--orange-soft); line-height: 1.2; }
.score-row span { color: var(--ink-3); font-size: .76rem; font-weight: 700; }

.comparison { margin-top: 1rem; padding-top: 1rem; border-top: 1px dashed var(--line); }
.comparison-block { margin-bottom: 1rem; }
.comparison-block h4 { margin: 0 0 .5rem; font-size: .88rem; }

.pin-list { list-style: none; margin: 0 0 1.4rem; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.pin-list li { padding: .4rem .9rem; background: var(--surface); border: 1px solid var(--line); border-radius: 99px; font-size: .84rem; }

.locked-teaser {
  padding: 1rem 1.2rem; margin-bottom: .9rem;
  background: var(--surface); border: 1px dashed var(--line-strong); border-radius: 16px;
}
.locked-teaser h3 { margin: 0 0 .3rem; font-size: .95rem; }
.locked-teaser p { margin: 0 0 .6rem; color: var(--ink-3); font-size: .84rem; }

/* ---------------------------------------------------------------- *
 * Toasts and noscript
 * ---------------------------------------------------------------- */

.toasts {
  position: fixed; inset-block-end: calc(1rem + env(safe-area-inset-bottom)); z-index: 40;
  inset-inline-start: calc(1rem + env(safe-area-inset-left));
  inset-inline-end: calc(1rem + env(safe-area-inset-right));
  display: grid; gap: .5rem; justify-items: center; pointer-events: none;
}
/*
 * The whole feedback channel of the game runs through here, and it had no
 * `color` at all: a near-black ground left over from the dark theme, with the
 * page's own dark ink inherited on top of it. 1.06:1 — "کپی شد", "ثبت نشد",
 * "چراغت رسید" and every 409 were painted in ink you cannot see. It is a small
 * white card now, like every other surface in this theme.
 */
.toast {
  max-inline-size: min(30rem, 100%);
  padding: .65rem 1.1rem;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 99px;
  box-shadow: var(--shadow-2);
  font-size: .84rem; font-weight: 600; line-height: 1.7;
  animation: rise .3s cubic-bezier(.22,1,.36,1) both;
}
.toast[data-tone="error"] {
  border-color: var(--danger); color: var(--danger);
  background: #fffaf8;
}
.toast.is-leaving { opacity: 0; transition: opacity .5s; }

.noscript { max-width: 40rem; margin: 3rem auto; padding: 1.2rem; text-align: center; color: var(--ink-2); }

/* ---------------------------------------------------------------- *
 * Motion preference
 *
 * Not a decoration toggle: for someone with vestibular sensitivity these
 * animations are the difference between a usable page and a hostile one.
 * ---------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .province, .city-card, button, .button-like, .scale-option, .choice-card { transform: none !important; }
}

/* ---------------------------------------------------------------- *
 * قرار بعدی — the closing commitment
 *
 * Framed like a note the player wrote, not like a form the product filled in:
 * the committed state is the sentence, large and quiet, with everything that
 * produced it gone. Naming when and what is the whole intervention
 * (Gollwitzer & Sheeran, d = 0.65), so the sentence is the object on screen.
 * ---------------------------------------------------------------- */

.plan {
  margin: 1.6rem 0 0;
  padding: 1.1rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 3px solid var(--orange);
  border-radius: 18px;
  box-shadow: var(--shadow-1);
}
.plan h3 { margin: 0 0 .4rem; font-size: 1.05rem; }
.plan > p { margin: 0 0 .9rem; color: var(--ink-2); font-size: .92rem; }

.plan-group { border: 0; margin: 0 0 1rem; padding: 0; }
.plan-group legend {
  padding: 0; margin-bottom: .45rem;
  font-size: .78rem; font-weight: 800; color: var(--orange-ink);
}
.plan-options { display: grid; gap: .5rem; }
.plan-options .choice-card { text-align: start; }

.plan-preview {
  margin: 0 0 .8rem; min-height: 1.6rem;
  font-size: 1rem; font-weight: 700; line-height: 1.9;
}

/* The global `opacity: .55` on a disabled button is fine over ink but not over
   the brand orange: it drags 5.5:1 down under the bar, on the one control whose
   label is the reason it cannot be pressed yet. So the waiting state is a plain
   surface with ink-3 text, and it goes orange the moment it can be used. */
#plan-save:disabled {
  opacity: 1;
  background: var(--surface-2);
  color: var(--ink-3);
  border: 1px solid var(--line);
  box-shadow: none;
}

.plan--set .plan-sentence {
  margin: 0 0 .6rem;
  padding: .8rem .9rem;
  background: var(--surface-2);
  border-inline-start: 3px solid var(--orange);
  border-radius: 14px;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  font-size: 1.05rem; font-weight: 700; line-height: 1.9;
}
.plan--set .field-note { margin-bottom: .7rem; }

/* ── Blind commit, then reveal ─────────────────────────────────── */

.reveal-wait {
  display: flex; align-items: flex-start; gap: .7rem; margin: 0;
  padding: .9rem 1rem; background: var(--surface-2); border-radius: 14px;
  color: var(--ink-2); font-size: .86rem; line-height: 1.9;
}
.seal {
  display: grid; place-items: center; flex: none;
  width: 1.9rem; height: 1.9rem; border-radius: 50%;
  background: rgba(238, 112, 32, .18); color: var(--orange-ink);
  font-weight: 800; font-size: .9rem;
}

.compare-grid {
  display: grid; gap: .9rem; margin: .7rem 0;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}
.compare-label {
  margin: 0 0 .4rem; color: var(--ink-3);
  font-size: .7rem; font-weight: 800; letter-spacing: .03em;
}
.pin-chip {
  display: inline-block; padding: .35rem .8rem; border-radius: 99px;
  background: var(--surface); border: 1px solid var(--line);
  font-size: .82rem; font-weight: 600; color: var(--ink);
}
button.pin-chip { cursor: pointer; border-color: var(--orange); background: rgba(238, 112, 32, .14); }
button.pin-chip:hover { filter: brightness(1.15); transform: none; }
.pin-chip.is-shared { border-color: var(--green); color: var(--green-ink); background: rgba(49, 128, 92, .1); }
.pin-chip.is-mine { border-color: var(--line-strong); }

/* ── The ordinary Tuesday ──────────────────────────────────────── */

.tuesday-q {
  margin: 0 0 1rem; padding: 1rem;
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--line); border-radius: 18px;
  box-shadow: var(--shadow-1);
}
.tuesday-q legend {
  padding: 0 .2rem; margin-bottom: .5rem; font-size: 1rem; font-weight: 700;
}
.tuesday-q-body {
  display: grid; grid-template-columns: minmax(7.5rem, 10rem) minmax(0, 1fr);
  align-items: center; gap: clamp(.7rem, 3vw, 1.3rem);
}
.question-art { margin: 0; pointer-events: none; }
.question-art img {
  display: block; width: 100%; height: auto; border-radius: 1rem;
  mix-blend-mode: multiply;
}
.tuesday-q .choice-grid { margin-top: 0; grid-template-columns: minmax(0, 1fr); }

@media (max-width: 34rem) {
  .tuesday-q { padding: .8rem; }
  .tuesday-q-body { grid-template-columns: minmax(5.8rem, 7rem) minmax(0, 1fr); gap: .6rem; }
}

/* ── SURE check-in ─────────────────────────────────────────────── */

.sure-form { display: grid; gap: 1.3rem; max-width: 34rem; }
.sure-item { margin: 0; padding: 0; border: 0; }
.sure-item legend { padding: 0; margin-bottom: .55rem; font-size: .95rem; font-weight: 600; line-height: 1.8; }
.sure-toggle { display: flex; gap: .45rem; }
.sure-toggle button {
  flex: 1; padding: .6rem 1rem; background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--line); border-radius: 12px; font-size: .86rem; font-weight: 600;
}
.sure-toggle button:hover { border-color: var(--orange-soft); color: var(--ink); filter: none; transform: none; }
.sure-toggle button[aria-checked="true"] {
  background: rgba(238, 112, 32, .18); border-color: var(--orange); color: var(--ink);
}
#sure-submit { justify-self: start; }

/* ── The day, restated on every city card ──────────────────────── */

.day-lines {
  list-style: none; margin: .1rem 0 .6rem; padding: .55rem .8rem;
  background: rgba(49, 128, 92, .06);
  border-inline-start: 2px solid rgba(49, 128, 92, .38);
  border-radius: 0 8px 8px 0;
  display: grid; gap: .25rem;
}
.day-lines li { color: var(--ink-2); font-size: .8rem; line-height: 1.8; }

/* ── City page ─────────────────────────────────────────────────────
   Three layers of knowledge, kept visually separate because they are not
   equally trustworthy: counted, estimated, and reported by residents.
   ───────────────────────────────────────────────────────────────── */

.city-page { display: grid; gap: 1.4rem; }

.city-hero {
  display: flex; flex-wrap: wrap; gap: 1.2rem; justify-content: space-between; align-items: flex-start;
  padding: 1.3rem 1.4rem;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: 20px;
  border-top: 3px solid var(--orange);
  box-shadow: var(--shadow-1);
}
.city-hero-main { flex: 1 1 16rem; min-width: 0; }
.city-eyebrow { margin: 0 0 .2rem; color: var(--orange-soft); font-size: .74rem; font-weight: 800; }
.city-hero h3 { margin: 0 0 .4rem; font-size: clamp(1.4rem, 4vw, 1.9rem); }
.city-lede { margin: 0; color: var(--ink-2); font-size: .88rem; line-height: 1.9; max-width: 34rem; }

.city-hero-stats { display: flex; flex-wrap: wrap; gap: 1.4rem; }
.city-hero-stats .stat { display: grid; gap: .1rem; }
.city-hero-stats b { font-size: 1.5rem; font-weight: 900; color: var(--orange-deep); line-height: 1.2; }
.city-hero-stats b small { font-size: .8rem; font-weight: 700; opacity: .7; }
.city-hero-stats span { color: var(--ink-3); font-size: .7rem; font-weight: 700; }

.city-verdict { display: grid; gap: .6rem; }
.city-verdict p { max-width: none; margin: 0; font-size: .88rem; line-height: 1.95; color: var(--ink-2); }
.verdict-tag {
  display: inline-block; margin-inline-end: .4rem; padding: .1rem .55rem;
  border-radius: 99px; font-size: .68rem; font-weight: 800;
}
.verdict-tag.is-fit { background: rgba(49, 128, 92, .12); color: var(--green); }
.verdict-tag.is-watch { background: rgba(179, 57, 28, .1); color: var(--danger); }

/* Scrollable, not wrappable: four labels at 360px otherwise fold into
   three-line towers. Material's scrolling-tabs shape, minus the chrome.
   The is-clipped-* classes (set in game.js) fade the edge that hides a tab,
   so an off-screen tab announces itself. In RTL, "end" is the left edge. */
.city-tabs { display: flex; gap: .3rem; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
.city-tabs::-webkit-scrollbar { display: none; }
.city-tabs.is-clipped-end { mask-image: linear-gradient(to left, #000 calc(100% - 2.2rem), transparent); }
.city-tabs.is-clipped-start { mask-image: linear-gradient(to right, #000 calc(100% - 2.2rem), transparent); }
.city-tabs.is-clipped-end.is-clipped-start {
  mask-image: linear-gradient(to left, #000 calc(100% - 2.2rem), transparent),
              linear-gradient(to right, #000 calc(100% - 2.2rem), transparent);
  mask-composite: intersect;
}
.city-tabs button {
  flex: none; white-space: nowrap;
  min-block-size: 44px; padding: .6rem 1rem; background: none; color: var(--ink-3);
  border: 0; border-bottom: 2px solid transparent; border-radius: 0;
  font-size: .86rem; font-weight: 700;
}
.city-tabs button:hover { color: var(--ink-2); transform: none; filter: none; }
.city-tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--orange); }

.city-tab { display: grid; gap: .5rem; }
.city-tab h4 { margin: 1rem 0 .2rem; font-size: .95rem; font-weight: 700; }
.city-tab h4:first-child { margin-top: 0; }
.tab-intro { margin: 0 0 .5rem; color: var(--ink-2); font-size: .86rem; max-width: 40rem; }

.uni-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .3rem; max-height: 26rem; overflow-y: auto; }
.uni-list li {
  display: flex; align-items: baseline; gap: .6rem;
  padding: .5rem .75rem; background: var(--surface); border-radius: 10px; font-size: .84rem;
}
.uni-cat {
  flex: none; min-width: 5.2rem; font-size: .68rem; font-weight: 800;
  color: var(--ink-3); letter-spacing: .01em;
}
.uni-cat[data-cat="public"], .uni-cat[data-cat="medical"] { color: var(--green); }
.uni-cat[data-cat="azad"], .uni-cat[data-cat="nonprofit"] { color: var(--orange-soft); }
.uni-name { flex: 1; min-width: 0; }
.uni-link { flex: none; font-size: .74rem; }

.cost-bars { list-style: none; margin: .4rem 0; padding: 0; display: grid; gap: .4rem; }
.cost-bars li { display: grid; grid-template-columns: 6rem 1fr auto; align-items: center; gap: .6rem; font-size: .8rem; }
.cost-label { color: var(--ink-3); }
.cost-bar { height: 7px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.cost-bar i { display: block; height: 100%; width: var(--w, 0%); background: linear-gradient(90deg, var(--orange), var(--orange-deep)); }
.cost-value { font-variant-numeric: tabular-nums; color: var(--ink-2); }

.place-list, .ask-list { list-style: none; margin: .3rem 0; padding: 0; display: grid; gap: .35rem; }
.place-list li, .ask-list li { font-size: .84rem; color: var(--ink-2); }
.ask-list li { padding-inline-start: 1rem; position: relative; }
.ask-list li::before { content: "؟"; position: absolute; inset-inline-start: 0; color: var(--orange-soft); font-weight: 800; }

.quorum-head { margin: 0 0 .5rem; font-size: 1rem; font-weight: 700; }
.resident-call {
  margin: .7rem 0 0; padding: .6rem .8rem; border-radius: 10px;
  background: rgba(49, 128, 92, .09); color: var(--green); font-size: .84rem;
}

.post-actions { display: flex; align-items: center; gap: .8rem; }
.post-actions .field-note { margin: 0; font-variant-numeric: tabular-nums; }
.empty-thread {
  background: none !important; padding: 1rem 0 !important;
  border-top: 1px dashed var(--line); color: var(--ink-3);
}
.empty-thread p { margin: 0 0 .4rem; font-size: .86rem; line-height: 1.9; }
.post-head .avatar { width: 2rem; height: 2rem; }

.city-page-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: .8rem;
  padding-top: 1rem; border-top: 1px solid var(--line);
}
.city-page-actions .field-note { margin: 0; }

/* ── Explorer ──────────────────────────────────────────────────────
   Free browsing over the whole atlas, so the database is visible on its
   own terms and not only through the compass's opinion of it.
   ───────────────────────────────────────────────────────────────── */

.explorer-controls {
  display: grid; gap: .5rem; margin: 1rem 0 .6rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.explorer-list {
  list-style: none; margin: .4rem 0 0; padding: 0;
  display: grid; gap: .3rem; max-height: 24rem; overflow-y: auto;
}
.explorer-list button {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  width: 100%; padding: .6rem .85rem; text-align: start;
  background: var(--surface); color: var(--ink);
  border: 1px solid transparent; border-radius: 10px; font-weight: 500;
}
.explorer-list button:hover {
  background: var(--surface-2); border-color: var(--line-strong); transform: none; filter: none;
}
.explorer-name { display: flex; align-items: baseline; gap: .5rem; min-width: 0; }
.explorer-name strong { font-size: .92rem; }
.explorer-meta {
  flex: 1 1 auto; min-width: 0; color: var(--ink-3); font-size: .74rem;
  font-variant-numeric: tabular-nums;
}
.explorer-dialog { width: min(38rem, 94vw); }

/* ── The guide rail ────────────────────────────────────────────────
   One line, always present, answering "where am I and why am I being
   asked this". Games lose people at the seams between steps.
   ───────────────────────────────────────────────────────────────── */

.scene-guide {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1rem;
  max-width: 62rem; margin: 0 auto; padding: .7rem clamp(.9rem, 4vw, 2rem) 0;
  color: var(--ink-3); font-size: .8rem;
}
.scene-guide b {
  padding: .12rem .55rem; border-radius: 99px;
  background: rgba(238, 112, 32, .16); color: var(--orange-soft);
  font-size: .68rem; font-weight: 800;
}

/* ── Discussion ────────────────────────────────────────────────────
   The resident badge is the whole basis of this section's credibility,
   so it is stamped server-side and given real visual weight here.
   ───────────────────────────────────────────────────────────────── */

.resident-badge-note {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
  margin: 0 0 .8rem; padding: .6rem .8rem; max-width: none;
  background: rgba(49, 128, 92, .07);
  border-inline-start: 2px solid var(--green); border-radius: 0 10px 10px 0;
  color: var(--ink-2); font-size: .82rem; line-height: 1.85;
}

.topic-row { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .7rem; }
.topic-label { color: var(--ink-3); font-size: .74rem; font-weight: 700; }
.topic-row .chips { margin-top: 0; }

.thread-filter { margin: 1.2rem 0 .6rem; padding-top: .8rem; border-top: 1px solid var(--line); }

.post-list li.is-resident {
  background: linear-gradient(90deg, rgba(49, 128, 92, .08), var(--surface-2));
  border-inline-start: 2px solid var(--green);
}
.post-topic { color: var(--ink-3); font-size: .7rem; }
.post-head { flex-wrap: wrap; }
.empty-thread .chips { margin-top: .8rem; }
.empty-thread .chip-button { text-align: start; white-space: normal; line-height: 1.7; }

/* ---- city page: verdict lists, climate grid, verified transit ---------- *
 * Two short lists beat one paragraph: a candidate comparing six cities scans
 * bullets. Colour never carries meaning alone — every state also has a word. */
.city-verdict { display: grid; gap: .9rem; }
.verdict-col { display: grid; gap: .4rem; }
.city-verdict ul { margin: 0; padding-inline-start: 1.1rem; display: grid; gap: .35rem; }
.city-verdict li { font-size: .86rem; line-height: 1.9; color: var(--ink-2); }
.city-verdict li::marker { color: var(--ink-3); }
.field-note.is-callout {
  border-inline-start: 2px solid var(--line);
  padding-inline-start: .6rem;
  color: var(--ink-2);
}

.fact-grid {
  list-style: none; margin: .6rem 0 0; padding: 0;
  display: grid; gap: .5rem;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}
.fact-grid li {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: .6rem; padding: .55rem .65rem;
  display: grid; gap: .2rem;
}
.fact-grid span { font-size: .7rem; color: var(--ink-3); }
.fact-grid b { font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.fact-grid small { font-size: .7rem; color: var(--ink-3); margin-inline-start: .1rem; }

.transit-list { list-style: none; margin: .6rem 0 0; padding: 0; display: grid; gap: .35rem; }
.transit-list li {
  display: flex; align-items: baseline; gap: .5rem;
  padding: .4rem .55rem; border-radius: .5rem; background: var(--surface-2);
  font-size: .82rem;
}
.transit-list span { color: var(--ink-3); min-inline-size: 5.5rem; }
.transit-list b[data-tone="good"] { color: var(--green-ink); }
.transit-list b[data-tone="soft"] { color: var(--ink-3); }
.transit-list em { font-style: normal; color: var(--ink-3); font-size: .76rem; }

.explorer-filters { margin-block: .2rem .6rem; }
.chip-toggle {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .3rem .6rem; border: 1px solid var(--line); border-radius: 999px;
  font-size: .78rem; color: var(--ink-2); cursor: pointer;
}
.chip-toggle:has(input:checked) { border-color: var(--green); color: var(--green-ink); }
.chip-toggle input { accent-color: var(--green); inline-size: .85rem; block-size: .85rem; }

/* The tag is a label on the list, not a banner across it. */
.verdict-col .verdict-tag { justify-self: start; inline-size: fit-content; }

/* ---------------------------------------------------------------- *
 * Importance bands
 *
 * Three shelves, drawn identically on purpose. Any colour or weight that
 * made one band look better than another would smuggle a ranking back in
 * through the stylesheet after the markup refused to write one.
 * ---------------------------------------------------------------- */
.band-picker { position: relative; display: inline-block; }
.band-menu {
  position: absolute; inset-inline-start: 0; top: calc(100% + .4rem); z-index: 20;
  min-width: 15rem; display: grid; gap: .2rem; padding: .4rem;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: 14px; box-shadow: var(--shadow-2);
  animation: band-menu-in .18s cubic-bezier(.22, 1, .36, 1) backwards;
  transform-origin: top center;
}
@keyframes band-menu-in { from { opacity: 0; transform: translateY(-4px) scale(.98); } }
/* A 15rem flyout anchored inside a card overflows a 320px viewport and drags
   the whole page sideways to reveal itself. Below 30rem it is a bottom sheet:
   full width, thumb-reachable, nothing shifts. */
@media (max-width: 30rem) {
  .band-menu {
    position: fixed; top: auto; z-index: 45;
    inset-inline: .5rem; bottom: calc(.5rem + env(safe-area-inset-bottom));
    max-height: 60vh; overflow-y: auto;
    padding: .6rem; border-radius: 18px;
    animation-name: band-sheet-in;
    transform-origin: bottom center;
  }
  @keyframes band-sheet-in { from { opacity: 0; transform: translateY(14px); } }
}
.band-menu button {
  display: grid; gap: .1rem; width: 100%; text-align: start;
  padding: .5rem .7rem; background: transparent; border: 1px solid transparent;
  border-radius: 10px; font: inherit; color: inherit; cursor: pointer;
}
.band-menu button b { font-size: .9rem; font-weight: 700; }
.band-menu button span { font-size: .76rem; color: var(--ink-3); }
.band-menu button:hover { background: var(--surface-2); border-color: var(--line); transform: none; filter: none; }
.band-menu button[aria-pressed="true"] { border-color: var(--orange-soft); }
.band-menu .band-drop { margin-top: .2rem; border-top: 1px dashed var(--line); border-radius: 0 0 10px 10px; font-size: .82rem; }

.profile-bands { display: grid; gap: 1.1rem; margin: 1.4rem 0; }
.profile-band h3 { margin: 0 0 .5rem; font-size: .96rem; font-weight: 800; }
.profile-band h3 .muted { font-weight: 500; font-size: .8rem; }
.profile-band .pin-list { flex-direction: column; gap: .4rem; margin-bottom: 0; }
.profile-band .pin-list li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
  width: 100%; border-radius: 12px; padding: .5rem .8rem;
}
.pin-why { font-size: .78rem; color: var(--orange-soft); }
.pin-narrators { font-size: .76rem; }

/* ---------------------------------------------------------------- *
 * "My list" — the header button and the drawer behind it
 *
 * The stage asks people to curate a list; until this existed they could
 * not look at it until the closing screen. It is a drawer rather than a
 * scene so that leaving it never costs the place you were in.
 * ---------------------------------------------------------------- */
.hud-list {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .4rem .8rem; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-radius: 99px;
  font: inherit; font-size: .82rem; font-weight: 700; color: inherit; cursor: pointer;
}
.hud-list:hover { border-color: var(--orange-soft); filter: none; transform: none; }
.hud-list b {
  min-width: 1.5rem; padding: .05rem .35rem; border-radius: 99px;
  background: rgba(238, 112, 32, .22); color: var(--orange-ink); text-align: center;
}
.hud-list.is-empty b { background: var(--surface-2); color: inherit; }

/* The music switch: the same pill, pressed state painted in the brand orange
 * with white text (7.1:1 on #c14f0c-ish ink; see the contrast notes above). */
.hud-music-group { position: relative; display: inline-flex; align-items: stretch; gap: 2px; }
.hud-music { min-height: 44px; border-start-end-radius: 6px; border-end-end-radius: 6px; }
.hud-music-style { min-height: 44px; min-width: 44px; padding: .4rem .5rem; justify-content: center;
  border-start-start-radius: 6px; border-end-start-radius: 6px; }
.hud-music-style[aria-expanded="true"] { border-color: var(--orange-soft); background: rgba(238, 112, 32, .12); }
.hud-music[aria-pressed="true"] { background: var(--orange-ink, #c14f0c); border-color: var(--orange-ink, #c14f0c); color: #fff; }
.hud-music[aria-pressed="true"] .hud-music-off, .hud-music[aria-pressed="false"] .hud-music-on { display: none; }

/* ---------------------------------------------------------------- *
 * The scene navigator
 *
 * A light popover off the HUD, not a modal: it lists the steps already
 * reached (locked ones are simply absent — no fake affordances) and two
 * quick exits, پنل and اطلس. Text colours are the *-ink tokens, which the
 * notes at the top of this file certify at 4.5:1 or better on white and on
 * the tinted orange surface.
 * ---------------------------------------------------------------- */
.hud-scenes { position: relative; }
.scene-menu {
  animation: band-menu-in .18s cubic-bezier(.22, 1, .36, 1) backwards;
  transform-origin: top center;
  position: absolute; top: calc(100% - .3rem);
  inset-inline-end: calc(clamp(.8rem, 3vw, 2rem) + env(safe-area-inset-right));
  z-index: 30;
  width: min(24rem, calc(100vw - 1.6rem));
  /* The panel itself does not scroll: the music styles open out of it as their
     own popover, and a scroll box here would cut them off. It is a column that
     stops at the viewport, and the one part that can grow without bound — the
     step list — takes the leftover room and scrolls inside it. */
  display: flex; flex-direction: column;
  max-block-size: calc(100dvh - 7rem);
  padding: .9rem;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-2);
}

/* Each section names itself before it lists anything. */
.scene-menu-title {
  flex: none;
  margin: 1rem 0 .45rem;
  color: var(--ink-3); font-size: .7rem; font-weight: 800; letter-spacing: .04em;
}
.scene-menu-rows { display: grid; gap: .4rem; }
/* Inside the menu a pill becomes a row: full width, label first, count last. */
.scene-menu .hud-list {
  width: 100%; min-block-size: 44px;
  justify-content: flex-start; gap: .55rem;
  padding: .55rem .75rem; border-radius: 12px;
  font-size: .86rem;
}
.scene-menu .hud-list b { margin-inline-start: auto; }
.scene-menu .hud-music-group { display: flex; width: 100%; }
.scene-menu .hud-music { flex: 1 1 auto; justify-content: flex-start; }
/* The style picker stays a square next to the switch; stretched across the row
   it read as an empty field rather than a control. */
.scene-menu .hud-music-style { flex: 0 0 44px; min-width: 44px; justify-content: center; }
#scene-menu-list { flex: 1 1 auto; min-height: 4rem; overflow-y: auto; }
.scene-menu-note {
  margin: 0 0 .6rem; padding-bottom: .6rem;
  border-bottom: 1px dashed var(--line);
  color: var(--ink-3); font-size: .76rem; line-height: 1.7;
}
/* The way into the map screen, set apart from the jump list under it: this
   one leaves the dropdown, the others stay inside it. */
.scene-menu-map {
  flex: none;
  display: flex; flex-direction: column; align-items: flex-start; gap: .15rem;
  width: 100%; min-block-size: 44px; margin-bottom: .7rem; padding: .65rem .8rem;
  background: rgba(238, 112, 32, .1);
  border: 1px solid var(--orange); border-radius: 12px;
  color: var(--ink); text-align: start; line-height: 1.6;
}
.scene-menu-map:hover { background: rgba(238, 112, 32, .16); filter: none; transform: none; }
.scene-menu-map b { font-size: .84rem; font-weight: 800; }
.scene-menu-map span { font-size: .74rem; font-weight: 500; color: var(--ink-3); }

.scene-menu ul { display: grid; align-content: start; gap: .25rem; margin: 0 0 .8rem; padding: 0; list-style: none; }
/* number · name and why · standing — one row, three columns, so the eye can
   run down the numbers without reading a word. */
.scene-menu li button {
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr) auto;
  align-items: center; gap: .55rem;
  width: 100%; min-block-size: 44px; padding: .55rem .7rem;
  background: none; border: 1px solid transparent; border-radius: 10px;
  color: var(--ink); font-weight: 500; text-align: start; line-height: 1.6;
}
.scene-menu li button i {
  display: grid; place-items: center;
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  background: var(--surface-2); color: var(--ink-3);
  font-style: normal; font-size: .72rem; font-weight: 800;
}
.scene-menu-step { display: grid; gap: .1rem; min-width: 0; }
.scene-menu li button em {
  font-style: normal; font-size: .66rem; font-weight: 700;
  color: var(--ink-3); white-space: nowrap;
}
.scene-menu li button[aria-current="true"] i { background: var(--orange); color: #fff; }
.scene-menu li button[aria-current="true"] em { color: var(--orange-ink); }
.scene-menu li button:hover {
  background: var(--surface-2); border-color: var(--orange-soft);
  filter: none; transform: none;
}
.scene-menu li button b { font-size: .82rem; font-weight: 800; color: var(--orange-ink); }
.scene-menu-step span { font-size: .74rem; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; }
.scene-menu li button[aria-current="true"] {
  background: rgba(238, 112, 32, .12); border-color: var(--orange);
}
.scene-menu-links { display: flex; gap: .5rem; }
.scene-menu-links a {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 1; min-block-size: 44px; padding: .4rem .8rem;
  border: 1px solid var(--line-strong); border-radius: 99px;
  color: var(--ink-2); font-size: .82rem; font-weight: 700; text-decoration: none;
}
.scene-menu-links a:hover { border-color: var(--orange-soft); color: var(--orange-ink); }

.drawer {
  position: fixed; inset: 0; z-index: 60;
  display: flex; justify-content: flex-start;
  background: rgba(70, 48, 34, .34);
  animation: backdrop-in .2s ease both;
}
/* The list slides in from its own edge; display:none → flex restarts the
   animation each open, so this needs no JS. fa is the only locale, so the
   physical direction is safe. */
.drawer-panel {
  animation: drawer-in .26s cubic-bezier(.22, 1, .36, 1) backwards;
  width: min(30rem, 100%); max-height: 100%; overflow-y: auto;
  padding: calc(1.2rem + env(safe-area-inset-top)) calc(1.3rem + env(safe-area-inset-right))
           calc(2.4rem + env(safe-area-inset-bottom)) calc(1.3rem + env(safe-area-inset-left));
  background: var(--surface); border-inline-end: 1px solid var(--line-strong);
  box-shadow: 0 0 48px rgba(70, 48, 34, .22);
}
.drawer-top {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin: -1.2rem -1.3rem 1rem; padding: 1rem 1.3rem;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.drawer-top h2 { margin: 0; font-size: 1.05rem; font-weight: 800; }
@keyframes drawer-in { from { opacity: .4; transform: translateX(10%); } }

.profile-identity {
  display: grid;
  grid-template-columns: 5.4rem minmax(0, 1fr);
  gap: .9rem 1rem;
  align-items: center;
  margin-bottom: 1.15rem;
  padding: 1rem;
  overflow: hidden;
  border: 1px solid rgba(237, 109, 28, .24);
  border-radius: 20px;
  background:
    radial-gradient(circle at 12% 20%, rgba(237, 109, 28, .16), transparent 38%),
    linear-gradient(145deg, #fffdf9, #fff6ed);
}
.avatar--profile { width: 5.2rem; height: 5.2rem; padding: 4px; }
.profile-identity__copy { min-width: 0; }
.profile-identity__copy h3 { margin: 0; font-size: 1.22rem; overflow-wrap: anywhere; }
.profile-identity__copy > p:last-child { margin: .25rem 0 0; color: var(--ink-3); font-size: .76rem; line-height: 1.8; }
.profile-identity__facts {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .55rem;
  margin: 0;
}
.profile-identity__facts div { padding: .55rem .7rem; border-radius: 12px; background: rgba(255, 255, 255, .7); }
.profile-identity__facts dt { color: var(--ink-3); font-size: .66rem; font-weight: 700; }
.profile-identity__facts dd { margin: .2rem 0 0; font-size: .9rem; font-weight: 850; }

.list-empty { padding: 1.4rem 0; }
.list-empty p { margin: 0 0 .5rem; }

.band-menu-note {
  margin: .1rem .3rem .5rem; padding-bottom: .5rem;
  border-bottom: 1px dashed var(--line);
  font-size: .76rem; line-height: 1.7; color: var(--ink-3);
}
.band-menu button[disabled] { opacity: .5; cursor: not-allowed; }
.band-menu button[disabled]:hover { background: transparent; border-color: transparent; }

.band-count {
  margin-inline-start: auto; padding: .1rem .5rem;
  border: 1px solid var(--line); border-radius: 99px;
  font-size: .72rem; font-weight: 600;
}
.profile-band h3 { display: flex; align-items: baseline; gap: .5rem; }

/* ---------------------------------------------------------------- *
 * Narrow screens
 *
 * This audience is on a phone, and a real one: 320–360 CSS px wide, often at
 * 150–200% text zoom. Everything below is a place where a fixed rem column or
 * a nowrap ate more than half of that width.
 * ---------------------------------------------------------------- */

@media (max-width: 30rem) {
  .drawer-panel { width: 100%; }
  .hud-list { padding: .35rem .6rem; font-size: .78rem; }

  /* The HUD is sticky, so every line it wraps to is a line permanently gone
     from a 640px-tall viewport. Two rows, always: identity above, the bar and
     the two drawers below. */
  .hud { gap: .35rem .7rem; padding-block: .45rem; }
  .brand { font-size: .9rem; }
  .hud-stats { flex: 1 1 auto; justify-content: space-between; gap: .7rem; }
  .hud-stats dt { font-size: .58rem; }
  .hud-stats dd { font-size: .78rem; }
  .hud-stats .hud-player { grid-template-columns: 1.9rem minmax(0, auto); column-gap: .35rem; }
  .hud-avatar { width: 1.8rem; height: 1.8rem; }
  .hud-progress { order: 3; flex: 1 1 5rem; }
  .hud-list { order: 4; }
  .scene-menu { inset-inline: .4rem auto; width: calc(100vw - .8rem); }
  .profile-identity { grid-template-columns: 4.4rem minmax(0, 1fr); padding: .85rem; }
  .avatar--profile { width: 4.15rem; height: 4.15rem; }

  /* "روزانه رایگان، نوبت دوم شهریه‌پرداز" is a chip. nowrap made it a
     horizontal scrollbar. */
  .chip { white-space: normal; }

  .why-row { grid-template-columns: minmax(0, 1fr) auto; }
  .why-label { grid-column: 1 / -1; }

  .transit-list span { min-inline-size: min(5.5rem, 40%); }
  .band-menu { min-width: min(15rem, calc(100vw - 2.5rem)); }
  .explorer-list button, .option-list button { flex-wrap: wrap; }
  .city-actions button, .city-actions .band-picker { flex: 1 1 auto; }
  .city-actions .band-toggle { width: 100%; }
  .scene-actions button, .scene-actions .button-like { flex: 1 1 12rem; }
  .fact-grid { grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }
  .compare-grid { grid-template-columns: minmax(0, 1fr); }

  /* The map's own labels are unreadable below roughly 360px whatever we do to
     them, so at this width the province list beside the map is the primary
     control and the map is the picture. See map.js: the labels are dropped
     from the DOM, not merely hidden, so nothing is announced twice. */
  .map-fallback { max-width: none; }
  .map-fallback label { font-weight: 700; color: var(--ink-2); font-size: .82rem; }
}

/* ---------------------------------------------------------------- *
 * Failure and empty states
 *
 * "این شهر رو توی اطلس نداریم" for a dropped connection is the product
 * lying about its own data, which is the one thing it cannot afford. Every
 * one of these says what happened and offers the same way out: try again.
 * ---------------------------------------------------------------- */

.load-error {
  display: grid; gap: .5rem; justify-items: start;
  padding: 1rem 1.1rem; margin: .6rem 0;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: 16px; box-shadow: var(--shadow-1);
}
.load-error p { margin: 0; color: var(--ink-2); font-size: .88rem; line-height: 1.9; }
.load-error .field-note { margin: 0; }
.load-error button { min-block-size: 44px; }

.is-loading-inline {
  display: inline-flex; align-items: center; gap: .4rem;
  color: var(--ink-3); font-size: .8rem;
}
.is-loading-inline::before {
  content: ""; width: .7rem; height: .7rem; border-radius: 50%;
  border: 2px solid var(--line-strong); border-block-start-color: var(--orange);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* The drawer is a modal, so the page behind it must not scroll under the
   finger. Set by showDrawer, cleared by closeList. */
body.is-drawer-open { overflow: hidden; }

/* ---------------------------------------------------------------- *
 * Provenance, everywhere a number appears
 *
 * The rule the design doc calls non-negotiable: no number without a label
 * saying where it came from. These are the three places that were still
 * printing bare figures.
 * ---------------------------------------------------------------- */

.day-lines li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem; }
.explorer-meta .chip { margin-inline-start: .25rem; }
.map-tip .chip { margin-block-start: .15rem; }

/* The native-quota warning, restated on every card rather than only in the
   header — a candidate who scrolls past the header is exactly the reader who
   most needs it. */
.card-quota {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem;
  margin: 0 0 .5rem; padding: .35rem .55rem;
  background: rgba(179, 57, 28, .07);
  border-inline-start: 2px solid rgba(179, 57, 28, .35);
  border-radius: 0 8px 8px 0;
  color: var(--ink-2); font-size: .74rem; line-height: 1.75;
}

/* What the compass's `network` answer generates. Not a ranking input — a
   sentence — so it is drawn as a sentence and not as a score. */
.network-note {
  margin: 0 0 1rem; padding: .8rem 1rem;
  background: rgba(49, 128, 92, .06);
  border-inline-start: 2px solid rgba(49, 128, 92, .38);
  border-radius: 0 12px 12px 0;
  color: var(--ink-2); font-size: .86rem; line-height: 1.95;
}
.network-note p { margin: 0; }
.network-note p + p { margin-block-start: .4rem; }

/* Resident-written, moderator-reviewed sentences. Never averaged: a median
   over sentences would present one person's evening as a measured fact. */
.notes-list { list-style: none; margin: .6rem 0 0; padding: 0; display: grid; gap: .5rem; }
.notes-list li {
  padding: .7rem .85rem; background: var(--surface-2);
  border: 1px solid var(--line); border-inline-start: 2px solid var(--green);
  border-radius: 0 12px 12px 0;
}
.notes-list .note-prompt { margin: 0 0 .2rem; color: var(--ink-3); font-size: .74rem; font-weight: 700; }
.notes-list .note-text { margin: 0; color: var(--ink-2); font-size: .88rem; line-height: 1.95; }

/* The distribution behind a "most of them said X". The share is the point:
   sixty per cent and ninety-five per cent are different answers. */
.dist-list { list-style: none; margin: .35rem 0 0; padding: 0; display: grid; gap: .2rem; }
.dist-list li {
  display: grid; grid-template-columns: minmax(0, 1fr) 3.5rem auto;
  align-items: center; gap: .4rem; font-size: .74rem; color: var(--ink-3);
}
.dist-bar { height: 5px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.dist-bar i { display: block; height: 100%; width: var(--w, 0%); background: rgba(238, 112, 32, .55); }


/* ---- institutions grouped by the door you walk through ----------------- */
.chip[data-tone="warn"] { background: rgba(179, 57, 28, .1); color: var(--danger); }

.uni-group {
  border: 1px solid var(--line); border-radius: .7rem;
  margin-block-start: .6rem; overflow: hidden;
}
.uni-group > summary {
  display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
  padding: .6rem .7rem; cursor: pointer; list-style: none;
  background: var(--surface-2);
}
.uni-group > summary::-webkit-details-marker { display: none; }
.uni-group > summary::after {
  content: "▾"; margin-inline-start: auto; color: var(--ink-3); font-size: .8rem;
  transition: transform .15s ease;
}
.uni-group[open] > summary::after { transform: rotate(180deg); }
.uni-group > summary b { font-variant-numeric: tabular-nums; color: var(--ink-3); font-size: .8rem; }
.uni-group .uni-list { margin: 0; padding: .3rem .7rem .7rem; }
.uni-group .uni-list li { flex-wrap: wrap; }

.net-card {
  margin: 0; padding: .7rem;
  border-block-end: 1px solid var(--line);
  display: grid; gap: .45rem;
}
.net-card h5 { margin: 0; font-size: .82rem; color: var(--ink-3); font-weight: 600; }
.net-card p { margin: 0; font-size: .84rem; line-height: 1.9; color: var(--ink-2); max-width: none; }
.net-card .net-warn { color: var(--danger); }
.net-card .net-reality {
  border-inline-start: 2px solid var(--orange-soft, var(--line));
  padding-inline-start: .6rem;
}
.uni-link.is-candidate { opacity: .75; text-decoration-style: dashed; }
.uni-note { flex-basis: 100%; font-size: .76rem; color: var(--ink-3); line-height: 1.8; }

/* A button that reads as part of a sentence, for an action offered inline. */
.link-button {
  padding: 0; background: none; border: none;
  font: inherit; color: var(--orange-soft); text-decoration: underline;
  text-underline-offset: .2em; cursor: pointer;
}
.link-button:hover { filter: none; transform: none; color: var(--orange-ink); }

.crew-pending li { flex-wrap: wrap; }
.pending-actions { display: flex; gap: .4rem; margin-inline-start: auto; }
.pending-actions button { padding: .3rem .7rem; font-size: .78rem; border-radius: 99px; }

/* ---------------------------------------------------------------- *
 * Light theme, final pass
 *
 * On a dark ground a 1px border was enough to separate a card from the page.
 * On near-white it is not: the containers a reader treats as objects get the
 * site's own soft warm shadow instead, and the flat inner boxes keep the plain
 * border so the hierarchy stays two levels deep, not five.
 * ---------------------------------------------------------------- */

/* .signal-q and .sure-item are deliberately NOT here: they are transparent
   <fieldset>s, and a fieldset's paint box starts at the midline of its
   <legend>'s first line — so a shadow on them rendered as a ghost box slicing
   straight through every two-line question. */
.panel-card, .choice-card, .city-card, .crew-panel, .filter-panel,
.drawer-panel, .comparison-block, .room-block, .uni-group,
.post-form, .explorer-list li, .locked-teaser {
  box-shadow: var(--shadow-1);
}

.choice-card:hover, .city-card:hover, .panel-option:hover {
  box-shadow: var(--shadow-2);
  border-color: rgba(237, 109, 28, .34);
}

/* Selected states carry the site's orange ring rather than a fill swap, so the
   text inside stays on white and keeps its contrast. */
.choice-card[aria-pressed="true"], .panel-option[aria-pressed="true"],
.scale-option:has(input:checked), .sure-toggle:has(input:checked) {
  border-color: var(--orange);
  box-shadow: 0 0 0 3px rgba(237, 109, 28, .12), var(--shadow-1);
}

/* Dialogs sit above a warm scrim, not a black one — and arrive, rather than
   teleport: a short rise from where the tap happened to live. */
dialog::backdrop { background: rgba(70, 48, 34, .38); backdrop-filter: blur(2px); animation: backdrop-in .22s ease both; }
.city-dialog, .explorer-dialog {
  background: var(--ground-2);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-2);
}
.city-dialog[open] { animation: dialog-in .24s cubic-bezier(.22, 1, .36, 1) backwards; }
@keyframes dialog-in { from { opacity: 0; transform: translateY(12px) scale(.985); } }
@keyframes backdrop-in { from { opacity: 0; } }

/* Inputs read as wells on white: a hair of inset, never a glow. */
input, select, textarea {
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  color: var(--ink);
}
input:focus, select:focus, textarea:focus {
  border-color: var(--orange);
  box-shadow: 0 0 0 3px rgba(237, 109, 28, .14);
  outline: none;
}
::placeholder { color: var(--ink-3); opacity: .8; }

/* Links inherit the brand orange rather than the browser blue. */
a { color: var(--orange-soft); text-underline-offset: .2em; }
a:hover { color: var(--orange-ink); }

/* ---------------------------------------------------------------- *
 * The map as the opponent
 *
 * A shared, visible target — how much of the atlas has real narrators —
 * and never a ranking between players. The bar is the only progress in
 * this product that belongs to everybody at once.
 * ---------------------------------------------------------------- */
.hud-map b { font-variant-numeric: tabular-nums; }

.map-lede { margin: 0 0 1rem; line-height: 1.9; }

.coverage { margin-bottom: 1.4rem; }
.coverage-bar {
  display: flex; height: .7rem; overflow: hidden;
  background: rgba(33, 28, 25, .07); border-radius: 99px;
}
.coverage-bar i { display: block; width: var(--w); height: 100%; }
.coverage-bar .is-lit { background: linear-gradient(90deg, var(--orange-deep), var(--orange)); }
.coverage-bar .is-kindling { background: rgba(237, 109, 28, .28); }

.coverage-legend {
  display: flex; flex-wrap: wrap; gap: .2rem 1rem;
  margin: .6rem 0 0; padding: 0; list-style: none; font-size: .8rem;
}
.coverage-legend li { display: flex; align-items: center; gap: .35rem; }
.coverage-legend .dot { width: .6rem; height: .6rem; border-radius: 99px; }
.coverage-legend .dot.is-lit { background: var(--orange-soft); }
.coverage-legend .dot.is-kindling { background: rgba(237, 109, 28, .32); }
/* Another dark-theme leftover: white at 14% on a white card, i.e. the "خاموش"
   dot — five hundred of the five hundred and eighty-two cities — was invisible.
   This is 3.8:1 against the card, which is the bar for a graphical object. */
.coverage-legend .dot.is-dark { background: rgba(33, 28, 25, .55); }

.map-block { margin-bottom: 1.6rem; }
.map-block h3 { margin: 0 0 .5rem; font-size: .96rem; font-weight: 800; }
.lit-note { font-size: .78rem; color: var(--orange-soft); font-weight: 700; }

.target-list { list-style: none; margin: .6rem 0 0; padding: 0; display: grid; gap: .5rem; }
.target-list li {
  display: flex; align-items: center; gap: .8rem; justify-content: space-between;
  padding: .7rem .9rem; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 14px;
}
.target-list strong { font-size: .95rem; }
.target-why { display: block; margin-top: .2rem; font-size: .76rem; line-height: 1.7; }
.target-list button { flex: none; padding: .35rem .8rem; font-size: .78rem; border-radius: 99px; }

.target-call {
  margin: 0 0 1.2rem; padding: 1rem 1.1rem;
  background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: 18px;
}
.target-call h3 { margin: .1rem 0 .5rem; font-size: 1.05rem; font-weight: 800; }
.target-call p { margin: 0 0 .5rem; line-height: 1.9; }

/* Of the three actions on a city card only one is the decision — keeping the
   city. It gets the brand's outline so the eye lands on it; the other two stay
   neutral so the card still reads as a card, not a toolbar. */
.city-actions .band-toggle {
  background: #fff; color: var(--orange-soft); border-color: rgba(237, 109, 28, .45);
}
.city-actions .band-toggle:hover { border-color: var(--orange); color: var(--orange-soft); }
.city-actions .band-toggle[aria-pressed="true"] {
  background: rgba(237, 109, 28, .12); border-color: var(--orange); color: var(--orange-soft);
}
.city-actions button[aria-pressed="true"] { background: rgba(237, 109, 28, .12); border-color: var(--orange); color: var(--orange-soft); }

/* Offline. A persistent line, not a toast: the state lasts and it is not the
   player's mistake, so it stays on screen until the connection returns. */
.offline-banner {
  position: sticky; top: 0; z-index: 70; margin: 0;
  padding: calc(.6rem + env(safe-area-inset-top)) calc(1rem + env(safe-area-inset-right))
           .6rem calc(1rem + env(safe-area-inset-left));
  background: var(--orange); color: #211c19;
  font-size: .82rem; font-weight: 700; text-align: center;
}

/* The update offer wears the offline banner's shape and orange — but not its
   stickiness: both banners are body's first child, and a second sticky strip
   at top:0 sat permanently OVER the sticky HUD (z 70 vs 20), hiding the
   progress bar and both drawers until dismissed. An update offer is a
   suggestion, so it scrolls away; the offline strip stays sticky because
   connection state has to stay visible. */
.update-banner { position: static; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .4rem .6rem; }
.update-banner button {
  min-block-size: 44px; padding: .35rem 1rem; border-radius: 99px;
  background: #211c19; color: #fffefd; font-size: .78rem; font-weight: 800;
}
.update-banner button.ghost { background: none; border: 1px solid rgba(33, 28, 25, .35); color: #211c19; }
/* On a 320px phone the two pills otherwise land on separate rows and the strip
   eats 40% of the viewport. */
@media (max-width: 30rem) {
  .update-banner { padding-inline: .7rem; }
  .update-banner button { padding: .3rem .8rem; }
}

/* WCAG 2.5.8 puts the floor for a pointer target at 24×24 CSS px. Two inline
   controls sat just under it — a link inside a dense list and a button that
   reads as part of a sentence. Padding, not font size, closes the gap so the
   typography does not change. */
.uni-link, .link-button {
  display: inline-flex; align-items: center;
  min-block-size: 24px; padding-block: .2rem;
}

/* Lights, crew quests, and the strip that says what changed while you were away. */
.thanks-button {
  margin-top: .5rem; min-block-size: 44px; padding: .5rem .9rem; font-size: .76rem; border-radius: 99px;
  background: transparent; border: 1px solid var(--line); color: inherit; cursor: pointer;
}
.thanks-button:hover { border-color: var(--orange-soft); color: var(--orange-soft); filter: none; transform: none; }
.thanks-button[disabled] { border-color: var(--orange); color: var(--orange-soft); opacity: .8; cursor: default; }

.lights ul { list-style: none; margin: .4rem 0 .6rem; padding: 0; display: grid; gap: .5rem; }
.lights li {
  padding: .6rem .8rem; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 12px; line-height: 1.9; font-size: .86rem;
}
.lights .light-entry { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: .65rem; }
.light-note { display: block; margin-top: .2rem; color: var(--orange-soft); }

.crew-quest p { margin: 0 0 .5rem; line-height: 1.9; }

.news {
  max-width: 46rem; margin: 0 auto 1rem; padding: .9rem 1.1rem;
  background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: 16px;
}
.news p { margin: 0 0 .4rem; font-size: .88rem; }
.news ul { list-style: none; margin: 0 0 .6rem; padding: 0; display: grid; gap: .3rem; font-size: .84rem; }
.news li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem; }
.news [data-open-city] {
  padding: .15rem .6rem; background: transparent; border: 1px solid var(--line-strong);
  border-radius: 99px; font: inherit; font-size: .8rem; color: var(--orange-soft); cursor: pointer;
}
.news #news-close { padding: .3rem .9rem; font-size: .78rem; border-radius: 99px; }

/*
 * The notification ask.
 *
 * Sits where «از وقتی نبودی» sits and is built to be dismissible at a glance:
 * the two buttons carry equal visual weight on purpose. A decline styled as a
 * whisper next to a bright yes is how a permission gets pressed by accident and
 * then revoked in the OS, where it can never be asked for again.
 */
.notify-ask {
  max-width: 46rem; margin: 0 auto 1rem; padding: .95rem 1.1rem;
  background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: 16px;
}
.notify-ask-title { margin: 0 0 .35rem; font-size: .92rem; }
.notify-ask-body { margin: 0 0 .5rem; font-size: .84rem; line-height: 1.9; color: var(--ink-2); }
.notify-ask-note {
  margin: 0 0 .6rem; padding-inline-start: .7rem;
  border-inline-start: 2px solid var(--line-strong);
  font-size: .78rem; line-height: 1.8; color: var(--ink-3);
}
.notify-ask-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; }
.notify-ask-actions button { min-block-size: 44px; padding: .4rem 1.1rem; font-size: .82rem; border-radius: 99px; }
.notify-ask .status { margin: .5rem 0 0; font-size: .78rem; line-height: 1.8; }

/* Report and self-remove. Quiet by design: present when someone needs them,
   never loud enough to invite a fight. */
.post-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }
.post-actions .link-button {
  min-block-size: 44px; padding-inline: .3rem;
  color: var(--ink-3); font-weight: 600; font-size: .74rem;
}
.post-actions .link-button:hover { color: var(--danger); }
.post-actions .link-button:disabled { color: var(--ink-3); opacity: .7; }
.report-reasons {
  flex-basis: 100%; margin-top: .4rem; padding: .6rem .7rem;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 12px;
}
.report-reasons .field-note { margin: 0 0 .4rem; }


/* The journey map — the parchment board, its trail, medallions, counters and
 * motion — lives in journey.css, because the panel draws the same board and
 * must not download the whole game stylesheet to do it. */

/* ───────────────────────────────────────────────────────────────────────────
 * City gallery
 *
 * Two rules shape everything below.
 *
 * The grid is `auto-fill` over a 150px minimum rather than a fixed column
 * count, because this page is read on a 320px phone and on a laptop and the
 * same declaration has to serve both. Each tile reserves its own height from
 * the dimensions the upload reported (`--ratio`), so the grid does not jump as
 * images decode — the single most irritating thing a photo grid does on a slow
 * connection.
 *
 * The completeness bar is drawn as eight equal segments and never as a
 * percentage-filled line. Eight segments say *which* part is missing; a line
 * says only how far along you are, and «کدومش مونده» is the whole question.
 * ─────────────────────────────────────────────────────────────────────────── */

.tab-count {
  display: inline-block;
  min-inline-size: 1.35em;
  padding: 0 .3em;
  margin-inline-start: .25em;
  border-radius: 999px;
  background: rgba(237, 109, 28, .16);
  color: var(--orange-ink);
  font-size: .78em;
  font-weight: 700;
}

.gal-progress {
  margin-block: .9rem 1.2rem;
  padding: .9rem 1rem;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface-2);
}
.gal-progress-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .5rem;
  margin-block-end: .55rem;
}
.gal-progress-head span { color: var(--ink-3); font-size: .9rem; }

.gal-bar { display: flex; gap: 3px; }
.gal-bar i {
  flex: 1;
  block-size: 8px;
  border-radius: 3px;
  background: rgba(33, 28, 25, .1);
  transition: background .25s ease;
}
.gal-bar i.is-filled { background: var(--orange); }

.gal-slots {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  margin-block: .7rem 0;
  padding: 0;
  list-style: none;
}
.gal-slots button {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  min-block-size: 30px;
  padding: .25rem .6rem;
  border: 1px dashed var(--line-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-3);
  font: inherit;
  font-size: .85rem;
  cursor: pointer;
}
.gal-slots li.is-filled button {
  border-style: solid;
  border-color: rgba(49, 128, 92, .4);
  background: rgba(49, 128, 92, .08);
  color: var(--green-ink);
}
.gal-slots button span { font-weight: 700; }

.gal-ask {
  margin-block: .8rem 0;
  padding-block-start: .7rem;
  border-block-start: 1px solid var(--line);
  line-height: 1.9;
  font-size: .95rem;
}
.gal-ask strong { color: var(--orange-ink); }
.gal-ask.is-done { color: var(--green-ink); }

.gal-upload { margin-block-end: 1rem; }
.gal-upload > summary {
  padding: .6rem .8rem;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--surface);
  font-weight: 700;
  cursor: pointer;
}
.gal-upload-body { padding: .8rem .2rem 0; }
.gal-upload input[type="file"] {
  display: block;
  inline-size: 100%;
  min-block-size: 44px;
  margin-block: .3rem .6rem;
  font: inherit;
}
.gal-preview { margin-block: .6rem; }
.gal-preview img,
.gal-preview video {
  inline-size: 100%;
  max-block-size: 320px;
  object-fit: contain;
  border-radius: 12px;
  background: rgba(33, 28, 25, .05);
}

.gal-sort { margin-block: .8rem .6rem; }

.gal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: .8rem;
  padding: 0;
  margin: 0;
  list-style: none;
}
.gal-tile {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  overflow: hidden;
}
.gal-open {
  display: block;
  inline-size: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.gal-frame {
  position: relative;
  display: block;
  inline-size: 100%;
  padding-block-end: var(--ratio, 75%);
  background: rgba(33, 28, 25, .06);
  overflow: hidden;
}
.gal-frame img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
.gal-play {
  position: absolute;
  inset-block-end: .5rem;
  inset-inline-start: .5rem;
  inline-size: 30px;
  block-size: 30px;
  border-radius: 50%;
  background: rgba(0, 0, 0, .55);
}
.gal-play::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  transform: translate(50%, -50%);
  border-block: 6px solid transparent;
  /* RTL: the triangle points along the writing direction of the control, not
     of the text — a play glyph mirrored by `border-inline` reads as rewind. */
  border-right: 9px solid #fff;
}
.gal-noposter { position: absolute; inset: 0; background: rgba(33, 28, 25, .12); }

.gal-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem;
  padding: .5rem .6rem 0;
  font-size: .82rem;
}
.gal-slot { color: var(--ink-3); font-weight: 700; }
.gal-caption {
  margin: .35rem 0 0;
  padding-inline: .6rem;
  font-size: .9rem;
  line-height: 1.8;
}
.gal-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  padding: .5rem .6rem .6rem;
}
.gal-author {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  min-width: 0;
  margin-inline-start: auto;
  color: var(--ink-3);
  font-size: .8rem;
}
.gal-author > span:last-child { max-width: 8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.gal-like,
.gal-comment-open {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  min-block-size: 30px;
  padding: .2rem .5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-3);
  font: inherit;
  font-size: .85rem;
  cursor: pointer;
}
.gal-like.is-on { border-color: rgba(215, 87, 14, .45); background: rgba(237, 109, 28, .1); color: var(--orange-ink); }
.gal-like b, .gal-comment-open b { font-variant-numeric: tabular-nums; }

.gal-icon-heart,
.gal-icon-chat {
  inline-size: 14px;
  block-size: 14px;
  background: currentColor;
  /* Inlined as masks rather than as <svg> or an icon font: two shapes are not
     worth another request, and a mask inherits `currentColor` so the filled
     state needs no second asset. */
  -webkit-mask: center / contain no-repeat;
  mask: center / contain no-repeat;
}
.gal-icon-heart {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21s-8-4.9-8-10.4A4.6 4.6 0 0 1 12 7a4.6 4.6 0 0 1 8 3.6C20 16.1 12 21 12 21z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21s-8-4.9-8-10.4A4.6 4.6 0 0 1 12 7a4.6 4.6 0 0 1 8 3.6C20 16.1 12 21 12 21z'/%3E%3C/svg%3E");
}
.gal-icon-chat {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 4h16v12H8l-4 4z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 4h16v12H8l-4 4z'/%3E%3C/svg%3E");
}
.gal-like.is-pop .gal-icon-heart { animation: gal-pop .32s ease; }
@keyframes gal-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.45); }
  100% { transform: scale(1); }
}

.gal-credits { margin-block-start: 1.4rem; }
.gal-credits h5 { margin-block-end: .5rem; }
.gal-credits ul { margin: 0; padding: 0; list-style: none; }
.gal-credits li {
  display: flex;
  align-items: center;
  gap: .45rem;
  padding-block: .35rem;
  border-block-end: 1px solid var(--line);
  font-size: .9rem;
}
.gal-credits .muted { margin-inline-start: auto; color: var(--ink-3); font-size: .85rem; }

/* The overlay is mounted on <body> (or on the city dialog), not inside the
   gallery — see the comment in gallery.js. `is-modal-open` is the same scroll
   lock the list drawer uses; without it the page scrolled behind the photo on
   a phone, which reads as the overlay being broken. */
body.is-modal-open { overflow: hidden; }

.gal-lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(20, 15, 12, .78);
  overflow-y: auto;
}
.gal-modal {
  position: relative;
  inline-size: min(720px, 100%);
  max-block-size: 92vh;
  overflow-y: auto;
  border-radius: 16px;
  background: var(--surface);
}
.gal-close {
  position: absolute;
  inset-block-start: .4rem;
  inset-inline-end: .4rem;
  inline-size: 34px;
  block-size: 34px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, .5);
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}
.gal-modal-media { background: #000; }
.gal-modal-media img,
.gal-modal-media video {
  display: block;
  inline-size: 100%;
  max-block-size: 62vh;
  object-fit: contain;
}
.gal-modal-body { padding: .8rem 1rem 1.1rem; }
.gal-comments { margin: .5rem 0; padding: 0; list-style: none; }
.gal-comments > li {
  padding-block: .55rem;
  border-block-end: 1px solid var(--line);
}
.gal-comment-head { display: flex; align-items: center; gap: .4rem; font-size: .88rem; }
.gal-comments p { margin: .3rem 0 .4rem; line-height: 1.9; }
.gal-comment-actions { display: flex; align-items: center; gap: .6rem; }
.gal-comment-form { display: flex; align-items: flex-start; gap: .5rem; flex-wrap: wrap; }
.gal-comment-form .field { flex: 1 1 12rem; }
.gal-comment-form .status { flex-basis: 100%; }
.gal-reasons {
  margin-block-start: .8rem;
  padding: .7rem;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--surface-2);
}

@media (prefers-reduced-motion: reduce) {
  .avatar--profile img { animation: none; }
  .gal-like.is-pop .gal-icon-heart { animation: none; }
  .gal-bar i { transition: none; }
}

/* The way home. Same shape as the other bar controls; the map is a place, not
   a mode, so it gets no pressed state — it simply disappears while you are on
   it (renderHud). */
.hud-home { color: var(--ink); }
.hud-home svg { color: var(--orange-strong, #c14f0c); }
