/* RetroForecast web — the early-90s cable weather visual system.
   Palette and chrome mirror the iOS app's RetroTheme.swift exactly. */

/* FONT DROP-IN: like the iOS app, the full WeatherStar-era look comes from a
   Star4000-style font you have rights to. Drop it in assets/fonts/ as
   Star4000.woff2 and it takes over automatically; VT323 (OFL, bundled) is the
   licensing-clean default. */
@font-face {
  font-family: 'Star4000';
  src: url('../assets/fonts/Star4000.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'VT323';
  src: url('../assets/fonts/VT323.woff2') format('woff2');
  font-display: swap;
}

:root {
  --blue-top: #121C78;
  --blue-bottom: #030538;
  --panel-top: #1A298C;
  --panel-bottom: #080D4D;
  --border-light: #8C9EF2;
  --border-dark: #050826;
  --yellow: #FFD11A;
  --cyan: #73E6FF;
  --white: #FFFFFF;
  --header-orange: #FF780D;
  --header-magenta: #940D73;
  --alert-red: #F23029;
  --moon-pale: #F2F2BF;

  /* Retro chrome scale, the web port of RetroScale. JS ramps it 1.0 -> 2.0
     against a 1280x720 reference raster, measured on the *picture* rather
     than the window (see updateScale in main.js); phones stay at 1.0. */
  --k: 1;

  /* Widest a line of slide content gets. Now that the picture runs to 16:9,
     an uncapped stat row parks its label and its value a foot apart; this
     keeps label/value pairs readable while the panel still spans the frame. */
  --measure: calc(960px * var(--k));

  --font-display: 'Star4000', 'VT323', 'Courier New', monospace;
  /* Reading font. The pixel face is a broadcast typeface — it belongs on the
     slides, not on paragraphs someone has to actually read. Settings prose and
     the privacy/support documents use this instead. */
  --font-ui: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI',
             Roboto, Helvetica, Arial, sans-serif;
  /* Fixed-px drop shadows: em-based ones double-print at display sizes. */
  --shadow-hard: 3px 3px 0 rgba(0, 0, 0, 0.8);
  --shadow-soft: 2px 2px 0 rgba(0, 0, 0, 0.7);
  --panel-radius: 10px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body { height: 100%; }

body {
  background: #000;
  font-family: var(--font-display);
  color: var(--white);
  overflow: hidden;
  -webkit-font-smoothing: none;
  text-rendering: optimizeSpeed;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* The picture fills the viewport height always, and the viewport width up to
   16:9. Past that — ultrawides, maximized windows on very wide panels — it
   pillarboxes and centers rather than stretching the broadcast into a strip.
   Phones, tablets, and ordinary laptops (all <= 16:9) therefore fill edge to
   edge; only genuinely wider-than-broadcast canvases get side bars, and those
   read as a TV in a dark room, not as an unfinished column.

   The retro 4:3 feel is carried by the chrome scale (--k in main.js, which
   ramps with the picture size) rather than by starving the frame. */
#app {
  position: relative;
  width: min(100vw, calc(100dvh * 16 / 9));
  height: 100dvh;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  background: linear-gradient(to bottom, var(--blue-top), var(--blue-bottom));
  box-shadow: 0 0 calc(70px * var(--k)) rgba(60, 92, 220, 0.22);
  overflow: hidden;
}

/* ---------- Header: the sunburst bar ---------- */

#retro-header {
  display: flex;
  align-items: center;
  gap: calc(14px * var(--k));
  padding: calc(10px * var(--k)) calc(16px * var(--k));
  padding-top: max(calc(10px * var(--k)), env(safe-area-inset-top));
  background: linear-gradient(to right, var(--header-orange), var(--header-magenta));
  border-bottom: 2px solid var(--border-dark);
  flex: none;
}

.logo-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 0.95;
  padding: calc(6px * var(--k)) calc(10px * var(--k));
  background: linear-gradient(to bottom, var(--panel-top), var(--panel-bottom));
  border: 2px solid var(--yellow);
  border-radius: calc(6px * var(--k));
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.6);
  flex: none;
}
.logo-retro { font-size: calc(18px * var(--k)); color: var(--yellow); }
.logo-forecast { font-size: calc(14px * var(--k)); color: var(--white); }

.title-block {
  flex: 1 1 auto;
  min-width: 0;
  text-shadow: var(--shadow-hard);
}
#slide-title {
  font-size: calc(26px * var(--k));
  color: var(--white);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.05;
}
#location-name {
  font-size: calc(19px * var(--k));
  color: var(--yellow);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.clock-block {
  flex: none;
  text-align: right;
  text-shadow: var(--shadow-hard);
}
#clock-time {
  font-size: calc(24px * var(--k));
  color: var(--white);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
