/* ═══════════════════════════════════════════════════════════════════════════
   HALO MIST · ORBITAL

   The app is an instrument for reading radius. Every screen is built around
   one centre: a focal ring with the figure inside it and small bodies in orbit
   around it. The game is a 22° ice halo seen through night fog, so the whole
   product is drawn the way that phenomenon is: concentric, cold, and lit by a
   single warm ring.

   Rules this sheet keeps, without exception:
     · NOTHING IS A RECTANGLE. Every box is a circle or a capsule. Radius is
       50% or 999px. If a shape has a corner it is a bug, not a variant.
     · There is NO elevation. No shadow, no glow, no gradient fill. Depth is
       made only by concentric strokes of different weight and opacity.
     · Structure is radial. Content is centred and things sit AROUND the
       centre, never stacked into a column of cards.
     · Dividers are rows of dots, never a solid rule.
     · Figures are large, light-weight, tabular, and sit inside a ring.
     · Motion is 220ms. Rings sweep, figures rise, nothing slides sideways and
       nothing bounces.

   Type is ONE geometric sans, Jost, because circular counters are the same
   drawing as the halo. Instrument Serif is linked for one reason only: game.js
   sets the canvas multiplier and the score floats in it, and that file is
   fixed. No chrome in this sheet uses a serif.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Written from JS by applySafeArea(). 0 outside Telegram fullscreen, so
     every utility that reads them collapses on its own. */
  --tg-safe-top: 0px;
  --tg-safe-bottom: 0px;

  /* Duplicated from brand.js so the splash paints correctly on frame one,
     before shell.js has stamped the palette onto <html>. game.js reads these
     same names off the root, which is what keeps the field and the chrome in
     one night. */
  --bg:       #05080F;
  --surface:  #0B111E;
  --tint:     #101827;
  --tint-2:   #18223A;
  --line:     #26324B;
  --ink:      #EDF1FA;
  --ink-2:    #A5B0C8;
  --ink-3:    #7C8AA6;
  --accent:   #F3C97D;
  --accent-d: #F8DCA8;
  --good:     #86C9F0;
  --warn:     #F2836F;

  --f: "Jost", "Futura", "Avenir Next", -apple-system, BlinkMacSystemFont, sans-serif;

  /* the only two radii in the app */
  --pill: 999px;
  --mg: 22px;

  --t: 220ms cubic-bezier(.3, .7, .2, 1);

  /* a rule made of dots, used everywhere a line would have been */
  --dots: radial-gradient(circle at center, var(--line) 0 1px, rgba(0, 0, 0, 0) 1.3px);
}

*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Overlays set their own display, which outranks the UA rule for [hidden]. */
[hidden] { display: none !important; }

html, body {
  margin: 0; padding: 0; height: 100%;
  background: var(--bg);
  overscroll-behavior: none;
  -webkit-overflow-scrolling: touch;
}

