/* Paikalliskyylä – valvontahuone.
   Nothing here animates or transitions: a wall display must not flicker, and
   e-ink pays for every repaint. Colour only ever means status; everything
   else is ink on a dark plane. */

:root {
  --plane: #0a0c0d;
  --surface: #111415;
  --raise: #1a1f21;
  --line: #242b2d;
  --line-2: #3d474a;
  --ink: #e9edea;
  --ink-2: #b1b9b5;
  --ink-3: #828b87;
  --warn: #fab219;
  --crit: #d03b3b;
  --crit-ink: #f28b8b;
  --crit-wash: rgba(208, 59, 59, 0.13);
  --warn-wash: rgba(250, 178, 25, 0.07);
  --warn-ink: #fab219;       /* warning colour when it is text, not a mark */
  --radar-ground: #0b0e10;
  color-scheme: dark;
  --water: #3987e5;
  --snow: #d5e2f2;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;
  --gap: clamp(8px, 1vw, 14px);
  --pad: clamp(10px, 1.2vw, 18px);
}

/* Light: the same room with the blinds open. Chosen in the browser (top
   bar) and remembered there; dark stays the default. The status colours are
   darker steps of the same hues, so they still read on a pale surface. */
:root[data-theme="light"] {
  --plane: #e9ecea;
  --surface: #fbfcfb;
  --raise: #eef1ef;
  --line: #d5dbd8;
  --line-2: #a7b0ac;
  --ink: #111514;
  --ink-2: #3f4744;
  --ink-3: #636c68;
  --warn: #c98500;
  --warn-ink: #7d5200;
  --crit: #d03b3b;
  --crit-ink: #ad2424;
  --crit-wash: rgba(208, 59, 59, 0.09);
  --warn-wash: rgba(201, 133, 0, 0.11);
  --water: #2a78d6;
  --snow: #86a8d2;
  --radar-ground: #e3e8e5;
  color-scheme: light;
}

/* E-ink / monochrome: black on white, status carried by weight and shape. */
:root[data-theme="eink"] {
  --plane: #fff;
  --surface: #fff;
  --raise: #e4e4e4;
  --line: #9a9a9a;
  --line-2: #000;
  --ink: #000;
  --ink-2: #1c1c1c;
  --ink-3: #3a3a3a;
  --warn: #000;
  --warn-ink: #000;
  --radar-ground: #fff;
  color-scheme: light;
  --crit: #000;
  --crit-ink: #000;
  --crit-wash: transparent;
  --warn-wash: transparent;
  --water: #000;
  --snow: #777;
}

* { box-sizing: border-box; }