#clock-date { font-size: calc(17px * var(--k)); color: var(--yellow); }
#paused-chip[hidden] { display: none; }
#paused-chip {
  display: inline-block;
  font-size: calc(14px * var(--k));
  color: var(--blue-bottom);
  background: var(--yellow);
  border-radius: 4px;
  padding: 0 8px;
  text-shadow: none;
}

/* ---------- Stage & slides ---------- */

#stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  cursor: pointer;
}

.slide-layer {
  position: absolute;
  inset: 0;
  padding: 16px;
  opacity: 0;
  transition: opacity 0.5s ease-in-out;
  pointer-events: none;
}
.slide-layer.visible { opacity: 1; pointer-events: auto; }

/* The bordered blue panel every slide is built from (RetroPanel). */
.retro-panel {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: calc(16px * var(--k));
  background: linear-gradient(to bottom, var(--panel-top), var(--panel-bottom));
  border: 2px solid var(--border-light);
  border-radius: var(--panel-radius);
  box-shadow: inset 0 0 0 2px rgba(5, 8, 38, 0.6);
  overflow: hidden;
}

.retro-divider {
  height: 2px;
  background: var(--border-light);
  opacity: 0.6;
  flex: none;
}

/* ---------- Vertical rhythm ----------

   Every slide is a column of *bands* inside the panel. A band sizes to its
   content, takes a weighted share of whatever height is left over, and
   centers its own content inside that share. Leftover height therefore turns
   into even breathing room between bands instead of pooling into one dead
   zone under the last row — which is what a fixed-height stack does once the
   picture is 900px+ tall.

   Repeating bands (forecast days, observation rows) grow to fill but stop at
   a max-height, so six rows on a 1440px-tall display read as a table with
   generous spacing rather than six lines marooned an inch apart. */

.band { min-height: 0; display: flex; flex-direction: column; justify-content: center; }

.cc-panel > *, .obs-table, .haz-wrap {
  width: 100%;
  max-width: var(--measure);
  margin-left: auto;
  margin-right: auto;
}

/* ---------- Current conditions ---------- */

.cc-panel { gap: calc(8px * var(--k)); }

.cc-headline {
  flex: 6 1 auto;
  min-height: 0;
  display: flex;
  gap: calc(20px * var(--k));
  align-items: center;
}
.cc-main {
  flex: 1 1 50%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(10px * var(--k));
}
.cc-main .retro-icon { filter: drop-shadow(3px 3px 0 rgba(0,0,0,0.6)); }
.cc-temp {
  font-size: calc(96px * var(--k));
  line-height: 0.9;
  color: var(--white);
  text-shadow: var(--shadow-hard);
}
.cc-cond {
  font-size: calc(30px * var(--k));
  color: var(--yellow);
  text-align: center;
  text-shadow: var(--shadow-hard);
  line-height: 1.05;
}
.cc-stats {
  flex: 1 1 50%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: calc(10px * var(--k));
  justify-content: center;
}

.stat-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: calc(21px * var(--k));
  text-shadow: var(--shadow-soft);
  white-space: nowrap;
}
.stat-row .label { color: var(--yellow); }
.stat-row .value { color: var(--white); margin-left: auto; }
.stat-row .value.cyan { color: var(--cyan); }

/* A band may never be squeezed below the height its own content needs.
   Without this, flex shrinks every band in proportion to its weight even when
   one of them has no slack left, and the overflow paints straight over the
   neighbour below — which is how the UV INDEX row ended up sitting on top of
   the rule above HOURLY FORECAST on a phone. Any genuine shortfall is taken
   from the bands that can still absorb it instead. */
.cc-headline, .hourly-strip { min-height: min-content; }

.hourly-strip { flex: 3 1 auto; text-align: center; }
.cc-today { flex: 1.4 1 auto; }
.hourly-title {
  font-size: calc(21px * var(--k));
  color: var(--yellow);
  text-shadow: var(--shadow-hard);
  margin-bottom: calc(10px * var(--k));
}
.hourly-cells { display: flex; }
.hour-cell {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(7px * var(--k));
  text-shadow: var(--shadow-soft);
}
.hour-cell .hour { font-size: calc(16px * var(--k)); color: var(--cyan); }
.hour-cell .temp { font-size: calc(23px * var(--k)); color: var(--white); }