body {
  font-family: var(--f);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.55;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

h1, h2, p, ol, ul, dl, figure { margin: 0; padding: 0; }
ol, ul { list-style: none; }
button, input { font: inherit; color: inherit; }
button { border: 0; background: none; padding: 0; cursor: pointer; }

/* ── glyphs ──────────────────────────────────────────────────────────────
   Eight marks, and every one of them is made of arcs, rings, dots or
   capsules. There is not a single straight edge or corner in the set. */

.glyphs { position: absolute; width: 0; height: 0; overflow: hidden; }

.gl {
  display: block; flex: none;
  width: 22px; height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
}
.gl .sol { fill: currentColor; stroke: none; }
.gl-sm { width: 15px; height: 15px; stroke-width: 1.6; }
.gl-lg { width: 34px; height: 34px; stroke-width: 1; }
.gl-xl { width: 76px; height: 76px; stroke-width: .7; }

/* ── type roles ──────────────────────────────────────────────────────────── */

/* the tracked micro-label. Every caption, unit and section marker. */
.tag {
  display: block;
  font-size: 9.5px; font-weight: 500;
  letter-spacing: .24em; text-transform: uppercase;
  line-height: 1.4;
  color: var(--ink-3);
}
.tag--lit { color: var(--accent); }

.head {
  font-size: 23px; font-weight: 400;
  line-height: 1.24;
  letter-spacing: -.012em;
  text-align: center;
  text-wrap: balance;
}

/* the one prose voice: centred, short measure, never full bleed */
.say {
  margin: 12px auto 0;
  max-width: 31ch;
  text-align: center;
  font-size: 14.5px; line-height: 1.6;
  color: var(--ink-2);
}
.say--sm { font-size: 13px; max-width: 34ch; }

.note {
  margin: 30px auto 0;
  max-width: 34ch;
  text-align: center;
  font-size: 11.5px; line-height: 1.55;
  color: var(--ink-3);
}

/* every large figure in the app: light, tight, tabular */
.fig-n {
  display: block;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* a dot used as punctuation between two small values */
.dot {
  display: inline-block; flex: none;
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--ink-3);
  vertical-align: middle;
}

/* the dotted rule: the only divider in the app */
.dotrule {
  height: 7px;
  background: var(--dots) 0 center / 7px 7px repeat-x;
  opacity: .85;
}

/* ── controls ────────────────────────────────────────────────────────────
   Circular when the action needs no word, capsule when it needs one. There
   is no third button shape. */

.cap-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 26px;
  border-radius: var(--pill);
  background: var(--accent);
  color: var(--bg);
  font-size: 15px; font-weight: 500;
  text-align: center;
  transition: transform var(--t), background var(--t), border-color var(--t), opacity var(--t);
}
.cap-btn:active { transform: scale(.965); }
.cap-btn--ghost {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink);
}
.cap-btn--ghost:active { border-color: var(--accent); }
.cap-btn--quiet { background: transparent; color: var(--ink-2); font-weight: 400; }
.cap-btn--wide { width: 100%; max-width: 300px; }
.cap-btn--sm { min-height: 40px; font-size: 13.5px; padding: 0 20px; }
.cap-btn:disabled { opacity: .45; }

.round-btn {
  width: 42px; height: 42px; flex: none;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--ink-2);
  display: grid; place-items: center;
  transition: transform var(--t), border-color var(--t), color var(--t);
}
.round-btn:active { transform: scale(.94); border-color: var(--accent); color: var(--accent); }

/* ── splash: rings leaving a core, which is the whole game ───────────────── */

#splash {
  position: fixed; inset: 0; z-index: 90;
  background: var(--bg);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  color: var(--accent);
  transition: opacity var(--t);
}
#splash.out { opacity: 0; }

.sp-orb { position: relative; display: grid; place-items: center; }
.sp-orb::before, .sp-orb::after {
  content: ""; position: absolute;
  left: 50%; top: 50%;
  width: 76px; height: 76px;
  margin: -38px 0 0 -38px;
  border-radius: 50%;
  border: 1px solid var(--accent);
  animation: bloom 2600ms ease-out infinite;
}
.sp-orb::after { animation-delay: 1300ms; }
@keyframes bloom {
  from { transform: scale(1); opacity: .5; }
  to   { transform: scale(2.6); opacity: 0; }
}

.sp-name {
  margin-top: 30px;
  font-size: 30px; font-weight: 300;
  letter-spacing: .34em; text-transform: uppercase;
  text-indent: .34em;
  color: var(--ink);
}
.sp-line {
  margin-top: 10px;
  font-size: 12px; font-weight: 400;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-3);
}

/* ── frame ───────────────────────────────────────────────────────────────
   There is no top bar. It was removed: a persistent chrome strip pins content
   to a corner of the screen, and this language has no corners to pin to. Each
   screen carries the fullscreen inset itself and opens on its own centre. */

#app {
  position: fixed; inset: 0;
  display: flex; flex-direction: column;
  background: var(--bg);
}
main { position: relative; flex: 1; min-height: 0; z-index: 0; }

.sky {
  position: absolute; inset: 0;
  display: none;
  overflow-y: auto;
  padding: calc(max(var(--tg-safe-top), env(safe-area-inset-top, 0px)) + 20px)
           var(--mg) 34px;
}
.sky.is-active { display: block; animation: rise var(--t) ease-out; }

