/* =============================================================================
   Hello World - the sky over Veresegyhaz
   Implements Resources/design/tokens.md and Resources/design/egbolt-spec.md.

   Rules this file obeys:
   - Every colour comes from a token (--sky-*, --c-*, --ground-*, --moon-*), never
     from a literal hex written here. The literals below live only in the :root
     token block and in the documented @supports fallbacks.
   - No @font-face, no CDN, no external URL of any kind.
   - The server writes the dynamic tokens into the inline style of .stage; this file
     only ever reads them.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Registered custom properties.
   Registration is what makes a custom property animatable, which is how the
   one-minute refresh can fade the sky instead of snapping it (spec 7.4). Browsers
   without @property simply snap - the picture stays correct, only less smooth.
   -------------------------------------------------------------------------- */
@property --star-f       { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --star-dim     { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --plate-alpha  { syntax: "<number>"; inherits: true; initial-value: 0.12; }
@property --vignette-alpha { syntax: "<number>"; inherits: true; initial-value: 0.1; }
@property --glow-a       { syntax: "<number>"; inherits: true; initial-value: 0.12; }
@property --moon-opacity { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --sun-corona-a { syntax: "<number>"; inherits: true; initial-value: 0.42; }
@property --dayness      { syntax: "<number>"; inherits: true; initial-value: 0; }
@property --dayness2     { syntax: "<number>"; inherits: true; initial-value: 0; }
@property --moon-illum   { syntax: "<number>"; inherits: true; initial-value: 0.5; }
@property --plate-blur   { syntax: "<length>"; inherits: true; initial-value: 16px; }
@property --sky-5        { syntax: "<color>";  inherits: true; initial-value: #23305A; }
@property --ground-base  { syntax: "<color>";  inherits: true; initial-value: #05070E; }

/* -----------------------------------------------------------------------------
   Token catalogue - tokens.md ch. 12, verbatim.
   -------------------------------------------------------------------------- */
:root {
  color-scheme: dark;

  /* typography */
  --font-display: ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont,
                  "Segoe UI Variable Display", "Segoe UI", Roboto, "Helvetica Neue",
                  Arial, "Noto Sans", sans-serif;
  --font-ui:      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --font-num:     ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                  "Liberation Mono", monospace;
  --fs-hello:   clamp(2.5rem, min(9.5vw, 13vh), 7rem);
  --fs-info:    clamp(.6875rem, 1.05vw, .875rem);
  --fs-compass: clamp(.6875rem, 1vw, .8125rem);
  --fs-chip:    clamp(.75rem, 1.05vw, .875rem);
  --fw-bold: 700; --fw-semi: 600; --fw-base: 400;
  --lh-tight: 1.05; --lh-base: 1.45;
  --ls-caps: .14em; --ls-display: -.02em;

  /* spacing + shape */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px;
  --radius-sm: 12px; --radius-md: 20px; --radius-pill: 999px;

  /* text colours */
  --c-text-strong: #FFFFFF;
  --c-text:        #E8EEF6;
  --c-text-muted:  rgb(255 255 255 / .78);
  --c-text-dim:    rgb(255 255 255 / .70);
  --c-plate-rgb:   5 10 22;
  --c-chip-bg:     rgb(8 12 24 / .72);
  --c-danger-bg:   #4A1418;
  --c-danger-text: #FFE9E6;
  --c-focus:       #FFD37A;
  --c-line:        rgb(255 255 255 / .55);
  --c-line-weak:   rgb(255 255 255 / .26);
  --c-border-glass: rgb(255 255 255 / .10);
  --c-submarker-sun:  #FFB169;
  --c-submarker-moon: #A9C0E8;

  /* the Moon's static colours */
  --moon-lit-a: #FFFDF6; --moon-lit-b: #F2EEE2; --moon-lit-c: #D9D3C4;
  --moon-mare:  #C7C1B2; --moon-dark:  #1B2233; --moon-glow: 220 231 255;

  /* star colours */
  --star-white: #FFFFFF; --star-blue: #CFE0FF; --star-warm: #FFE9C8;

  /* motion */
  --dur-fast: 180ms; --dur-base: 420ms; --dur-body: 900ms; --dur-sky: 1200ms;
  --dur-chip-in: 240ms; --dur-chip-out: 300ms;
  --ease-out:   cubic-bezier(.22,.61,.36,1);
  --ease-inout: cubic-bezier(.4,0,.2,1);

  /* z-index */
  --z-sky: 0; --z-stars: 10; --z-glow: 20; --z-bodies: 30; --z-ridge: 40;
  --z-haze: 45; --z-compass: 50; --z-submarker: 55; --z-vignette: 60;
  --z-hello: 70; --z-info: 80; --z-edgechip: 90;

  /* constants */
  --touch-min: 44px;
  --conflict-alpha: .62; --conflict-blur: 22px; --nojs-alpha: .5;

  /* geometry - mobile default */
  --horizon-y: 74%;
  --hello-top: 30%;
  --hello-max-w: calc(100% - 32px);
  --plate-pad: 14px 20px;
  --radius-plate: 22px;
  --plate-blur: 16px;
  --info-bottom: calc(env(safe-area-inset-bottom) + 12px);
  --sun-d: clamp(52px, 7.2vmax, 92px);
  --moon-d: calc(var(--sun-d) * .85);
  --haze-a: .22;
  --vignette-size: 118% 96%;

  /* safety defaults, should the server write nothing (must never be visible) */
  --sky-0: #040814; --sky-1: #060D1F; --sky-2: #0A1430;
  --sky-3: #101C40; --sky-4: #17244B; --sky-5: #23305A;
  --skyA-0: #040814; --skyA-1: #060D1F; --skyA-2: #0A1430;
  --skyA-3: #101C40; --skyA-4: #17244B; --skyA-5: #23305A;
  --skyB-0: #040814; --skyB-1: #060D1F; --skyB-2: #0A1430;
  --skyB-3: #101C40; --skyB-4: #17244B; --skyB-5: #23305A;
  --glow-rgb: 51 50 92; --glow-a: .12;
  --dayness: 0; --dayness2: 0;
  --star-f: 1; --star-dim: 1; --star-o-scale: 1;
  --plate-alpha: .12; --vignette-alpha: .10;
  --sun-vis: 0; --moon-vis: 0; --moon-opacity: 1; --moon-illum: .5;
  --sun-core: #FFFEF4; --sun-edge: #FFF0B8;
  --sun-corona-k: 2.6; --sun-corona-a: .42;
  --sun-x: 50%; --sun-k: 1; --sun-sub: 0;
  --moon-x: 50%; --moon-k: 1; --moon-sub: 0; --moon-chi: 100deg;
  --ground-base: #05070E;
}

@media (min-width: 768px) {
  :root { --horizon-y: 77%; --hello-top: 32%; --hello-max-w: min(86%, 660px);
          --plate-pad: 18px 28px; --radius-plate: 26px;
          --info-bottom: calc(env(safe-area-inset-bottom) + 16px);
          --vignette-size: 108% 92%; }
}
@media (min-width: 1280px) {
  :root { --horizon-y: 78%; --hello-top: 33%; --hello-max-w: min(72%, 900px);
          --plate-pad: 22px 36px; --radius-plate: 30px;
          --info-bottom: calc(env(safe-area-inset-bottom) + 20px);
          --vignette-size: 100% 88%; }
}
/* The landscape-phone branch comes last on purpose: it overrides all of the above. */
@media (orientation: landscape) and (max-height: 480px) {
  :root { --horizon-y: 70%; --hello-top: 30%; --hello-max-w: min(76%, 520px);
          --plate-pad: 10px 18px; --radius-plate: 20px;
          --info-bottom: calc(env(safe-area-inset-bottom) + 8px);
          --vignette-size: 130% 100%; }
}

/* Derived ground colours - a single rule, from the horizon stop (spec 6.4). */
.stage {
  --ground-far:  color-mix(in oklab, var(--sky-5) 26%, var(--ground-base));
  --ground-near: color-mix(in oklab, var(--sky-5) 11%, var(--ground-base));
  --ground-deep: color-mix(in oklab, var(--sky-5)  4%, var(--ground-base));
}
@supports not (color: color-mix(in oklab, red, blue)) {
  .stage { --ground-far: #2A3140; --ground-near: #171C27; --ground-deep: #0C1018; }
}

/* -----------------------------------------------------------------------------
   Reset and the scroll-free stage (spec 8.3)
   -------------------------------------------------------------------------- */
* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  font-family: var(--font-ui);
  font-size: var(--fs-info);
  line-height: var(--lh-base);
  color: var(--c-text);
  -webkit-text-size-adjust: 100%;
}

.stage {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100dvh;
  overflow: hidden;
  animation: stage-in var(--dur-base) var(--ease-out) both;
  /* The tokens the refresh fades; every one of them is @property-registered above. */
  transition:
    --star-f var(--dur-sky) linear,
    --star-dim var(--dur-sky) linear,
    --plate-alpha var(--dur-sky) linear,
    --vignette-alpha var(--dur-sky) linear,
    --glow-a var(--dur-sky) linear,
    --moon-opacity var(--dur-sky) linear,
    --sun-corona-a var(--dur-sky) linear,
    --dayness var(--dur-sky) linear,
    --dayness2 var(--dur-sky) linear,
    --moon-illum var(--dur-sky) linear,
    --sky-5 var(--dur-sky) linear,
    --ground-base var(--dur-sky) linear;
}
@supports not (height: 100dvh) { .stage { height: 100vh; } }

@keyframes stage-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

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

/* -----------------------------------------------------------------------------
   z0 - the sky gradient. Two layers, cross-faded on refresh (spec 7.4).
   The gradient runs from the top of the viewport to the HORIZON LINE, not to the
   bottom of the screen, which is why the layer stops at --horizon-y.
   -------------------------------------------------------------------------- */
.sky {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: var(--horizon-y);
  z-index: var(--z-sky);
  will-change: opacity;
  transition: opacity var(--dur-sky) linear;
}
.sky svg { display: block; width: 100%; height: 100%; }

.sky--b { opacity: 0; }
.stage[data-sky-active="b"] .sky--a { opacity: 0; }
.stage[data-sky-active="b"] .sky--b { opacity: 1; }

.sky--a .s0 { stop-color: var(--skyA-0); }
.sky--a .s1 { stop-color: var(--skyA-1); }
.sky--a .s2 { stop-color: var(--skyA-2); }
.sky--a .s3 { stop-color: var(--skyA-3); }
.sky--a .s4 { stop-color: var(--skyA-4); }
.sky--a .s5 { stop-color: var(--skyA-5); }
.sky--b .s0 { stop-color: var(--skyB-0); }
.sky--b .s1 { stop-color: var(--skyB-1); }
.sky--b .s2 { stop-color: var(--skyB-2); }
.sky--b .s3 { stop-color: var(--skyB-3); }
.sky--b .s4 { stop-color: var(--skyB-4); }
.sky--b .s5 { stop-color: var(--skyB-5); }

/* -----------------------------------------------------------------------------
   z10 - the star field (spec 6.3). Position and base brightness come from the
   server; whether a star is visible at all is pure CSS.
   -------------------------------------------------------------------------- */
.stars { position: absolute; inset: 0; z-index: var(--z-stars); }

.star {
  position: absolute;
  left: var(--x);
  top: calc(var(--horizon-y) * var(--yk));
  border-radius: 50%;
  background: currentColor;
  /* --th is the star's brightness rank: the bright ones come out first. */
  --star-computed: calc(var(--o) * var(--star-o-scale) * var(--star-dim)
                        * clamp(0, (var(--star-f) - var(--th)) * 5, 1));
  opacity: var(--star-computed);
}

.star--s1 { width: 1.4px; height: 1.4px; }
.star--s2 { width: 2.2px; height: 2.2px; }
.star--s3 { width: 3.2px; height: 3.2px; }
.star--s4 { width: 4.4px; height: 4.4px; }

.star--white { color: var(--star-white); }
.star--blue  { color: var(--star-blue); }
.star--warm  { color: var(--star-warm); }

/* A discreet four-pointed sparkle on the brightest size class only. */
.star--s4::before,
.star--s4::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 300%; height: 1px;
  background: linear-gradient(90deg, transparent, currentColor, transparent);
  opacity: .35;
}
.star--s4::before { transform: translate(-50%, -50%); }
.star--s4::after  { transform: translate(-50%, -50%) rotate(90deg); }

@keyframes twinkle {
  from { opacity: calc(var(--star-computed) * .82); }
  to   { opacity: calc(var(--star-computed) * 1); }
}
.star--big { animation: twinkle var(--tw-dur) var(--ease-inout) infinite alternate; }

/* On a phone the smallest stars are a touch dimmer, so the field stays calm (8.2). */
@media (max-width: 767px) { .star--s1 { --star-o-scale: .85; } }

/* -----------------------------------------------------------------------------
   z20 - horizon glow at the Sun's azimuth (spec 5.6). It stays after sunset;
   that is what twilight looks like.
   -------------------------------------------------------------------------- */
.glow {
  position: absolute; inset: 0;
  z-index: var(--z-glow);
  mix-blend-mode: screen;
  pointer-events: none;
}
.glow--sun {
  background: radial-gradient(
      ellipse 58% 34% at var(--sun-x) var(--horizon-y),
      rgb(var(--glow-rgb) / var(--glow-a)) 0%,
      rgb(var(--glow-rgb) / calc(var(--glow-a) * .45)) 38%,
      rgb(var(--glow-rgb) / 0) 72%);
}
@supports not (mix-blend-mode: screen) { .glow { opacity: .85; } }

/* -----------------------------------------------------------------------------
   z30 - the Sun and the Moon.
   The wrappers are positioned in per-cent, but every disc lives in a FIXED PIXEL
   SVG box, so a circle is always a circle even where the panorama is stretched
   (spec 3.3 / 4).
   -------------------------------------------------------------------------- */
.bodies { position: absolute; inset: 0; z-index: var(--z-bodies); pointer-events: none; }

.body {
  position: absolute;
  display: grid;
  place-items: center;
  transform: translate(-50%, -50%);
  transition: left var(--dur-body) var(--ease-inout),
              top  var(--dur-body) var(--ease-inout);
}
.body svg { display: block; overflow: visible; }

.body--sun {
  left: var(--sun-x);
  top: calc(var(--horizon-y) * var(--sun-k));
  width:  calc(var(--sun-d) * var(--sun-corona-k));
  height: calc(var(--sun-d) * var(--sun-corona-k));
}
.sun__corona {
  position: absolute;
  width: 100%; height: 100%;
  mix-blend-mode: screen;
  animation: corona-breathe 6s var(--ease-inout) infinite alternate;
}
@keyframes corona-breathe {
  from { transform: scale(1);     opacity: .94; }
  to   { transform: scale(1.035); opacity: 1; }
}
/* r = 26 in a 100-unit viewBox, so the box is the disc diameter / 0.52 (spec 6.1). */
.sun__disc {
  position: absolute;
  width:  calc(var(--sun-d) / .52);
  height: calc(var(--sun-d) / .52);
}
#sunCore .k0 { stop-color: var(--sun-core); }
#sunCore .k1 { stop-color: var(--sun-core); }
#sunCore .k2 { stop-color: var(--sun-edge); }
#sunCorona .c0 { stop-color: var(--sun-edge); stop-opacity: var(--sun-corona-a); }
#sunCorona .c1 { stop-color: var(--sun-edge); stop-opacity: calc(var(--sun-corona-a) * .55); }
#sunCorona .c2 { stop-color: var(--sun-edge); stop-opacity: calc(var(--sun-corona-a) * .18); }
#sunCorona .c3 { stop-color: var(--sun-edge); stop-opacity: 0; }
.sun__rim { stroke: var(--sun-edge); opacity: .5; }

.body--moon {
  left: var(--moon-x);
  top: calc(var(--horizon-y) * var(--moon-k));
  width: var(--moon-d); height: var(--moon-d);
  opacity: var(--moon-opacity);
  transition: left var(--dur-body) var(--ease-inout),
              top  var(--dur-body) var(--ease-inout),
              opacity var(--dur-sky) linear;
}
/* r = 40 in a 100-unit viewBox, so the box is the disc diameter / 0.8. */
.moon__disc {
  position: absolute;
  width:  calc(var(--moon-d) / .8);
  height: calc(var(--moon-d) / .8);
}
.moon__glow {
  position: absolute;
  width:  calc(var(--moon-d) * 2.4);
  height: calc(var(--moon-d) * 2.4);
  border-radius: 50%;
  mix-blend-mode: screen;
  background: radial-gradient(closest-side,
      rgb(var(--moon-glow) / calc(.30 * (.35 + .65 * var(--moon-illum)) * (1 - var(--dayness2)))) 0%,
      rgb(var(--moon-glow) / 0) 100%);
}
#moonLit .m0 { stop-color: var(--moon-lit-a); }
#moonLit .m1 { stop-color: var(--moon-lit-b); }
#moonLit .m2 { stop-color: var(--moon-lit-c); }
/* Earthshine: the ashen glow that only reads on a thin crescent, and never by day. */
.moon__earthshine {
  fill: var(--moon-dark);
  opacity: calc(.34 * (1 - var(--moon-illum)) * (1 - var(--dayness2)));
}
.moon__maria { fill: var(--moon-mare); opacity: .38; }
.moon__limb  { stroke: var(--c-text-strong); opacity: .25; }

/* Both bodies are always in the DOM; the server (and later app.js) simply hides the one
   that is below the horizon or outside the panorama. Keeping the nodes means a refresh
   never has to build markup - it only writes attributes. */
.body[hidden], .submarker[hidden], .subline[hidden],
.edgechip[hidden], .edgeband[hidden], .glow[hidden] { display: none; }

/* -----------------------------------------------------------------------------
   z40 - ground, silhouette, haze (spec 6.4)
   -------------------------------------------------------------------------- */
.ground {
  position: absolute;
  left: 0; right: 0; top: var(--horizon-y); bottom: 0;
  z-index: var(--z-ridge);
  background: linear-gradient(180deg,
      var(--ground-far) 0%, var(--ground-near) 55%, var(--ground-deep) 100%);
}
.ridge__defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.ridge {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  /* viewBox row y = 25 (10% of 250) has to land exactly on the horizon line. */
  top: calc((var(--horizon-y) - 10%) / .9);
  z-index: var(--z-ridge);
}
.ridge__svg { display: none; width: 100%; height: 100%; }
.ridge__svg--m { display: block; }
@media (min-width: 768px) {
  .ridge__svg--m { display: none; }
  .ridge__svg--t { display: block; }
}
@media (min-width: 1280px) {
  .ridge__svg--t { display: none; }
  .ridge__svg--d { display: block; }
}
.ridge__far  { fill: var(--ground-far); }
.ridge__near { fill: var(--ground-near); }

.haze {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(100% - var(--horizon-y));
  height: 6vh;
  z-index: var(--z-haze);
  mix-blend-mode: screen;
  pointer-events: none;
  background: linear-gradient(0deg, rgb(var(--glow-rgb) / var(--haze-a)), transparent);
}

/* -----------------------------------------------------------------------------
   z50 - compass scale (spec 6.6)
   -------------------------------------------------------------------------- */
.compass {
  position: absolute;
  left: 0; right: 0; top: var(--horizon-y); bottom: 0;
  z-index: var(--z-compass);
  pointer-events: none;
}
.compass__tick, .compass__major { position: absolute; top: 0; display: block; }
.compass__tick  { width: 1px; height: 5px;  background: var(--c-line-weak);
                  transform: translateX(-50%); }
.compass__major { width: 2px; height: 10px; background: var(--c-line);
                  transform: translateX(-50%); }
.compass__major--start { transform: none; }
.compass__major--end   { transform: translateX(-100%); }

.compass__label {
  position: absolute;
  top: 16px;
  font-family: var(--font-ui);
  font-size: var(--fs-compass);
  font-weight: var(--fw-semi);
  letter-spacing: var(--ls-caps);
  /* Spec 6.6 (v1.1): the label text is already final in the markup - "DNy", "Ny" - and the
     Hungarian digraph "ny" takes a capital on its FIRST letter only. Any case transform here
     would print DNY/NY, so it is forbidden; the even, sign-like look comes from the weight
     and the letter-spacing above. Declared explicitly (not just omitted) so that the rule
     states the intent and tests/render.php can assert it. */
  text-transform: none;
  font-variant-caps: normal;
  color: var(--c-text-muted);
  text-shadow: 0 1px 2px rgb(0 0 0 / .65);
  white-space: nowrap;
}
.compass__label--mid   { transform: translateX(-50%); }
.compass__label--start { padding-left: 10px; }
.compass__label--end   { transform: translateX(-100%); padding-right: 10px; }

@media (orientation: landscape) and (max-height: 480px) {
  .compass__major { height: 8px; }
  .compass__tick  { height: 4px; }
  .compass__label { top: 13px; }
}

/* -----------------------------------------------------------------------------
   z55 - depth markers for a body below the horizon (spec 6.5)
   -------------------------------------------------------------------------- */
.submarkers { position: absolute; inset: 0; z-index: var(--z-submarker); pointer-events: none; }
.submarker {
  position: absolute;
  width:  calc(var(--sun-d) * .58);
  height: calc(var(--sun-d) * .58);
  transform: translate(-50%, -50%);
  opacity: .42;
  transition: left var(--dur-body) var(--ease-inout),
              top  var(--dur-body) var(--ease-inout);
}
.submarker--deep { opacity: .22; }
.submarker svg { width: 100%; height: 100%; display: block; }

/* A 1px guide line from the horizon line down to the marker. */
.subline {
  position: absolute;
  top: var(--horizon-y);
  width: 1px;
  transform: translateX(-50%);
  opacity: .45;
  background: linear-gradient(0deg, currentColor, transparent);
}
.submarker--sun, .subline--sun  { color: var(--c-submarker-sun); }
.submarker--moon, .subline--moon { color: var(--c-submarker-moon); }

.submarker--sun {
  left: var(--sun-x);
  top: calc(var(--horizon-y) + (100% - var(--horizon-y)) * var(--sun-sub));
}
.subline--sun {
  left: var(--sun-x);
  height: calc((100% - var(--horizon-y)) * var(--sun-sub));
}
.submarker--moon {
  left: var(--moon-x);
  top: calc(var(--horizon-y) + (100% - var(--horizon-y)) * var(--moon-sub));
}
.subline--moon {
  left: var(--moon-x);
  height: calc((100% - var(--horizon-y)) * var(--moon-sub));
}

/* -----------------------------------------------------------------------------
   z60 - vignette
   -------------------------------------------------------------------------- */
.vignette {
  position: absolute; inset: 0;
  z-index: var(--z-vignette);
  pointer-events: none;
  background: radial-gradient(ellipse var(--vignette-size) at 50% 50%,
      rgb(0 0 0 / 0) 45%, rgb(0 0 0 / var(--vignette-alpha)) 100%);
}

/* -----------------------------------------------------------------------------
   z70 - the "Hello World" plate.
   The text is always white; the contrast comes from the plate behind it (10.3).
   -------------------------------------------------------------------------- */
.hello {
  position: absolute;
  left: 50%;
  top: var(--hello-top);
  transform: translate(-50%, -50%);
  z-index: var(--z-hello);
  max-width: var(--hello-max-w);
  padding: var(--plate-pad);
  border: 1px solid var(--c-border-glass);
  border-radius: var(--radius-plate);
  background: rgb(var(--c-plate-rgb) / var(--plate-alpha));
  backdrop-filter: blur(var(--plate-blur)) brightness(.78) saturate(1.12);
  -webkit-backdrop-filter: blur(var(--plate-blur)) brightness(.78) saturate(1.12);
  animation: hello-in 460ms var(--ease-out) 120ms both;
  transition: --plate-alpha var(--dur-sky) linear,
              --plate-blur var(--dur-sky) linear;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .hello { background: rgb(var(--c-plate-rgb) / calc(var(--plate-alpha) + .16)); }
}
@keyframes hello-in {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 8px)); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}

/* Set by app.js when a body would sit behind the text (spec 8.4). Without it the
   headline is unreadable at noon - the Sun's corona is effectively white. */
.hello--conflict { --plate-alpha: var(--conflict-alpha); --plate-blur: var(--conflict-blur); }
/* Without JavaScript nobody can measure the overlap, so we pay a fixed premium. */
html:not(.js) .hello { --plate-alpha: var(--nojs-alpha); }

.hello h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-hello);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  color: var(--c-text-strong);
  text-shadow: 0 1px 2px rgb(0 0 0 / .45), 0 4px 24px rgb(0 0 0 / .35);
  text-wrap: balance;
  text-align: center;
}