.today-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  max-width: calc(560px * var(--k));
  margin: 0 auto;
  width: 100%;
  text-shadow: var(--shadow-soft);
  white-space: nowrap;
}
.today-row .pair { display: flex; align-items: baseline; gap: calc(8px * var(--k)); }
.today-row .tag { font-size: calc(16px * var(--k)); color: var(--cyan); }
.today-row .tag.hi { color: var(--yellow); }
.today-row .head { font-size: calc(21px * var(--k)); color: var(--yellow); }
.today-row .val { font-size: calc(21px * var(--k)); color: var(--white); }

/* Steady-pressure arrow: rotate the up arrow, since the pixel font has no
   right arrow glyph. */
.arrow-steady { display: inline-block; transform: rotate(90deg); }

/* ---------- Latest observations table ---------- */

.obs-table {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(8px * var(--k));
  height: 100%;
}
.obs-title {
  font-size: calc(26px * var(--k));
  color: var(--yellow);
  text-align: center;
  text-shadow: var(--shadow-hard);
  margin-bottom: calc(6px * var(--k));
  flex: none;
}
.obs-head, .obs-row {
  display: grid;
  grid-template-columns: 1fr calc(70px * var(--k)) calc(120px * var(--k)) calc(105px * var(--k));
  gap: 8px;
  align-items: center;
}
.obs-head {
  font-size: calc(18px * var(--k));
  color: var(--cyan);
}
.obs-head > :not(:first-child), .obs-row > :not(:first-child) { text-align: right; }
.obs-row {
  flex: 1 1 auto;
  /* Grow into the free height, but stop before rows drift so far apart that
     the table stops reading as one block. */
  max-height: calc(58px * var(--k));
  font-size: calc(24px * var(--k));
  text-shadow: var(--shadow-soft);
  min-height: 0;
}
.obs-row .city {
  color: var(--yellow);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.obs-row .val { color: var(--white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---------- Regional map ---------- */

.regional-wrap { position: relative; height: 100%; border-radius: calc(var(--panel-radius) - 2px); overflow: hidden; }
.regional-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.map-labels { position: absolute; inset: 0; pointer-events: none; }

/* City pins and leader lines. Crisp screen-resolution SVG rather than canvas:
   the map buffer is 260px tall and upscales ~3.5x, which turns a modest pin
   into a chunky block that hides more radar than the label did. */
.map-pins {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.map-pins .pin-halo { fill: var(--border-dark); }
.map-pins .pin-core { fill: var(--white); }
.map-pins .pin-core.center { fill: var(--yellow); }
.map-pins .leader-casing { stroke: var(--border-dark); }
.map-pins .leader { stroke: var(--border-light); }
.map-pins .leader.center { stroke: var(--yellow); }

/* No filled box. A boxed tag blanks out a whole rectangle of the radar it is
   annotating; a chyron-style hard outline occludes only the glyph ink, which
   is roughly a fifth of the pixels and is also what broadcast graphics
   actually did over video. The exact city position is marked by a pin drawn
   on the canvas, so the text is free to move off the weather — see the
   placement scoring in slides/regional.js. */
.map-label {
  --outline: calc(1.6px * var(--k));
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 calc(2px * var(--k));
  white-space: nowrap;
  text-shadow:
    var(--outline) 0 0 var(--border-dark),
    calc(var(--outline) * -1) 0 0 var(--border-dark),
    0 var(--outline) 0 var(--border-dark),
    0 calc(var(--outline) * -1) 0 var(--border-dark),
    var(--outline) var(--outline) 0 var(--border-dark),
    calc(var(--outline) * -1) var(--outline) 0 var(--border-dark),
    var(--outline) calc(var(--outline) * -1) 0 var(--border-dark),
    calc(var(--outline) * -1) calc(var(--outline) * -1) 0 var(--border-dark);
}
.map-label .top { display: flex; align-items: center; gap: calc(4px * var(--k)); }
/* SVG ignores text-shadow, so the icon gets the same outline via drop-shadow
   or it would vanish into a bright echo. */
.map-label .retro-icon {
  filter:
    drop-shadow(1px 0 0 #050826) drop-shadow(-1px 0 0 #050826)
    drop-shadow(0 1px 0 #050826) drop-shadow(0 -1px 0 #050826);
}
.map-label .temp { font-size: calc(23px * var(--k)); color: var(--white); line-height: 1.15; }
.map-label.center .temp { color: var(--yellow); }
/* Cyan rather than yellow: yellow is a band in the radar ladder and would
   camouflage against moderate returns. Cyan appears nowhere in it. */
.map-label .name { font-size: calc(14px * var(--k)); color: var(--cyan); line-height: 1.15; }
.map-label.center .name { color: var(--yellow); }

/* Radar legend + timestamp: hidden until frames actually arrive, so a map
   with no radar coverage stays exactly as it was. */
.radar-chrome {
  position: absolute;
  inset: auto calc(6px * var(--k)) calc(5px * var(--k));
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: calc(10px * var(--k));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease-in;
}
.radar-chrome.on { opacity: 1; }

.map-legend, .map-credit {
  display: flex;
  align-items: center;
  gap: calc(6px * var(--k));
  padding: calc(2px * var(--k)) calc(7px * var(--k));
  background: rgba(3, 5, 56, 0.8);
  border: 1px solid rgba(140, 158, 242, 0.6);
  border-radius: 4px;
  box-shadow: 1.5px 1.5px 0 rgba(0, 0, 0, 0.6);
}
.map-legend .cap { font-size: calc(11px * var(--k)); color: var(--cyan); }
.map-legend .ramp { display: flex; }
.map-legend i { width: calc(13px * var(--k)); height: calc(8px * var(--k)); }
.map-credit { align-items: baseline; }
.map-credit .stamp { font-size: calc(15px * var(--k)); color: var(--yellow); }
.map-credit .src { font-size: calc(11px * var(--k)); color: rgba(255, 255, 255, 0.65); }

/* ---------- Local forecast ---------- */

.local-wrap {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(14px * var(--k));
  padding: calc(8px * var(--k));
}
.local-seg {
  flex: 1 1 auto;
  min-height: 0;
  max-height: calc(150px * var(--k));
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-shadow: var(--shadow-soft);
}
.local-seg .head {
  font-size: calc(26px * var(--k));
  color: var(--yellow);
  margin-bottom: calc(6px * var(--k));
}
.local-seg .body {
  font-size: calc(23px * var(--k));
  color: var(--white);
  line-height: 1.3;
}

/* ---------- Extended forecast ---------- */

.ext-wrap { display: flex; height: 100%; gap: calc(12px * var(--k)); align-items: center; }
.ext-wrap.rows { flex-direction: column; align-items: stretch; justify-content: center; }

.ext-card {
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  /* Four columns across a 16:9 picture would otherwise be skyscrapers with
     the day, icon, and temps floating an inch apart. Cap the card shape and
     center the stack of cards instead. */
  aspect-ratio: 3 / 4;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
  gap: calc(10px * var(--k));
  padding: calc(10px * var(--k));
  background: rgba(3, 5, 56, 0.5);
  border: 1.5px solid rgba(140, 158, 242, 0.7);
  border-radius: 8px;
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.5);
  text-align: center;
}
.ext-card .day { font-size: calc(30px * var(--k)); color: var(--yellow); text-shadow: var(--shadow-soft); }
.ext-card .cond {
  font-size: calc(19px * var(--k));
  color: var(--white);
  text-shadow: var(--shadow-soft);
}
.ext-card .temps { width: 100%; max-width: calc(190px * var(--k)); }
.ext-card .temps .pair { display: flex; justify-content: space-between; align-items: baseline; }
.ext-card .tag { font-size: calc(17px * var(--k)); color: var(--cyan); }
.ext-card .tag.hi { color: var(--yellow); }
.ext-card .val { font-size: calc(30px * var(--k)); color: var(--white); text-shadow: var(--shadow-soft); }

/* Row layout (portrait): one full-width row per day. */
.ext-row {
  flex: 1 1 auto;
  min-height: 0;
  max-height: calc(122px * var(--k));
  display: flex;
  align-items: center;
  gap: calc(14px * var(--k));
  padding: calc(10px * var(--k)) calc(14px * var(--k));
  background: rgba(3, 5, 56, 0.5);
  border: 1.5px solid rgba(140, 158, 242, 0.7);
  border-radius: 8px;
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.5);
}
.ext-row .left { flex: none; width: calc(86px * var(--k)); display: flex; flex-direction: column; align-items: center; gap: calc(5px * var(--k)); }
.ext-row .day { font-size: calc(25px * var(--k)); color: var(--yellow); text-shadow: var(--shadow-soft); }
.ext-row .cond { flex: 1 1 auto; min-width: 0; font-size: calc(17px * var(--k)); color: var(--white); text-shadow: var(--shadow-soft); }
.ext-row .temps { flex: none; text-align: right; }
.ext-row .temps .pair { display: flex; justify-content: flex-end; align-items: baseline; gap: calc(8px * var(--k)); }
.ext-row .tag { font-size: calc(16px * var(--k)); color: var(--cyan); }
.ext-row .tag.hi { color: var(--yellow); }
.ext-row .val { font-size: calc(25px * var(--k)); color: var(--white); text-shadow: var(--shadow-soft); }

/* ---------- Hazards ---------- */

.haz-wrap { display: flex; flex-direction: column; height: 100%; }
.haz-lead {
  flex: 1.6 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(8px * var(--k));
}
.haz-sevrow { display: flex; align-items: center; gap: calc(10px * var(--k)); }
.haz-chip {
  font-size: calc(18px * var(--k));
  color: var(--border-dark);
  padding: calc(2px * var(--k)) calc(10px * var(--k));
}
.haz-timing { font-size: calc(17px * var(--k)); color: var(--cyan); }
.haz-headline {
  font-size: calc(38px * var(--k));
  color: var(--white);
  line-height: 1.05;
  text-shadow: var(--shadow-hard);
}
.haz-detail {
  font-size: calc(22px * var(--k));
  color: var(--yellow);
  text-shadow: var(--shadow-soft);
}
.haz-source { display: flex; gap: calc(8px * var(--k)); align-items: baseline; }
.haz-source .origin { font-size: calc(14px * var(--k)); color: rgba(255, 255, 255, 0.75); }
.haz-source a { font-size: calc(14px * var(--k)); color: var(--cyan); }

.haz-row {
  flex: 1 1 auto;
  min-height: 0;
  max-height: calc(78px * var(--k));
  display: flex;
  align-items: center;
  gap: calc(10px * var(--k));
  padding: calc(8px * var(--k)) 0;
  text-shadow: var(--shadow-soft);
}
.haz-row .sevbar { flex: none; width: calc(5px * var(--k)); align-self: stretch; }
.haz-row .mid { flex: 1 1 auto; min-width: 0; }
.haz-row .mid .h { display: block; font-size: calc(19px * var(--k)); color: var(--white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.haz-row .mid .d { display: block; font-size: calc(14px * var(--k)); color: rgba(255, 255, 255, 0.75); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.haz-row .when { flex: none; font-size: calc(14px * var(--k)); color: var(--cyan); }

.sim-badge {
  align-self: flex-start;
  font-size: calc(13px * var(--k));
  color: var(--border-dark);
  background: var(--cyan);
  padding: 1px 6px;
  margin-bottom: calc(8px * var(--k));
}

/* ---------- Stand by / signal lost ---------- */

.signal-wrap {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(18px * var(--k));
  text-align: center;
}
.signal-head { font-size: calc(40px * var(--k)); color: var(--yellow); text-shadow: var(--shadow-hard); }
.signal-detail { font-size: calc(20px * var(--k)); color: var(--white); line-height: 1.4; }

/* Sign-on byline under the welcome card. Uppercased here rather than in the
   copy, so links.js can hold the publisher name in readable proper case for
   the settings fine print. */
.signal-byline {
  text-transform: uppercase;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: calc(8px * var(--k));
  margin-top: calc(6px * var(--k));
  font-size: calc(15px * var(--k));
  color: rgba(255, 255, 255, 0.7);
}
.signal-byline a { color: var(--cyan); text-decoration: none; }
.signal-byline a:hover { text-decoration: underline; }
.signal-byline .sep { color: rgba(255, 255, 255, 0.35); }
.blink { animation: blink 1.6s step-end infinite; }
@keyframes blink { 50% { opacity: 0.25; } }

/* ---------- Bottom bar: chips, ticker, attribution ---------- */

#retro-bottom {
  flex: none;
  display: flex;
  align-items: center;
  height: calc(44px * var(--k));
  padding-bottom: env(safe-area-inset-bottom);
  background: linear-gradient(to bottom, var(--panel-top), var(--blue-bottom));
  border-top: 2px solid var(--border-light);
}

.chip-row { display: flex; gap: calc(6px * var(--k)); padding: 0 calc(8px * var(--k)); }
.retro-chip {
  width: calc(30px * var(--k));
  height: calc(26px * var(--k));
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(to bottom, var(--panel-top), var(--panel-bottom));
  border: 1.5px solid rgba(140, 158, 242, 0.5);
  border-radius: 5px;
  box-shadow: 1.5px 1.5px 0 rgba(0, 0, 0, 0.5);
  color: rgba(255, 255, 255, 0.35);
  cursor: pointer;
}
.retro-chip.on { color: var(--yellow); border-color: var(--yellow); }
.retro-chip.pulse { animation: chip-pulse 1.2s ease-in-out infinite; }
@keyframes chip-pulse { 50% { box-shadow: 0 0 8px 2px rgba(255, 209, 26, 0.7); } }

.bar-divider { width: 1.5px; height: calc(24px * var(--k)); background: rgba(140, 158, 242, 0.5); flex: none; }

#ticker {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  height: 100%;
  display: flex;
  align-items: center;
}
#ticker-inner {
  display: flex;
  flex: none;              /* never let the bar squeeze the tiled strip */
  gap: 80px;               /* ticker.js owns this: it must match the measured cycle */
  white-space: nowrap;
  will-change: transform;
  font-size: calc(21px * var(--k));
  color: var(--white);
  text-shadow: var(--shadow-soft);
}

#attribution {
  flex: none;
  padding: 0 calc(10px * var(--k));
  font-size: calc(14px * var(--k));
  color: var(--cyan);
  text-decoration: none;
}
#attribution:hover { text-decoration: underline; }

/* ---------- CRT overlays ---------- */

#scanlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.18) 0px,
    rgba(0, 0, 0, 0.18) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
  z-index: 40;
}
#scanlines.off { display: none; }

#vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 60%, rgba(0, 0, 0, 0.35) 100%);
  z-index: 41;
}

/* ---------- Retro icons ---------- */

.retro-icon { display: inline-block; image-rendering: pixelated; flex: none; }
.retro-icon svg { display: block; width: 100%; height: 100%; shape-rendering: crispEdges; }

/* ---------- Settings overlay ---------- */

#settings-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
#settings-overlay[hidden] { display: none; }

/* Settings is the one screen a viewer READS rather than watches, and it was
   also the only part of the UI whose type never scaled with --k — 13px fine
   print next to 30px broadcast graphics on a 1440p display. Everything here
   scales with the picture, sits at higher contrast, and renders antialiased
   instead of with the CRT's deliberately crunchy rasterization. */
#settings-panel {
  width: min(calc(560px * var(--k)), 100%);
  max-height: 92%;
  overflow-y: auto;
  background: linear-gradient(to bottom, var(--panel-top), var(--panel-bottom));
  border: 2px solid var(--border-light);
  border-radius: var(--panel-radius);
  box-shadow: inset 0 0 0 2px rgba(5, 8, 38, 0.6), 4px 4px 0 rgba(0, 0, 0, 0.5);
  padding: calc(20px * var(--k));
  cursor: default;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.settings-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: calc(12px * var(--k));
  font-size: calc(24px * var(--k));
  color: var(--yellow);
  text-shadow: var(--shadow-soft);
  margin-bottom: calc(16px * var(--k));
}