/* Fullscreen already reserves the chrome inset; trim a cosmetic sliver so a
   screen does not open on a gap, floored at the inset itself. */
[data-fullscreen="1"] .sky {
  padding-top: max(var(--tg-safe-top),
    calc(max(var(--tg-safe-top), env(safe-area-inset-top, 0px)) - 1.4vh + 20px));
}

@keyframes rise  { from { opacity: 0; transform: translateY(6px); } }
@keyframes sweep { from { opacity: .15; transform: rotate(-16deg); } }
@keyframes swell { from { opacity: 0; transform: scale(.9); } }

/* ── home ────────────────────────────────────────────────────────────────
   One focal ring holding the record, with everything else in orbit around
   it. The dial is drawn to the game's real proportions, so the screen is a
   picture of the mechanic rather than a menu that mentions it. */

.crest {
  display: flex; align-items: center; justify-content: center;
  gap: 9px;
  color: var(--accent);
}
.crest b {
  font-size: 11px; font-weight: 500;
  letter-spacing: .3em; text-transform: uppercase;
  text-indent: .3em;
  color: var(--ink-2);
}

/* ── the dial ────────────────────────────────────────────────────────────
   THE MODULE THIS GAME EARNS. Not a stat tile: a scale drawing of the catch
   window. The halo sits at 65% of the field radius, the true band opens 7.5%
   of that radius either side of it and the close band 18.5%, and those are
   the exact fractions game.js judges a tap against. A stacking game or a
   reaction game has nothing to put here, because their tolerance is not a
   radius and cannot be drawn concentrically. */

.dial {
  position: relative;
  width: min(100%, 288px);
  margin: 22px auto 0;
  aspect-ratio: 1;
}
.dial-svg {
  display: block; width: 100%; height: auto;
  overflow: visible;
}
.sky.is-active .dial-svg { animation: sweep var(--t) ease-out; }

.dial-svg circle { fill: none; stroke: currentColor; }
.d-field { color: var(--line); stroke-width: .8; opacity: .8; }
.d-lost  { color: var(--line); stroke-width: 1.2; stroke-dasharray: .6 5; stroke-linecap: round; }
.d-zone  { color: var(--accent); opacity: .055; }
.d-core  { color: var(--accent); opacity: .14; }
.d-edge  { color: var(--accent); stroke-width: 1; opacity: .42; stroke-dasharray: 2 4.4; stroke-linecap: round; }
.d-true  { color: var(--accent); stroke-width: 1; opacity: .72; }
.d-halo  { color: var(--accent); stroke-width: 2.4; }
.d-wave  { color: var(--ink); stroke-width: 1; }

/* the figure, centred in the ring, which is where every number in this app
   belongs */
.dial-c {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 9px;
  pointer-events: none;
}
.dial-n {
  font-size: clamp(34px, 11.5vw, 44px);
  color: var(--ink);
}
.sky.is-active .dial-n { animation: swell var(--t) ease-out; }

/* the rank, pinned on the outer orbit so the ring appears to pass behind it */
.dial-rank {
  position: absolute; left: 50%; bottom: 4.4%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 7px;
  padding: 6px 15px;
  border-radius: var(--pill);
  border: 1px solid var(--line);
  background: var(--bg);
  white-space: nowrap;
}
.dial-rank span {
  font-size: 10.5px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent-d);
}

.act-c { display: flex; justify-content: center; margin-top: 22px; }

/* ── the orbit of satellites ─────────────────────────────────────────────
   Four small bodies on a shallow arc under the focal ring. They sit at
   different heights because they are on a path, not in a row. */

.orbit {
  position: relative;
  display: flex; align-items: flex-start;
  margin-top: 28px;
  padding-top: 10px;
}
.orbit::before {
  content: ""; position: absolute;
  left: -6%; right: -6%; top: 20px; height: 150px;
  border-top: 1px dotted var(--line);
  border-radius: 50%;
  opacity: .7;
}
.sat {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.sat:nth-child(1), .sat:nth-child(4) { transform: translateY(13px); }
.sat b {
  width: 54px; height: 54px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--bg);
  font-size: 18px; font-weight: 300;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.sat span {
  font-size: 8.5px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-3);
  text-align: center;
}