/* -----------------------------------------------------------------------------
   z80 - the info strip
   -------------------------------------------------------------------------- */
.info {
  position: absolute;
  left: 0; right: 0;
  bottom: var(--info-bottom);
  z-index: var(--z-info);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: 0 var(--space-4);
  font-size: var(--fs-info);
  color: var(--c-text);
}

.times {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-1) var(--space-5);
  margin: 0;
}
.times__cell { display: flex; align-items: baseline; gap: var(--space-2); white-space: nowrap; }
.times dt { color: var(--c-text-dim); }
.times dd { margin: 0; font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.times__label-short { display: none; }
.times[hidden], .info__note[hidden] { display: none; }

@media (min-width: 768px) {
  .times { grid-template-columns: repeat(4, auto); gap: var(--space-5); }
}
@media (orientation: landscape) and (max-height: 480px) {
  .times { grid-template-columns: repeat(4, auto); gap: var(--space-4); }
  .times__label { display: none; }
  .times__label-short { display: inline; }
}

.info__note {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  max-width: min(92%, 620px);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--c-chip-bg);
  text-align: center;
}
.info__note-main { font-weight: var(--fw-semi); }
.info__note-sub  { color: var(--c-text-dim); }
.info__note--error { background: var(--c-danger-bg); color: var(--c-danger-text); }
.info__note--error .info__note-sub { color: var(--c-danger-text); opacity: .88; }