.settings-section { margin-bottom: calc(20px * var(--k)); }
.settings-label {
  font-size: calc(18px * var(--k));
  color: var(--cyan);
  margin-bottom: calc(9px * var(--k));
  letter-spacing: 0.05em;
}
.settings-row { display: flex; gap: calc(10px * var(--k)); flex-wrap: wrap; align-items: center; }
.seg-row .seg.on { background: var(--yellow); color: var(--blue-bottom); border-color: var(--yellow); }

.retro-btn {
  font-family: var(--font-display);
  font-size: calc(19px * var(--k));
  color: var(--white);
  background: rgba(3, 5, 56, 0.6);
  border: 1.5px solid var(--border-light);
  border-radius: 5px;
  padding: calc(7px * var(--k)) calc(13px * var(--k));
  cursor: pointer;
  box-shadow: 1.5px 1.5px 0 rgba(0, 0, 0, 0.5);
}
.retro-btn:hover { border-color: var(--yellow); }

#loc-search {
  flex: 1 1 200px;
  font-family: var(--font-display);
  font-size: calc(20px * var(--k));
  color: var(--white);
  background: rgba(3, 5, 56, 0.8);
  border: 1.5px solid var(--border-light);
  border-radius: 5px;
  padding: calc(7px * var(--k)) calc(11px * var(--k));
  outline: none;
}
#loc-search:focus { border-color: var(--yellow); }