/* ── today's mark ────────────────────────────────────────────────────────
   A capsule lane. The only non-circular figure on the screen, and it is a
   capsule, which is the one other shape this language allows. */

.lane { margin-top: 34px; text-align: center; }
.lane-k {
  font-size: 14px;
  color: var(--ink);
}
.lane-track {
  width: min(100%, 260px);
  height: 7px;
  margin: 13px auto 0;
  border-radius: var(--pill);
  background: var(--tint);
  overflow: hidden;
}
.lane-track i {
  display: block; height: 100%; width: 0;
  border-radius: var(--pill);
  background: var(--accent);
  transition: width var(--t), background var(--t);
}
.lane-track i.is-low { background: var(--warn); }
.lane-m {
  display: flex; align-items: center; justify-content: center;
  gap: 9px;
  margin-top: 11px;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
[data-daily-state].is-done { color: var(--good); }

/* the addendum: hidden in this build, because no ad block id is configured */
.addend { margin-top: 40px; text-align: center; }
.addend .cap-btn { margin-top: 16px; }

/* shell.js writes the cap here and leaves it EMPTY when there is none, so the
   line has to disappear on its own rather than leave a hole. */
.cap {
  display: block;
  margin-top: 8px;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-3);
}
.cap:empty { display: none; }

/* ── the field ───────────────────────────────────────────────────────────
   The round is played inside an aperture, not a panel. A true disc, so the
   fog is cropped by a circle and the whole surface agrees with the rings
   crossing it.

   The linear meter that used to sit above the stage is GONE. Every judgement
   this game makes is a radius, a horizontal bar could not be more wrong for
   it, and game.js already draws the two remaining rings as pips on the
   canvas. What was a labelled module is now an unlabelled 44px capsule
   inside the score ring. */

[data-screen="play"] .ring-nav { display: none; }

.sky--field { overflow: hidden; padding-bottom: 16px; }
.sky--field.is-active { display: flex; flex-direction: column; }

.readout {
  position: relative; flex: none;
  display: flex; align-items: center; justify-content: space-between;
}
.read {
  width: 108px; height: 108px;
  border-radius: 50%;
  border: 1px solid var(--line);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 5px;
}
.read b { font-size: 30px; color: var(--ink); }
.read .tag { font-size: 8.5px; }

/* the rings still standing, as a capsule inside the figure ring */
.pips {
  width: 44px; height: 3px;
  margin-top: 2px;
  border-radius: var(--pill);
  background: var(--tint-2);
  overflow: hidden;
}
.pips i {
  display: block; height: 100%; width: 100%;
  border-radius: var(--pill);
  background: var(--accent);
  transition: width var(--t), background var(--t);
}
.pips i.is-low { background: var(--warn); }

/* two satellites on the rim of the figure ring: the chain as a capsule
   because it carries a value, the shield as a bare disc because it carries
   only a state (its word stays in the markup for assistive tech) */
.chain, .guard {
  position: absolute; top: 50%;
  transform: translateY(-50%);
  border-radius: var(--pill);
  white-space: nowrap;
}
.chain {
  left: 50%; margin-left: 60px;
  padding: 4px 11px;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-size: 11px; font-weight: 500;
  letter-spacing: .1em;
  opacity: 0;
  transition: opacity var(--t);
}
.chain.is-on { opacity: 1; }
.guard {
  right: 50%; margin-right: 62px;
  width: 26px; height: 26px;
  border: 1px solid var(--accent);
  overflow: hidden; text-indent: -999px;
}
.guard::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 8px; height: 8px; margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--accent);
}

.arena {
  flex: 1; min-height: 0;
  display: grid; place-items: center;
  padding: 16px 0 10px;
}

/* The aperture. Sized from the shorter of the two axes so it stays a true
   circle on any phone, with a second faint ring outside it: this language
   makes depth out of concentric strokes and nothing else. */