html {
  background: var(--plane);
  color: var(--ink);
  font: 400 16px/1.35 var(--sans);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body { margin: 0; }
h1, h2, p, ul, dl, dd, figure { margin: 0; padding: 0; }
ul { list-style: none; }

.room {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: var(--gap);
  padding-top: calc(var(--gap) - 3px);
  /* The whole room's state in one line, readable from the doorway. */
  border-top: 3px solid var(--line);
}
.room[data-level="warn"] { border-top-color: var(--warn); }
.room[data-level="crit"] { border-top-color: var(--crit); }

/* ── Top bar ─────────────────────────────────────────────────────────────── */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  font: 500 0.72rem/1 var(--mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.bar__name { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
.bar__eye { width: 1.5rem; height: 0.95rem; color: var(--ink-2); flex: none; }
.bar h1 { font: inherit; color: var(--ink); letter-spacing: 0.16em; }
.places { display: flex; gap: 2px; }
.places a { color: var(--ink-2); text-decoration: none; padding: 0.55em 0.9em; border: 1px solid var(--line); border-radius: 2px; }
.places a.is-current { color: var(--plane); background: var(--ink); border-color: var(--ink); font-weight: 700; }
.bar__demo { color: #14100a; background: #fab219; padding: 0.25em 0.5em; letter-spacing: 0.06em; }
.bar__side { display: flex; align-items: center; gap: 1rem; min-width: 0; }
.bar__link { color: var(--warn-ink); text-align: right; text-transform: none; letter-spacing: 0.02em; }
.theme { color: var(--ink-2); text-decoration: none; padding: 0.55em 0.9em; border: 1px solid var(--line); border-radius: 2px; white-space: nowrap; }
.theme:hover, .theme:focus-visible { color: var(--ink); border-color: var(--line-2); }

/* ── Pulse ───────────────────────────────────────────────────────────────── */

/* The clock, then however many tiles this place has (a ski village gets
   one more than a seaside one). */
.pulse {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr);
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  gap: var(--gap);
}
.pulse__live { display: contents; }

.tile {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: 3px;
  padding: calc(var(--pad) * 0.8) var(--pad);
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.tile[data-level="warn"] { border-left-color: var(--warn); }
.tile[data-level="crit"] { border-left-color: var(--crit); background: linear-gradient(var(--crit-wash), var(--crit-wash)) var(--surface); }

.tile__label, .panel__head h2, .obs__tag, .watch dt {
  font: 500 0.7rem/1.2 var(--mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.tile__label { display: flex; justify-content: space-between; gap: 0.5rem; }
.tile__flag { display: inline-flex; align-items: center; gap: 0.35em; color: var(--ink); }
.tile__flag .glyph { width: 1em; height: 1em; }

.tile__value, .clock {
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  margin: 0.3em 0 0.22em;
  white-space: nowrap;
}
.tile__value { font-size: clamp(1.9rem, 3.4vw, 3.8rem); }
.clock { font-size: clamp(3rem, 5.4vw, 5.8rem); font-variant-numeric: tabular-nums; margin: 0.14em 0 0.1em; }
.tile__unit {
  font: 500 0.26em/1 var(--mono);
  letter-spacing: 0.04em;
  color: var(--ink-3);
  margin-left: 0.45em;
}
.tile__sub { color: var(--ink-2); font-size: clamp(0.82rem, 1.15vw, 1.05rem); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile--clock .tile__sub { font-size: clamp(0.95rem, 1.5vw, 1.35rem); }
/* Sources may take a second line when the tiles are many and narrow; the
   figures above them never wrap. */
.tile__src { margin-top: auto; padding-top: 0.35em; color: var(--ink-3); font: 0.68rem/1.3 var(--mono); overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; min-height: 1.3em; }
.tile { overflow: hidden; }
.tile--wrap .tile__sub { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }
.is-old .tile__value, .is-old dd { color: var(--ink-3); }

.arrow { width: 1em; height: 1em; display: inline-block; vertical-align: -0.12em; }
.arrow--big { width: 0.55em; height: 0.55em; margin-left: 0.3em; color: var(--ink-2); vertical-align: 0.05em; }

/* ── Panels ──────────────────────────────────────────────────────────────── */

.grid {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(14rem, 2.7fr) minmax(0, 5.6fr) minmax(14rem, 3fr);
  gap: var(--gap);
}
.side { display: grid; grid-template-rows: minmax(11rem, 1fr) auto; gap: var(--gap); min-width: 0; }
.charts { display: grid; grid-template-rows: minmax(10rem, 1fr) auto; gap: var(--gap); min-width: 0; }
/* A place without an event calendar has no village panel at all. */
.panel:empty { display: none; }
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: calc(var(--pad) * 0.8) var(--pad);
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.panel__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.55rem;
}
.panel__head h2 { flex: none; }
.panel__note { color: var(--ink-2); font-size: 0.8rem; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.empty { color: var(--ink-2); margin: auto 0; }

/* ── Havaintoloki ────────────────────────────────────────────────────────── */

/* Calm: one slim line. Trouble: the box grows a card per observation. */
.obs-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(max(17rem, 21vw), 1fr)); gap: var(--gap); }
.obs { display: flex; gap: 0.7rem; padding: 0.55rem 0.75rem 0.6rem; background: var(--raise); border-radius: 2px; box-shadow: inset 3px 0 0 var(--line-2); }
.obs--crit { background: linear-gradient(var(--crit-wash), var(--crit-wash)) var(--raise); box-shadow: inset 3px 0 0 var(--crit); }
.obs--warn { background: linear-gradient(var(--warn-wash), var(--warn-wash)) var(--raise); box-shadow: inset 3px 0 0 var(--warn); }
.obs__mark { flex: none; padding-top: 0.1rem; }
.glyph { width: 1.25rem; height: 1.25rem; display: block; }
.glyph--crit { color: var(--crit); }
.glyph--warn { color: var(--warn); }
.glyph--info { color: var(--ink-3); }
.glyph__cut { stroke: var(--raise); fill: none; }
.glyph__dot { fill: var(--raise); }
.obs__body { min-width: 0; }
.obs__head { display: flex; gap: 0.6rem; align-items: baseline; }
.obs__tag { color: var(--ink-2); }
.obs__level { font: 500 0.7rem/1.2 var(--mono); letter-spacing: 0.06em; color: var(--ink-3); }
.obs--crit .obs__level { color: var(--crit-ink); }
.obs--warn .obs__level { color: var(--warn-ink); }
/* A card is a headline: three lines at most, the rest is cut. The log must
   never grow so tall that it pushes the room off the screen. */
.obs__text { font-size: clamp(1rem, 1.3vw, 1.3rem); font-weight: 600; line-height: 1.25; margin-top: 0.15rem; text-wrap: pretty; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.obs--info .obs__text { font-weight: 500; }
.obs__detail { color: var(--ink-2); font-size: 0.8rem; margin-top: 0.2rem; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }

/* With observations on show, the background rows sit beside the cards
   rather than under them: a wall display has width to spare, not height. */
.log__body { display: flex; gap: var(--gap) clamp(1rem, 2vw, 2rem); align-items: flex-start; }
.log__body .obs-list { flex: 1 1 0; min-width: 0; }
.log__body .log__row { flex: 0 1 34%; flex-direction: column; align-items: stretch; gap: 0.4rem; }
.log__body .watch { margin-left: 0; }
.log__body .watch dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log__row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.4rem 2rem; }
.log__row--calm { margin-top: 0; }
.log__lead h2 { font: 500 0.7rem/1.2 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); }
.calm { font-size: clamp(1.15rem, 1.9vw, 1.9rem); font-weight: 500; line-height: 1.2; color: var(--ink); margin-top: 0.2rem; }
.past { color: var(--ink-3); font-size: 0.8rem; }
.past time { font-family: var(--mono); margin-right: 0.6em; }
.watch { display: grid; gap: 0.2rem; margin-left: auto; }
.watch div { display: flex; gap: 0.75rem; align-items: baseline; }
.watch dt { flex: none; width: 4rem; }
.watch dd { font-size: 0.82rem; color: var(--ink-2); }

/* ── Kelikamera ──────────────────────────────────────────────────────────── */

/* Wide layout: the stills share whatever height the charts leave, so the
   whole room fits one screen. The frame has no height of its own (the image
   is taken out of flow), so it can never push the page past the fold. */
.cams { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: var(--gap); }
.cam { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.cam__frame { position: relative; flex: 1; min-height: 7.5rem; background: #000; border-radius: 2px; overflow: hidden; }
.cam img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.cam figcaption { display: flex; justify-content: space-between; gap: 0.75rem; margin-top: 0.35rem; font: 0.7rem/1.2 var(--mono); color: var(--ink-2); }
.cam time { color: var(--ink-3); white-space: nowrap; }
.cam.is-old time { color: var(--warn-ink); }
:root[data-theme="eink"] .cam img, :root[data-theme="eink"] .zoom img { filter: grayscale(1) contrast(1.15); }

.zoom { position: fixed; inset: 0; z-index: 10; background: rgba(0, 0, 0, 0.92); display: flex; align-items: center; justify-content: center; cursor: zoom-out; }
.zoom[hidden] { display: none; }
.zoom img { max-width: 100%; max-height: 100%; }

/* ── Kylällä tapahtuu ────────────────────────────────────────────────────── */

.panel--village .panel__head, .panel--news .panel__head { margin-bottom: 0.4rem; }
.panel--village .empty, .panel--news .empty { margin: 0; font-size: 0.9rem; }
/* In the narrow side column a bulletin gets two lines instead of one. */
.happen--news .happen__what { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }
.happen--news li { padding: 0.2rem 0; }

/* ── Sadetutka ───────────────────────────────────────────────────────────── */

.radar { position: relative; flex: 1; min-height: 9rem; }
.radar svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.radar__ground { fill: var(--radar-ground); }
.radar__ring { fill: none; stroke: var(--ink-3); stroke-width: 0.5; vector-effect: non-scaling-stroke; opacity: 0.8; }
.radar__home { fill: none; stroke: var(--ink); stroke-width: 1.4; stroke-linecap: round; }
.radar__km, .radar__north { fill: var(--ink-3); font: 6.5px var(--mono); }
.radar__north { text-anchor: middle; fill: var(--ink-2); }
.radar__time { fill: var(--ink-2); font: 7.5px var(--mono); }
.radar__motion path { fill: none; stroke: var(--ink); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.radar__motion text { fill: var(--ink-2); font: 6.5px var(--mono); text-anchor: middle; }
:root[data-theme="eink"] .radar image { filter: grayscale(1) contrast(1.6); }
:root[data-theme="eink"] .radar__ring { stroke: #000; }
.happen li { display: flex; gap: 0.8rem; align-items: baseline; padding: 0.1rem 0; font-size: 0.86rem; line-height: 1.3; }
.happen time { flex: none; width: 10em; font: 0.72rem/1.3 var(--mono); color: var(--ink-3); white-space: nowrap; }
.happen--news time { width: 3.4em; }
.happen__what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-2); }
.happen__where { color: var(--ink-3); }
.happen a { color: inherit; text-decoration: none; }
.happen a:hover, .happen a:focus-visible { text-decoration: underline; }
.happen .is-today time, .happen .is-today .happen__what { color: var(--ink); font-weight: 600; }

/* ── Charts ──────────────────────────────────────────────────────────────── */

.chart { --axis: 2.9rem; position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.row { display: grid; grid-template-columns: var(--axis) minmax(0, 1fr); }
.plot { position: relative; min-width: 0; }
.cols { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); }
.cols--fill { position: absolute; inset: 0; }
.col { position: relative; min-width: 0; }

.axis { position: relative; font: 0.66rem/1 var(--mono); color: var(--ink-3); }
.axis span { position: absolute; right: 0.5rem; transform: translateY(-50%); white-space: nowrap; }
.axis--name span { top: 50%; left: 0; right: auto; line-height: 1.15; letter-spacing: 0.02em; }
.tick { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line); }
.tick--zero { border-top-color: var(--line-2); }
.daymark { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--line-2); }

.row--temp { flex: 1 1 3.5rem; min-height: 3.5rem; }
.frost { position: absolute; left: 0; right: 0; bottom: 0; background: rgba(57, 135, 229, 0.09); }
.line { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.line polyline { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.mark { position: absolute; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%; background: var(--ink); box-shadow: 0 0 0 2px var(--surface); }
.mark b { position: absolute; left: 50%; transform: translateX(-50%); font: 600 0.78rem/1 var(--sans); white-space: nowrap; }
.mark--above b { bottom: 13px; }
.mark--below b { top: 13px; }
.mark--start b { left: -2px; transform: none; }
.mark--end b { left: auto; right: -2px; transform: none; }

.row--rain { height: 2rem; margin-top: 0.4rem; }
.row--rain .col { display: flex; align-items: flex-end; padding: 0 1px; border-bottom: 1px solid var(--line-2); }
.drop { position: relative; display: block; width: 100%; min-height: 2px; background: var(--water); border-radius: 2px 2px 0 0; }
.drop b { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 3px; font: 600 0.72rem/1 var(--sans); color: var(--ink); white-space: nowrap; }
.drop--snow { background: var(--snow); }
.drop--sleet { background: linear-gradient(var(--snow) 50%, var(--water) 50%); }

.row--cloud { height: 0.45rem; margin-top: 0.45rem; }
.cloud { display: block; height: 100%; background: var(--ink); }

.row--wind { height: 1.1rem; margin-top: 0.4rem; }
.row--wind .col, .row--hours .col { display: flex; justify-content: center; }
.gust { display: flex; align-items: center; gap: 2px; color: var(--ink-2); white-space: nowrap; }
.gust .arrow { width: 0.85rem; height: 0.85rem; color: var(--ink-3); }
.gust b { font: 500 0.78rem/1 var(--sans); }
/* Forty-eight hours in a narrow column: every sixth hour is enough. */
@media (max-width: 1500px) { .gust.is-minor { display: none; } }
:root[data-tv] .gust.is-minor { display: none; }
.gust--warn b, .gust--crit b { font-weight: 700; color: var(--ink); padding: 1px 3px; margin: -1px -3px; border-radius: 2px; }
.gust--warn b { box-shadow: inset 0 -2px 0 var(--warn); }
.gust--crit b { background: var(--crit); color: #fff; }

.row--hours { height: 1.15rem; margin-top: 0.3rem; border-top: 1px solid var(--line-2); }
.row--hours .col span { font: 0.66rem/1 var(--mono); color: var(--ink-3); padding-top: 0.4rem; white-space: nowrap; }
.row--hours .col--newday span { color: var(--ink); }
.row--hours .axis { border-top: 1px solid var(--surface); margin-top: -1px; }
.row--hours .axis span { top: 0.4rem; transform: none; }

.drop b.is-start { left: 0; transform: none; }
.drop b.is-end { left: auto; right: 0; transform: none; }
.col--newday::before { content: ""; position: absolute; left: -1px; top: 0; bottom: 0; border-left: 1px solid var(--line-2); }

.hit { position: absolute; top: 0; bottom: 0; left: var(--axis); right: 0; cursor: crosshair; touch-action: pan-y; }
.cursor { position: absolute; top: 0; bottom: 1.2rem; width: 1px; margin-left: -0.5px; background: var(--ink-2); display: none; pointer-events: none; }
.readout.is-live { color: var(--ink); }

/* ── Footer ──────────────────────────────────────────────────────────────── */

.foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.3rem 1.5rem; font: 0.66rem/1.4 var(--mono); color: var(--ink-3); }
.foot__sources { display: flex; flex-wrap: wrap; gap: 0.2rem 1.1rem; }
.foot__sources .is-bad { color: var(--warn-ink); }
.foot__credit { text-align: right; }

/* ── E-ink specifics ─────────────────────────────────────────────────────── */

:root[data-theme="eink"] .obs { background: #fff; box-shadow: none; outline: 1px solid #000; outline-offset: -1px; }
:root[data-theme="eink"] .obs--crit { outline: 4px solid #000; outline-offset: -4px; }
:root[data-theme="eink"] .obs--warn { box-shadow: inset 7px 0 0 #000; }
:root[data-theme="eink"] .glyph__cut { stroke: #fff; }
:root[data-theme="eink"] .glyph__dot { fill: #fff; }
:root[data-theme="eink"] .obs--warn .obs__mark, :root[data-theme="eink"] .obs--crit .obs__mark { padding-left: 0.35rem; }
:root[data-theme="eink"] .tile[data-level] { border-left-width: 8px; }
:root[data-theme="eink"] .tile:not([data-level="warn"]):not([data-level="crit"]) { border-left-width: 1px; }
:root[data-theme="eink"] .frost { background: none; border-top: 2px solid #000; }
:root[data-theme="eink"] .gust--crit b { background: #000; }
:root[data-theme="eink"] .gust--warn b { box-shadow: inset 0 -3px 0 #000; }
:root[data-theme="eink"] .drop--snow { background: #fff; outline: 1px solid #000; outline-offset: -1px; }
:root[data-theme="eink"] .bar__demo { color: #fff; }
:root[data-theme="eink"] .room { border-top-color: transparent; }
:root[data-theme="eink"] .room[data-level="warn"], :root[data-theme="eink"] .room[data-level="crit"] { border-top-color: #000; }

/* ── Holiday photos in the rotation ──────────────────────────────────────── */

.photo { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 20; background: #000; }
.photo[hidden] { display: none; }
.photo img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: contain; }
.photo__clock { position: absolute; right: 2.2rem; bottom: 1.6rem; font: 600 2rem/1 var(--sans); color: #fff; opacity: 0.75; font-variant-numeric: tabular-nums; text-shadow: 0 0 6px #000, 0 0 2px #000; }

/* ── TV (?tv=1): the screensaver. One fixed screen, nothing to operate ──── */

:root[data-tv] { cursor: none; }
:root[data-tv] body { overflow: hidden; background: #000; }
/* The script sets the exact height; the margins are for overscan and for the
   few pixels the picture wanders to spare the panel. */
:root[data-tv] .room { height: 100vh; min-height: 0; padding: 14px 30px 16px; overflow: hidden; }
:root[data-tv] .foot, :root[data-tv] .hit, :root[data-tv] .bar__link:empty, :root[data-tv] .theme { display: none; }
/* Whatever the log says, the room is exactly one screen: the lower panels
   take what is left and shrink rather than fall off the bottom edge. */
:root[data-tv] .grid { flex: 1 1 0; min-height: 0; }
:root[data-tv] .panel { overflow: hidden; }
:root[data-tv] .obs__detail { -webkit-line-clamp: 1; line-clamp: 1; }
:root[data-tv] .radar, :root[data-tv] .row--temp { min-height: 0; }
:root[data-tv] .cam img { cursor: default; }
:root[data-tv] .obs:nth-child(n+4) { display: none; }
:root[data-tv] .places a { pointer-events: none; }
/* Fixed sizes instead of the viewport-relative ones: the layout is always
   1280 px wide here, whatever the browser claims the viewport to be, and the
   compact rules for short screens always apply. */
:root[data-tv] { --gap: 12px; --pad: 15px; }
:root[data-tv] .clock { font-size: 4.2rem; }
:root[data-tv] .tile { padding-top: 0.55rem; padding-bottom: 0.5rem; }
:root[data-tv] .tile__value { font-size: 2.7rem; }
:root[data-tv] .tile__sub { font-size: 0.95rem; }
:root[data-tv] .tile__src, :root[data-tv] .tile--wrap .tile__sub { -webkit-line-clamp: 1; line-clamp: 1; }
:root[data-tv] .tile--clock .tile__sub { font-size: 1.2rem; }
:root[data-tv] .calm { font-size: 1.5rem; }
:root[data-tv] .obs__text { font-size: 1rem; }
/* Up to three cards, always on one row. */
:root[data-tv] .obs-list { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
:root[data-tv] .log__body .log__row { flex-basis: 30%; }
:root[data-tv] .log__body { gap: 12px 1.5rem; }
:root[data-tv] .charts { grid-template-rows: minmax(0, 1fr) auto; }
:root[data-tv] .cam__frame { min-height: 0; }
:root[data-tv] .side { grid-template-rows: minmax(0, 1fr) auto; }
:root[data-tv] .happen li:nth-child(n+4), :root[data-tv] .happen--news li:nth-child(n+3) { display: none; }
/* Nobody can scroll a screensaver. Three cards wrap their text and grow
   tall, so the village panel gives up its place rather than be cut in half. */
:root[data-tv] .room:has(.obs:nth-child(3)) #village, :root[data-tv] .room:has(.obs:nth-child(3)) #news { display: none; }

/* ── Narrow screens: one column, same order ──────────────────────────────── */

@media (max-width: 900px) {
  .pulse { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-flow: row; }
  .pulse__live > .tile:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .side { grid-template-rows: none; }
  .radar { flex: none; aspect-ratio: 1; max-height: 28rem; }
  .tile--clock { grid-column: 1 / -1; flex-direction: row; align-items: baseline; gap: 1rem; flex-wrap: wrap; }
  .tile--clock .tile__label { display: none; }
  .grid { grid-template-columns: minmax(0, 1fr); }
  .charts { grid-template-rows: none; }
  .happen__what { white-space: normal; }
  .cams { flex-direction: row; }
  .cam__frame { flex: none; min-height: 0; aspect-ratio: 16 / 9; }
  .row--temp { min-height: 7rem; }
  .watch { margin-left: 0; }
  .log__body { flex-direction: column; align-items: stretch; }
  .log__body .log__row { flex: none; }
  .log__body .watch dd { white-space: normal; }
  .tile__sub { white-space: normal; }
  .foot__credit { text-align: left; }
}
@media (max-width: 520px) {
  .chart { --axis: 2.4rem; }
  .tile__value { font-size: 2.3rem; }
  .clock { font-size: 3.4rem; }
  .cams { flex-direction: column; }
  .panel__head { flex-direction: column; gap: 0.25rem; }
  .panel__note { text-align: left; white-space: normal; }
  .obs-list { grid-template-columns: minmax(0, 1fr); }
  .is-minor { display: none; }
}

/* Short landscape screens (a tablet with its system bar showing): tighten
   the pulse so the room still fits without scrolling. */
@media (min-width: 901px) and (max-height: 780px) {
  .clock { font-size: clamp(2.6rem, 5vw, 4.4rem); }
  .tile__value { font-size: clamp(1.8rem, 3.2vw, 3rem); }
  .tile { padding-top: 0.55rem; padding-bottom: 0.5rem; }
  .calm { font-size: clamp(1.1rem, 1.6vw, 1.5rem); }
  .charts { grid-template-rows: minmax(9rem, 1fr) auto; }
  .happen li:nth-child(n+4) { display: none; }
  .cam__frame { min-height: 6rem; }
}

@media print {
  .hit { display: none; }
}