.loc-results { display: flex; flex-direction: column; gap: calc(4px * var(--k)); margin-top: calc(9px * var(--k)); }
.loc-results button {
  font-family: var(--font-display);
  font-size: calc(19px * var(--k));
  text-align: left;
  color: var(--white);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: calc(6px * var(--k)) calc(9px * var(--k));
  cursor: pointer;
}
.loc-results button:hover { border-color: var(--yellow); color: var(--yellow); }

.slide-toggles { display: flex; flex-wrap: wrap; gap: calc(9px * var(--k)) calc(18px * var(--k)); margin-top: calc(11px * var(--k)); }
.toggle-row { display: flex; align-items: center; gap: calc(8px * var(--k)); font-size: calc(19px * var(--k)); cursor: pointer; }
.toggle-row input { accent-color: var(--yellow); width: calc(17px * var(--k)); height: calc(17px * var(--k)); }

.vol-row { display: flex; align-items: center; gap: calc(10px * var(--k)); font-size: calc(19px * var(--k)); flex: 1; }
.vol-row input { flex: 1; accent-color: var(--yellow); }

.now-playing { font-size: calc(17px * var(--k)); color: var(--cyan); margin-top: calc(9px * var(--k)); min-height: calc(20px * var(--k)); }

/* Fine print is the densest text in the product and the part people squint
   at, so it drops the pixel face for the reading font and sentence case. */