.aperture {
  position: relative;
  width: max(186px, min(100%, 380px,
    calc(100dvh - max(var(--tg-safe-top), env(safe-area-inset-top, 0px)) - 232px)));
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid var(--line);
}
.aperture::after {
  content: ""; position: absolute; inset: -9px;
  border-radius: 50%;
  border: 1px solid var(--line);
  opacity: .45;
  pointer-events: none;
}
#stage {
  display: block; width: 100%; height: 100%;
  border-radius: 50%;
  background: var(--bg);
  touch-action: none;
}
.field-note {
  flex: none;
  text-align: center;
  font-size: 10px; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-3);
}

/* the briefing, inside the aperture, until the round starts. Set short on
   purpose: a circle has no corners to hide a long line in. */
.brief {
  position: absolute; inset: 0;
  border-radius: 50%;
  background: var(--bg);
  display: grid; place-items: center;
  overflow: hidden;
  padding: 11%;
}
.brief-in {
  width: 100%; max-width: 236px;
  text-align: center;
  color: var(--accent);
}
.brief-in .gl { margin: 0 auto; }
.brief-h {
  margin-top: 10px;
  font-size: 10px; font-weight: 500;
  letter-spacing: .24em; text-transform: uppercase;
  color: var(--ink-3);
}
.steps { margin-top: 14px; }
.step {
  display: flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: 5px 0;
  font-size: 12.5px; line-height: 1.35;
  color: var(--ink-2);
}
.step::before {
  content: ""; flex: none;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent);
  opacity: .55;
}
.brief .cap-btn { margin-top: 16px; }

/* ── the board ───────────────────────────────────────────────────────────
   Positions are discs, not cells. Rows are parted by dots. */

.scope {
  display: flex; justify-content: center; gap: 9px;
  margin-top: 22px;
}
.seg-btn {
  padding: 9px 21px;
  border-radius: var(--pill);
  border: 1px solid var(--line);
  color: var(--ink-2);
  font-size: 11px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  transition: background var(--t), color var(--t), border-color var(--t);
}
.seg-btn.is-on { background: var(--accent); border-color: var(--accent); color: var(--bg); }