.info__noscript {
  max-width: min(92%, 520px);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--c-chip-bg);
  color: var(--c-text-dim);
  text-align: center;
}

.info__status { display: flex; align-items: center; min-width: var(--space-2); }
.refresh-dot {
  display: block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgb(255 255 255 / .8);
  animation: pulse 1.1s var(--ease-inout) infinite alternate;
}
.refresh-dot[hidden] { display: none; }
@keyframes pulse { from { opacity: .25; } to { opacity: .9; } }

/* Refresh failure chip (spec 7.3/b). The picture stays; only this slides in above it. */
.errorchip {
  position: absolute;
  left: 50%;
  bottom: calc(var(--info-bottom) + 44px);
  transform: translateX(-50%);
  z-index: var(--z-info);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: min(94%, 560px);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--c-danger-bg);
  color: var(--c-danger-text);
  font-size: var(--fs-chip);
  animation: chip-in var(--dur-chip-in) var(--ease-out) both;
  transition: opacity var(--dur-chip-out) var(--ease-out);
}
.errorchip--faded { opacity: .35; }
@keyframes chip-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.errorchip__text { flex: 1 1 auto; }
.errorchip__button {
  flex: 0 0 auto;
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 10px 18px;
  border: 1px solid rgb(255 255 255 / .34);
  border-radius: var(--radius-pill);
  background: rgb(255 255 255 / .12);
  color: var(--c-danger-text);
  font: inherit;
  font-weight: var(--fw-semi);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.errorchip__button:hover  { background: rgb(255 255 255 / .20); }
.errorchip__button:active { background: rgb(255 255 255 / .26); transform: scale(.97); }
.errorchip__button:focus-visible { outline: 3px solid var(--c-focus); outline-offset: 2px; }
.errorchip__button[disabled] { opacity: .5; cursor: not-allowed; }

/* -----------------------------------------------------------------------------
   z90 - edge chips for a body in the northern band (spec 6.7)
   -------------------------------------------------------------------------- */
.edgechips {
  position: absolute; inset: 0;
  z-index: var(--z-edgechip);
  pointer-events: none;
}
.edgeband {
  position: absolute;
  top: 0; height: var(--horizon-y);
  width: 4px;
  background: linear-gradient(180deg, transparent, rgb(255 255 255 / .18), transparent);
}
.edgeband--left  { left: 0; }
.edgeband--right { right: 0; }

.edgechip {
  position: absolute;
  top: 46%;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  background: var(--c-chip-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-size: var(--fs-chip);
  color: rgb(255 255 255 / .88);
  white-space: nowrap;
}
.edgechip--left  { left: 10px; }
.edgechip--right { right: 10px; }
/* Two chips on the same edge stack with an 8px gap. */
.edgechip--left:not([hidden])  ~ .edgechip--left:not([hidden])  { top: calc(46% + 38px); }
.edgechip--right:not([hidden]) ~ .edgechip--right:not([hidden]) { top: calc(46% + 38px); }

/* -----------------------------------------------------------------------------
   Preferences
   -------------------------------------------------------------------------- */
@media (prefers-contrast: more) {
  .hello { --plate-alpha: calc(var(--plate-alpha) + .18); border-color: rgb(255 255 255 / .3); }
  .compass__label { color: var(--c-text-strong); }
  .info { color: var(--c-text-strong); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .star--big   { animation: none !important; opacity: var(--star-computed); }
  .sun__corona { animation: none !important; transform: none !important; }
  .refresh-dot { animation: none !important; opacity: .9; }
  .stage, .hello { animation: none !important; opacity: 1; transform: none; }
  .hello { transform: translate(-50%, -50%); }
}