.fine-print { border-top: 1px solid rgba(140, 158, 242, 0.4); padding-top: calc(14px * var(--k)); }
.fine-print p {
  font-family: var(--font-ui);
  font-size: calc(13px * var(--k));
  color: rgba(255, 255, 255, 0.88);
  line-height: 1.65;
  margin-bottom: calc(10px * var(--k));
}
.fine-print a { color: var(--cyan); }
.fine-print .keys { font-family: var(--font-display); font-size: calc(17px * var(--k)); color: var(--cyan); }
.fine-print .credit {
  border-top: 1px solid rgba(140, 158, 242, 0.25);
  padding-top: calc(12px * var(--k));
  font-size: calc(13.5px * var(--k));
}
.fine-print .credit a { text-decoration: none; font-weight: 600; }
.fine-print .credit a:hover { text-decoration: underline; }
.doc-links { display: flex; flex-wrap: wrap; gap: calc(6px * var(--k)) calc(14px * var(--k)); }
#app-store-link[hidden] { display: none; }
#app-store-link { display: block; margin-top: calc(6px * var(--k)); color: var(--yellow); font-weight: 600; }

/* ---------- Documents (privacy, support) ----------

   These are real documents — an App Review reviewer and an actual customer
   have to read them end to end — so the retro treatment is the frame only:
   the sunburst header and panel chrome around body copy set in the reading
   font at a comfortable measure. The pixel face stays on headings. */