.board { margin-top: 22px; }
.rw {
  display: grid;
  grid-template-columns: 30px 36px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 11px 2px;
}
.rw + .rw {
  padding-top: 18px;
  background: var(--dots) 0 0 / 7px 7px repeat-x;
}
.rw-pos {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.rw-av {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 50%;
  /* shell.js hands each name a hue. Held to almost no chroma on purpose: a
     row of saturated discs would be the only colour in the app that is not
     the halo. */
  background: hsl(var(--h, 210) 16% 19%);
  border: 1px solid var(--line);
  font-size: 14px; font-weight: 500;
  color: var(--ink);
}
.rw-nm {
  min-width: 0;
  font-size: 15px;
  color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rw-sc {
  font-size: 19px; font-weight: 300;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.rw--top .rw-pos { border-color: var(--accent); color: var(--accent-d); }
.rw--me .rw-pos { background: var(--accent); border-color: var(--accent); color: var(--bg); }
.rw--me .rw-nm { color: var(--ink); font-weight: 500; }
.rw--me .rw-sc { color: var(--accent-d); }

/* ── preferences ─────────────────────────────────────────────────────────── */

.opts { margin-top: 20px; }
.opt {
  display: flex; align-items: center; gap: 16px;
  padding: 15px 2px;
}
.opt + .opt, .link-row + .link-row {
  background: var(--dots) 0 0 / 7px 7px repeat-x;
}
.opt > span { flex: 1; min-width: 0; font-size: 15px; }

/* a capsule rail with a disc running along it */
.sw {
  appearance: none; -webkit-appearance: none;
  flex: none; margin: 0; position: relative;
  width: 48px; height: 27px;
  border-radius: var(--pill);
  border: 1px solid var(--line);
  background: var(--tint);
  transition: background var(--t), border-color var(--t);
}
.sw::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 19px; height: 19px;
  border-radius: 50%;
  background: var(--ink-3);
  transition: transform var(--t), background var(--t);
}
.sw:checked { background: var(--tint-2); border-color: var(--accent); }
.sw:checked::after { transform: translateX(21px); background: var(--accent); }

.links { margin-top: 18px; }
.link-row {
  display: flex; align-items: center; gap: 14px;
  width: 100%;
  padding: 15px 2px;
  text-align: left;
  transition: color var(--t);
}
.link-row::before {
  content: ""; flex: none;
  width: 7px; height: 7px; border-radius: 50%;
  border: 1px solid var(--ink-3);
  transition: background var(--t), border-color var(--t);
}
.link-row span { flex: 1; min-width: 0; font-size: 15px; }
.link-row .gl { width: 16px; height: 16px; color: var(--ink-3); }
.link-row:active { color: var(--accent); }
.link-row:active::before { background: var(--accent); border-color: var(--accent); }

.sect { margin-top: 36px; text-align: center; }
.sect:first-child { margin-top: 2px; }
.sect + .opts, .sect + .links { margin-top: 14px; }

/* ── navigation: bottom, standard position ───────────────────────────────
   Four marks over a dotted rule. The active tab is not filled, it is haloed:
   a ring orbits in behind its glyph. */

.ring-nav {
  position: relative; flex: none;
  display: flex;
  background: var(--bg);
  padding: 12px 8px calc(10px + max(var(--tg-safe-bottom), env(safe-area-inset-bottom, 0px)));
}
.ring-nav::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0;
  height: 7px;
  background: var(--dots) 0 center / 7px 7px repeat-x;
  opacity: .8;
}
.ring-tab {
  position: relative;
  flex: 1;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 8px 0 2px;
  color: var(--ink-3);
  transition: color var(--t);
}
.ring-tab::before {
  content: ""; position: absolute; top: 2px; left: 50%;
  width: 36px; height: 36px; margin-left: -18px;
  border-radius: 50%;
  border: 1px solid var(--accent);
  opacity: 0;
  transform: scale(.62) rotate(-40deg);
  transition: opacity var(--t), transform var(--t);
}
.ring-tab span {
  font-size: 9px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
}
.ring-tab.is-on { color: var(--accent); }
.ring-tab.is-on::before { opacity: .55; transform: scale(1) rotate(0deg); }

/* ── overlays ────────────────────────────────────────────────────────────── */

.veil { position: fixed; inset: 0; z-index: 40; background: rgba(3, 5, 10, .72); }

/* The attached papers rise as a dome, not a card: the top edge is an arc, and
   the two bottom corners land on the corners of the phone itself. */
.doc {
  position: fixed; z-index: 50;
  left: 0; right: 0; bottom: 0;
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-radius: 50% 50% 0 0 / 52px 52px 0 0;
  display: flex; flex-direction: column;
  padding-bottom: max(var(--tg-safe-bottom), env(safe-area-inset-bottom, 0px));
  max-height: calc(100dvh - max(var(--tg-safe-top), env(safe-area-inset-top, 0px)) - 40px);
  /* Deliberately NOT animated. A panel that slides up from below sits, for the
     length of the animation, with its own Close control under the fold, and if
     a client throttles animations it stays there. It just appears. */
}
.doc-hd {
  flex: none;
  padding: 46px var(--mg) 16px;
  text-align: center;
}
.doc-ti {
  margin-top: 8px;
  font-size: 21px; font-weight: 400;
  line-height: 1.2;
}
.doc-bd {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 6px var(--mg) 4px;
  color: var(--ink-2);
  font-size: 14.5px;
}
.doc-bd p { margin-bottom: 14px; }
.doc-bd ul { margin-bottom: 14px; }
.doc-bd li { position: relative; padding: 0 0 10px 20px; }
.doc-bd li::before {
  content: ""; position: absolute; left: 2px; top: 9px;
  width: 6px; height: 6px; border-radius: 50%;
  border: 1px solid var(--accent);
}
.doc-bd b { font-weight: 500; color: var(--ink); }
.doc-ft {
  flex: none;
  display: flex; justify-content: center;
  padding: 14px var(--mg) 20px;
}

/* A pod: a stadium, so the head and the foot are true semicircles and the
   thing has no corner anywhere. Padding keeps the copy off the caps. */
.hold {
  position: fixed; inset: 0; z-index: 55;
  display: grid; place-items: center;
  padding: 20px;
  pointer-events: none;
}
.pod {
  pointer-events: auto;
  width: 100%; max-width: 302px;
  max-height: 100%;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--pill);
  padding: 54px 32px 50px;
  text-align: center;
  animation: swell var(--t) ease-out;
}
.pod-ring {
  width: 156px; height: 156px;
  margin: 16px auto 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 8px;
}
/* Held at 40px so a four-figure round still clears the ring it sits in. */
.pod-n { font-size: 40px; color: var(--ink); }
.pod-h {
  margin-top: 14px;
  font-size: 21px; font-weight: 400;
  line-height: 1.25;
}
.pod-s {
  margin-top: 12px;
  font-size: 13px;
  color: var(--ink-3);
}
.pod-acts {
  display: flex; flex-direction: column; align-items: center;
  gap: 8px;
  margin-top: 22px;
}
.pod-acts .cap-btn { width: 100%; }
/* the free-continue route, when the run holds one */
.pod > .cap-btn { width: 100%; margin-top: 18px; }