body.doc { overflow: auto; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
body.doc #app { height: auto; min-height: 100dvh; width: min(100vw, 1100px); box-shadow: none; }

.doc-main { flex: 1 1 auto; padding: clamp(16px, 3vw, 34px); }
.doc-body {
  max-width: 74ch;
  margin: 0 auto;
  padding: clamp(20px, 3vw, 38px);
  background: linear-gradient(to bottom, var(--panel-top), var(--panel-bottom));
  border: 2px solid var(--border-light);
  border-radius: var(--panel-radius);
  box-shadow: inset 0 0 0 2px rgba(5, 8, 38, 0.6);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.9);
}
.doc-body h1 {
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 400;
  color: var(--yellow);
  text-shadow: var(--shadow-soft);
  line-height: 1.1;
  margin-bottom: 6px;
}
.doc-body h2 {
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 400;
  color: var(--cyan);
  margin: 34px 0 10px;
  line-height: 1.2;
}
.doc-body h3 {
  font-size: 16px;
  font-weight: 700;
  color: var(--white);
  margin: 22px 0 6px;
}
.doc-body p, .doc-body li { margin-bottom: 12px; }
.doc-body ul { padding-left: 22px; }
.doc-body li { padding-left: 4px; }
.doc-body li::marker { color: var(--yellow); }
.doc-body a { color: var(--cyan); }
.doc-body strong { color: var(--white); }
.doc-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
  background: rgba(3, 5, 56, 0.6);
  padding: 1px 5px;
  border-radius: 3px;
}
.doc-updated { font-size: 14px; color: rgba(255, 255, 255, 0.6); margin-bottom: 26px; }
.doc-lede {
  border-left: 3px solid var(--yellow);
  padding: 2px 0 2px 16px;
  margin: 22px 0 26px;
  color: var(--white);
}
.doc-callout {
  border: 1.5px solid rgba(140, 158, 242, 0.7);
  border-radius: 8px;
  background: rgba(3, 5, 56, 0.45);
  padding: 16px 18px;
  margin: 24px 0;
}
.doc-callout p:last-child, .doc-body p:last-child { margin-bottom: 0; }
.doc-contact {
  font-family: var(--font-display);
  font-size: 26px;
  color: var(--yellow);
  word-break: break-all;
}
.doc-contact a { color: var(--yellow); text-decoration: none; }
.doc-contact a:hover { text-decoration: underline; }
.doc-nav { display: flex; flex-wrap: wrap; gap: 10px 20px; margin-top: 34px; padding-top: 18px; border-top: 1px solid rgba(140, 158, 242, 0.4); font-size: 15px; }
.doc-back {
  font-family: var(--font-display);
  font-size: 19px;
  color: var(--white);
  background: rgba(3, 5, 56, 0.6);
  border: 1.5px solid var(--border-light);
  border-radius: 5px;
  padding: 5px 12px;
  text-decoration: none;
  white-space: nowrap;
}
.doc-back:hover { border-color: var(--yellow); color: var(--yellow); }

@media (max-width: 520px) {
  .doc-body { font-size: 15px; padding: 18px; }
  .doc-body h1 { font-size: 30px; }
  .doc-body h2 { font-size: 22px; }
  .doc-contact { font-size: 20px; }
}

/* ---------- Compact height (landscape phones) ---------- */

@media (max-height: 480px) {
  .hourly-strip, .cc-today, .hourly-rule, .today-rule { display: none; }
  /* No room to spend on a restated title when the rows themselves are down to
     a few pixels each; the header bar still names the slide. */
  .obs-title { display: none; }
  .cc-temp { font-size: 60px; }
  .cc-cond { font-size: 22px; }
  .stat-row { font-size: 16px; }
  #slide-title { font-size: 20px; }
  #location-name { font-size: 15px; }
  #clock-time { font-size: 18px; }
  #clock-date { font-size: 13px; }
  .haz-headline { font-size: 26px; }
  .local-seg .head { font-size: 20px; }
  .local-seg .body { font-size: 17px; }
  .signal-head { font-size: 30px; }
}

/* ---------- Narrow AND short ----------

   Stacked, the current-conditions panel has a hard floor: the stat list runs
   to seven rows in hot or cold weather, and no amount of tightening fits that
   plus the hourly strip plus the today line into an iPhone-SE-sized viewport.
   Something has to go, and the choice is made here rather than left to flex —
   which would otherwise shrink a band below its content and paint the overflow
   over its neighbour, or clip the bottom band away without saying so.

   Dropped in order of what is least missed: the today line first (the extended
   forecast slide carries the same LO/HI), then the hourly strip. The stat list
   is the readout the slide exists for and is never dropped. */

@media (max-width: 520px) and (max-height: 640px) {
  .cc-today, .today-rule { display: none; }
}
@media (max-width: 520px) and (max-height: 620px) {
  .hourly-strip, .hourly-rule { display: none; }
}

/* ---------- Narrow phones ---------- */

@media (max-width: 520px) {
  /* Stacked, the icon block and the stat list share one vertical budget
     instead of sitting side by side, so both tighten up. current.js drops the
     hero icon to 64px for the same reason. The stat list can run to eight rows
     (a HEAT INDEX or WIND CHILL row appears in hot or cold weather), and the
     gap is what has to give for the eighth to fit. */
  .cc-headline { flex-direction: column; align-items: stretch; justify-content: center; gap: 8px; }
  .cc-main { gap: 6px; }
  .cc-stats { gap: 6px; }
  .cc-temp { font-size: 64px; }
  .cc-cond { font-size: 24px; }
  .stat-row { font-size: 17px; }
  .hourly-cells .hour-cell:nth-child(n+6) { display: none; }
  .obs-head, .obs-row { grid-template-columns: 1fr 52px 92px 78px; }
  /* Rows take more of the leftover height than they used to. Capped low, six
     of them left a dead band at each end of a tall phone panel; the title now
     fills the top, and a taller cap spends most of what is left on row
     spacing instead of pooling it above and below the table. */
  .obs-row { font-size: 17px; max-height: 72px; }
  .obs-head { font-size: 14px; }
  .obs-title { font-size: 22px; }
  .local-seg { max-height: 118px; }
  #slide-title { font-size: 19px; }
  #location-name { font-size: 14px; }
  #clock-time { font-size: 18px; }
  #clock-date { font-size: 12px; }
  .logo-retro { font-size: 14px; }
  .logo-forecast { font-size: 11px; }
  .ext-card .day, .ext-row .day { font-size: 20px; }
  .haz-headline { font-size: 28px; }
}

/* ---------- Narrow and very short ----------

   Last tier: with both extra bands already dropped, the headline itself is
   what no longer fits. Must come after the narrow-phone block above, which
   would otherwise win on source order and put the type back up. */

@media (max-width: 520px) and (max-height: 500px) {
  .cc-temp { font-size: 52px; }
  .cc-cond { font-size: 20px; }
  .cc-stats { gap: 4px; }
  .stat-row { font-size: 16px; }
}