/* the ad offer: prints its reward and its free route before the tap. Hidden
   in this build, because no block id is configured. */
.pod-ad {
  display: block; width: 100%;
  margin-top: 16px;
  padding: 15px 24px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  text-align: center;
  transition: border-color var(--t);
}
.pod-ad:active { border-color: var(--accent); }
.pod-ad b { display: block; font-size: 14px; font-weight: 500; }
.pod-ad i {
  display: block;
  margin-top: 4px;
  font-size: 11.5px; font-style: normal; line-height: 1.4;
  color: var(--ink-3);
}
.pod-ad i:empty { display: none; }

/* the countdown before a continue: one figure, centred in a ring */
.count {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  background: rgba(3, 5, 10, .78);
  pointer-events: none;
}
.count span {
  width: 156px; height: 156px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--accent);
  font-size: 74px; font-weight: 300;
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  animation: swell var(--t) ease-out;
}

.beacon {
  position: fixed; z-index: 70;
  left: 50%; transform: translateX(-50%);
  bottom: calc(100px + max(var(--tg-safe-bottom), env(safe-area-inset-bottom, 0px)));
  max-width: calc(100% - 44px);
  background: var(--accent);
  color: var(--bg);
  border-radius: var(--pill);
  padding: 11px 22px;
  font-size: 13.5px; font-weight: 500;
  text-align: center;
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* ── reduce motion: kill all of it ───────────────────────────────────────── */

[data-reduce-motion="1"] *,
[data-reduce-motion="1"] *::before,
[data-reduce-motion="1"] *::after {
  animation: none !important;
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* Narrow phones: the figure ring gives, so its two satellites stay clear of
   the back and pause discs at the edges. */
@media (max-width: 361px) {
  .read { width: 92px; height: 92px; }
  .read b { font-size: 25px; }
  .chain { margin-left: 53px; }
  .guard { margin-right: 55px; }
  .sat b { width: 48px; height: 48px; font-size: 16px; }
  .sp-name { font-size: 26px; letter-spacing: .26em; text-indent: .26em; }
}

/* Short phones: the figures give first, the centre never moves. */
@media (max-height: 700px) {
  .sky { padding-top: calc(max(var(--tg-safe-top), env(safe-area-inset-top, 0px)) + 14px); }
  .dial { width: min(100%, 252px); margin-top: 20px; }
  .orbit { margin-top: 26px; }
  .sat b { width: 48px; height: 48px; font-size: 16px; }
  .lane { margin-top: 34px; }
  .read { width: 92px; height: 92px; }
  .read b { font-size: 25px; }
  .chain { margin-left: 54px; }
  .guard { margin-right: 54px; }
  .pod { padding: 44px 28px 40px; }
  .pod-ring { width: 132px; height: 132px; }
  .pod-n { font-size: 38px; }
  .count span { width: 124px; height: 124px; font-size: 58px; }
  .sp-name { font-size: 25px; }
}
