/* Zermatt Weather v2 — synchronized mobile meteogram */
:root {
  color-scheme: light;
  --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --canvas: #ffffff;
  --ink: #24313d;
  --secondary-ink: #52606d;
  --minor-grid: #e5e9ee;
  --major-grid: #cbd4dd;
  --zero-line: #98a4b0;
  --night: #e9edf2;
  /* Possible fog: a hue no series uses, drawn as a thin bar at the top. */
  --fog: #f0a3aa;
  --temperature: #c82025;
  /* Darker than the spec's #2466A6 so it holds on the blue temperature fill. */
  --dew: #123f7a;
  --gust: #a76600;
  --wind: #6c5131;
  --sun: #f0cf23;
  --sun-ink: #806000;
  --focus: #155eef;
  /* Cursors and new labels: neutral, never a data colour, ≥ 7:1 on white. */
  --now-ink: #1b232c;
  --now-muted: #8a96a3;
  --select-ink: #24313d;
  --tick-ink: #46525e;
  --aging: #704600;
  --old: #9b1c1c;
  /* Bottom pill: 56 px targets inside a 1 px hairline. */
  --pill-h: 58px;
  --missing: #eef0f2;
  /* Precipitation is the one bright element of its panel. */
  --precip: #1366ff;
  --showers: #12c2b4;
  /* Snow is white with a dark-blue edge so it reads as snow at a glance. */
  --snow: #ffffff;
  --snow-edge: #0b4fd6;
  --freezing: #e03131;
  /* Freezing level: teal, a hue no other series uses. */
  --freezing-level: #0e7490;
  /* Horizontal geometry: the header, panel titles and dock keep the side
     margin; the date band and plots run edge to edge (gutter and edge 0,
     axis labels inside the plots). */
  --side: 8px;
  --gutter: 0px;
  --edge: 0px;
  /* Panel titles, one step smaller on narrow phones. Chart labels stay at
     12 px, the minimum for analytical text. */
  --fs-title: 13px;
}
@media (min-width: 360px) {
  :root { --side: 12px; }
}
@media (max-width: 400px) {
  :root { --fs-title: 12px; }
}

* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
html, body { width: 100%; max-width: 100%; overflow-x: clip; }
body { font-family: var(--font-family); background: var(--canvas); color: var(--ink); line-height: 1.5; -webkit-font-smoothing: antialiased; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
button:disabled { cursor: default; opacity: .4; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

.status-banner { position: fixed; z-index: 20; top: max(8px, env(safe-area-inset-top)); left: 50%; width: min(calc(100% - 16px), 660px); min-height: 44px; transform: translateX(-50%); align-items: center; justify-content: space-between; gap: 8px; padding: 6px 10px; border-radius: 6px; box-shadow: 0 2px 8px rgba(36, 49, 61, .25); color: #fff; font-size: 13px; font-weight: 700; }
.offline-banner { background: #314e75; }
.update-banner { background: var(--focus); }
.banner-btn { min-width: 44px; min-height: 44px; padding: 4px 10px; border: 1px solid #fff; border-radius: 6px; background: #fff; color: var(--ink); font-weight: 700; }

.meteogram-app {
  width: 100%;
  max-width: 1040px;
  margin: 0 auto;
  padding: env(safe-area-inset-top) max(var(--side), env(safe-area-inset-right)) env(safe-area-inset-bottom) max(var(--side), env(safe-area-inset-left));
  background: var(--canvas);
}
.meteogram-app .forecast-content { min-width: 0; }

/*
 * Height contract. --H is the usable height (stable small viewport minus the
 * safe areas that overlap content), kept at 520 px or more; below that the
 * page scrolls. Every row is a continuous piecewise-linear function of --H
 * that reproduces the specified 520 / 600 / 700 budgets exactly:
 *   --a ramps 0→80 over 520–600, --b 0→100 over 600–700, --c grows above 700
 *   (50 / 20 / 10 / 10 / 10 % of extra height to temperature / wind / sun /
 *   freezing level / cloud). The freezing-level panel's 34 px and its 4 px
 *   gap come from temperature (6), wind (10), sun (6) and cloud (16).
 * The time-range control lives in the Info sheet, so its 44 px row and 4 px
 * gap from the specified budget go to the panels, as do the 36 px and 4 px
 * gap of the former readout strip (temperature 20, wind 12, cloud 8). The
 * bottom dock (the pill) is fixed; --pad-bottom reserves its height so it
 * never covers chart content. The temperature panel includes the 12 px hour-tick row.
 * Rows plus gaps plus outer padding still sum to --H.
 */
.meteogram {
  --usable: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  --H: clamp(520px, var(--usable), 1100px);
  --a: clamp(0px, var(--H) - 520px, 80px);
  --b: clamp(0px, var(--H) - 600px, 100px);
  --c: max(0px, var(--H) - 700px);
  --pad-top: calc(8px + var(--a) * .025);
  --dock-h: calc(var(--pill-h) + 8px);
  --pad-bottom: calc(var(--dock-h) + 4px + var(--a) * .025);
  --h-day-base: calc(18px + var(--a) * .025);
  --h-temp-base: calc(144px + var(--a) * .395 + var(--b) * .37 + var(--c) * .5);
  --h-wind: calc(74px + var(--a) * .27 + var(--b) * .23 + var(--c) * .2);
  --h-sun: calc(44px + var(--a) * .09 + var(--b) * .12 + var(--c) * .1);
  --h-freezing: calc(34px + var(--a) * .08 + var(--b) * .1 + var(--c) * .1);
  --h-cloud: calc(68px + var(--a) * .09 + var(--b) * .18 + var(--c) * .1);
  --h-day: var(--h-day-base);
  --h-temp: var(--h-temp-base);
  min-width: 0;
  display: grid;
  grid-template-rows: 36px var(--h-day) calc(var(--h-temp) + var(--h-wind) + var(--h-sun) + var(--h-freezing) + var(--h-cloud) + 16px);
  row-gap: 4px;
  padding: var(--pad-top) 0 var(--pad-bottom);
  background: var(--canvas);
}
/* Regions without mountains have no freezing-level panel: its 34 px and 4 px
   gap go to temperature (20), wind (14) and sun (4), its growth to
   temperature and wind. */
.meteogram.no-freezing {
  --h-temp-base: calc(164px + var(--a) * .435 + var(--b) * .42 + var(--c) * .55);
  --h-wind: calc(88px + var(--a) * .31 + var(--b) * .28 + var(--c) * .25);
  --h-sun: calc(48px + var(--a) * .09 + var(--b) * .12 + var(--c) * .1);
  grid-template-rows: 36px var(--h-day) calc(var(--h-temp) + var(--h-wind) + var(--h-sun) + var(--h-cloud) + 12px);
}
.meteogram.no-freezing .chart-stack { grid-template-rows: var(--h-temp) var(--h-wind) var(--h-sun) var(--h-cloud); }
@supports (height: 100svh) {
  .meteogram { --usable: calc(100svh - env(safe-area-inset-top) - env(safe-area-inset-bottom)); }
}
/* 24-hour detail: the date band becomes window navigation (44 px) and the
   temperature panel gives up the difference. */
.meteogram.is-detail { --h-day: 44px; --h-temp: calc(var(--h-temp-base) - 44px + var(--h-day-base)); }
.meteogram > * { min-width: 0; }

/* Measured overflow (enlarged or localized text) → natural vertical reflow. */
.meteogram.is-reflow { grid-template-rows: auto; }
.meteogram.is-reflow .mg-header { height: auto; min-height: 36px; }
.meteogram.is-reflow .mg-title, .meteogram.is-reflow .mg-meta { white-space: normal; }
.meteogram.is-reflow .day-band { min-height: var(--h-day); }
.meteogram.is-reflow .chart-stack { grid-template-rows: auto; }
.meteogram.is-reflow .panel-temp { min-height: var(--h-temp); grid-template-rows: auto minmax(96px, 1fr) auto; }
.meteogram.is-reflow .panel-wind { min-height: var(--h-wind); grid-template-rows: auto minmax(40px, 1fr) 20px; }
.meteogram.is-reflow .panel-sun { min-height: var(--h-sun); grid-template-rows: auto auto minmax(24px, 1fr); }
.meteogram.is-reflow .panel-freezing { min-height: var(--h-freezing); grid-template-rows: auto minmax(28px, 1fr); }
.meteogram.is-reflow .panel-cloud { min-height: var(--h-cloud); grid-template-rows: auto minmax(14px, 1fr) 1px minmax(14px, 1fr) 1px minmax(14px, 1fr); }
.meteogram.is-reflow .panel-head { flex-wrap: wrap; }
.meteogram.is-reflow .hour-ticks { min-height: 1.4em; }
/* Enlarged text grows the dock; app.js reports its height. */
.meteogram.is-reflow { --pad-bottom: calc(var(--dock-measured, var(--dock-h)) + 4px); }

/* Header, one 36 px line: station and altitude, freshness, and the menu
   button. The controls live in the bottom pill, the release version in the
   Info sheet. The station name gives way first (ellipsis), then the
   altitude on narrow screens; freshness always stays. */
.mg-header { height: 36px; display: grid; grid-template-columns: minmax(0, 1fr) auto 44px; align-items: center; column-gap: 8px; }
.mg-title { min-width: 0; overflow: hidden; color: var(--ink); font-size: 15px; font-weight: 600; line-height: 20px; white-space: nowrap; text-overflow: ellipsis; }
.mg-elevation { color: var(--tick-ink); font-weight: 400; }
@media (max-width: 359px) {
  .mg-elevation { display: none; }
}
.mg-meta { color: var(--tick-ink); font-size: 12px; line-height: 16px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.forecast-status { font-weight: 600; color: var(--tick-ink); }
.forecast-status.is-aging { color: var(--aging); }
.forecast-status.is-old { color: var(--old); }
.refresh-spinner { width: 12px; height: 12px; margin-right: 4px; vertical-align: -2px; animation: refresh-spin .8s linear infinite; }
@keyframes refresh-spin { to { transform: rotate(360deg); } }
/* 44 px target over the 36 px header row, reaching 4 px above and below. */
.mg-header .icon-button { margin: -4px 0; }
.icon-button { width: 44px; height: 44px; display: grid; place-items: center; border: 0; border-radius: 8px; background: transparent; color: var(--ink); }
.icon-button svg { width: 22px; height: 22px; }

/* Full / 24 h segmented control */
.view-controls { display: grid; grid-template-columns: 1fr 1fr; height: 44px; margin-top: 6px; }
.view-button { min-height: 44px; border: 1px solid var(--major-grid); background: #fff; font-size: 14px; font-weight: 600; line-height: 18px; }
.view-button:first-child { border-radius: 6px 0 0 6px; }
.view-button:last-child { border-left: 0; border-radius: 0 6px 6px 0; }
.view-button.is-active { background: var(--ink); border-color: var(--ink); color: #fff; }
.language-switch .view-button { display: flex; align-items: center; justify-content: center; color: inherit; text-decoration: none; }
.language-switch .view-button.is-active { color: #fff; }

/* Rows sharing the plot columns: gutter | plot | right edge. The date band
   and the panels give up the page's side margin, so the plots span the full
   screen width (within the safe area). */
.mg-row, .panel { display: grid; grid-template-columns: var(--gutter) minmax(0, 1fr) var(--edge); min-width: 0; min-height: 0; }
.day-band, .chart-stack { margin-inline: calc(-1 * var(--side)); }
.mg-track { grid-column: 2; position: relative; min-width: 0; }

.day-band { align-items: stretch; }
/* A date never leaves its own day; "Today" may run on into the next. */
.day-track { overflow: visible; }
.day-boundary { position: absolute; top: 0; bottom: 0; width: 1px; margin-left: -.5px; background: var(--major-grid); }
.day-label { position: absolute; top: 50%; transform: translate(-50%, -50%); padding: 8px 3px; background: var(--canvas); color: var(--tick-ink); font-size: 12px; font-weight: 600; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The selected time's day sits in a soft pill with a hairline outline; the
   selected time's line reaches up to it. */
.day-label { transition: color .2s ease; }
.day-label::before { content: ""; position: absolute; inset: 5px -4px; border: 1px solid transparent; border-radius: 999px; transition: border-color .2s ease, background-color .2s ease; }
.day-label.is-selected { color: var(--ink); }
.day-label.is-selected::before { border-color: rgba(36, 49, 61, .5); background: rgba(36, 49, 61, .05); }
.detail-nav { grid-column: 1 / -1; grid-row: 1; display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px; align-items: center; }
.meteogram.is-detail .day-track { display: none; }
.detail-label { text-align: center; font-size: 13px; font-weight: 600; line-height: 16px; font-variant-numeric: tabular-nums; }
.nav-step { min-width: 44px; min-height: 44px; border: 1px solid var(--major-grid); background: #fff; font-size: 24px; line-height: 1; }
.nav-step:first-child { border-radius: 6px; }
.nav-step:last-child { border-radius: 6px; }

/* Panels */
.chart-stack { position: relative; min-width: 0; min-height: 0; display: grid; grid-template-rows: var(--h-temp) var(--h-wind) var(--h-sun) var(--h-freezing) var(--h-cloud); row-gap: 4px; }
.panel { position: relative; }
.panel-temp { grid-template-rows: 20px minmax(0, 1fr) 12px; }
.panel-wind { grid-template-rows: 18px minmax(0, 1fr) 20px; }
.panel-sun { grid-template-rows: 18px 14px minmax(0, 1fr); }
.panel-freezing { grid-template-rows: 16px minmax(0, 1fr); }
.panel-cloud { grid-template-rows: 16px minmax(0, 1fr) 1px minmax(0, 1fr) 1px minmax(0, 1fr); }
.panel-head { grid-column: 1 / -1; grid-row: 1; min-width: 0; padding-inline: var(--side); display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* Panel titles and keys sit over the cursors, which pass behind them. */
.panel-head h2, .panel-head .legend, .panel-head .cloud-key { position: relative; z-index: 5; background: var(--canvas); }
.panel-head h2 { font-size: var(--fs-title); font-weight: 600; line-height: 16px; white-space: nowrap; }
.panel-head .unit { color: var(--secondary-ink); font-size: 12px; font-weight: 400; }
.legend { display: flex; gap: 10px; list-style: none; color: var(--secondary-ink); font-size: 12px; line-height: 14px; white-space: nowrap; }
.legend li::before { content: ""; display: inline-block; width: 14px; margin-right: 4px; border-top: 2.5px solid; vertical-align: 3px; }
.legend .lg-temp::before { border-color: var(--temperature); }
.legend .lg-dew::before { border-color: var(--dew); border-top-width: 2px; }
.legend .lg-fog::before { width: 14px; height: 10px; border: 0; border-radius: 5px; background: var(--fog); vertical-align: -1px; }
.legend .lg-short { display: none; }
@media (max-width: 389px) {
  .legend .lg-long { display: none; }
  .legend .lg-short { display: inline; }
}
.legend .lg-mean::before { border-color: var(--wind); border-top-width: 2px; border-top-style: dashed; }
.legend .lg-gust::before { border-color: var(--gust); }
.legend .lg-freezing::before { border-color: var(--freezing-level); }
.cloud-key { display: inline-flex; align-items: center; gap: 4px; color: var(--secondary-ink); font-size: 12px; line-height: 14px; white-space: nowrap; }
.cloud-key .lg-short { display: none; }
@media (max-width: 389px) {
  .cloud-key .lg-long { display: none; }
  .cloud-key .lg-short { display: inline; }
}
.cloud-key i { width: 28px; height: 8px; border: 1px solid var(--minor-grid); background: linear-gradient(90deg, #f7fafc, #e4ebf1 25%, #d0dae3 50%, #bac6d1 75%, #a1afbd); }

/* Axis labels overlay the right of their plot, away from the "now" end and
   the default selection, above the data and below the cursors, on a white
   halo that keeps them legible over lines and fills. */
.panel-axis { grid-column: 2; grid-row: 2; position: relative; z-index: 3; container-type: size; pointer-events: none; }
.panel-plot { grid-column: 2; grid-row: 2; position: relative; overflow: hidden; container-type: size; }
.panel-strip { grid-column: 2; grid-row: 3; position: relative; overflow: hidden; }
.axis-tick { position: absolute; right: 3px; top: clamp(7px, var(--y), calc(100% - 7px)); transform: translateY(-50%); color: var(--secondary-ink); font-size: 12px; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; text-shadow: 0 0 2px #fff, 0 0 2px #fff, 0 0 3px #fff, 0 0 3px #fff; }
@container (max-height: 100px) {
  .axis-tick.is-minor, .grid-line.is-minor { display: none; }
}
/* A short wind plot keeps its 0 and top labels. */
@container (max-height: 35px) {
  .wind-axis .axis-tick:nth-child(2) { display: none; }
}
.plot-svg { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.night { fill: var(--night); }
.midnight { stroke: var(--major-grid); stroke-width: 1; vector-effect: non-scaling-stroke; }
.midnight.on-cloud { stroke: rgba(36, 49, 61, .14); }
.grid-line { stroke: var(--minor-grid); stroke-width: 1; vector-effect: non-scaling-stroke; }
.zero-line { stroke: var(--zero-line); stroke-width: 1.25; vector-effect: non-scaling-stroke; }
.series { fill: none; vector-effect: non-scaling-stroke; stroke-linecap: round; stroke-linejoin: round; }
.temperature-series { stroke: var(--temperature); stroke-width: 2.5; }
/* White halo under both temperature lines keeps them legible over the fill. */
.series.halo { stroke: #fff; stroke-width: 4.5; stroke-opacity: .9; }
/* Grid over the coloured fill: dark at low opacity reads on fill and white alike. */
.panel-temp .grid-line { stroke: rgba(36, 49, 61, .14); }
.dew-series { stroke: var(--dew); stroke-width: 2; }
/* Possible fog: a thin bar along the top of the temperature plot, under the
   weather symbols. */
.fog { position: absolute; z-index: 1; top: 2px; height: 12px; border-radius: 6px; background: var(--fog); pointer-events: none; }
.mean-series { stroke: var(--wind); stroke-width: 2; stroke-dasharray: 4 3; }
.gust-series { stroke: var(--gust); stroke-width: 2.5; }
.sun-fill { fill: var(--sun); }
.sun-edge { fill: none; stroke: var(--sun-ink); stroke-width: 1; vector-effect: non-scaling-stroke; }
.hatch { position: absolute; top: 0; bottom: 0; background: repeating-linear-gradient(135deg, var(--missing) 0 3px, var(--major-grid) 3px 4.5px); }
.panel-message { position: absolute; z-index: 3; top: 2px; right: 2px; padding: 0 4px; border-radius: 3px; background: rgba(255, 255, 255, .9); color: var(--secondary-ink); font-size: 12px; line-height: 14px; pointer-events: none; }
.panel-cloud > .panel-message { grid-column: 2; grid-row: 2; position: relative; justify-self: end; align-self: start; }

.wx { position: absolute; z-index: 2; width: 20px; height: 20px; transform: translate(-50%, -50%); pointer-events: none; }
.wx-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
/* Daily high/low: a dot on the curve and a small value just below it. */
.extreme { position: absolute; z-index: 3; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border: 1.5px solid #fff; border-radius: 50%; background: var(--temperature); pointer-events: none; }
/* Values on a translucent chip (as the peak values) so they read over the
   coloured fill and lines. */
.extreme b { position: absolute; top: 7px; left: 50%; transform: translateX(-50%); padding: 0 3px; border-radius: 4px; background: rgba(255, 255, 255, .78); color: var(--ink); font-size: 12px; font-weight: 600; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.extreme.is-above b { top: auto; bottom: 7px; }
/* Near an edge the label hangs to the inner side of its point. */
.extreme.is-end b { left: auto; right: 5px; transform: none; }
.extreme.is-start b { left: 5px; transform: none; }
.wx svg, .wind-mark svg { display: block; width: 100%; height: 100%; }
.wind-mark { position: absolute; top: 50%; width: 16px; height: 16px; color: var(--wind); transform: translate(-50%, -50%) rotate(var(--rot, 0deg)); }
.calm-mark { width: 9px; height: 9px; border: 1.5px solid var(--wind); border-radius: 50%; transform: translate(-50%, -50%); }

.sun-totals { grid-column: 2; grid-row: 2; position: relative; overflow: hidden; }
.sun-total { position: absolute; top: 0; transform: translateX(-50%); color: var(--sun-ink); font-size: 12px; font-weight: 600; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sun-plot { grid-row: 3; }
.sun-axis { grid-row: 3; }
.sun-max { top: 0; transform: none; }
.sun-min { top: auto; bottom: -2px; transform: none; }
@container (max-height: 27px) {
  .sun-max { display: none; }
}

/* Freezing level: the 0 °C line over a pale "frozen" tint above it, against
   dashed guides at each station's elevation (labelled in the gutter). The
   plot lets the value beside the selection dot run past its edges. */
.freezing-plot { overflow: visible; }
.freezing-series { stroke: var(--freezing-level); stroke-width: 2.25; }
.frozen-fill { fill: rgba(14, 116, 144, .1); }
.station-guide { stroke: rgba(36, 49, 61, .22); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.station-guide.is-current { stroke: rgba(36, 49, 61, .6); }
.station-tick { font-weight: 600; }
.station-tick.is-current { color: var(--ink); }
.freezing-value { color: #0b5566; }

/* Cloud layers top to bottom: high, mid, low (named in the panel header). */
.cloud-row.row-0 { grid-row: 2; }
.cloud-row.row-1 { grid-row: 4; }
.cloud-row.row-2 { grid-row: 6; }
.cloud-row { background: var(--missing); }
.precip-layer { grid-column: 2; grid-row: 2 / 7; position: relative; z-index: 2; overflow: hidden; pointer-events: none; }
.precip-bars { fill: var(--precip); }
.precip-bars.is-showers { fill: var(--showers); }
.precip-bars.is-snow { fill: var(--snow); stroke: var(--snow-edge); stroke-width: 1.25; vector-effect: non-scaling-stroke; }
.precip-bars.is-freezing { fill: var(--freezing); }
.precip-missing { top: auto; bottom: 0; height: 4px; }
/* One label per snow spell, just above its tallest bar. */
.snow-run { position: absolute; z-index: 3; transform: translate(-50%, -100%); padding: 0 3px; border-radius: 3px; background: rgba(255, 255, 255, .9); color: var(--snow-edge); font-size: 12px; font-weight: 600; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.precip-layer .panel-message { right: auto; left: 2px; }
.precip-axis { grid-row: 2 / 7; }
.precip-max { top: 0; transform: none; }
.precip-mid { top: 50%; }
.precip-min { top: auto; bottom: -2px; transform: none; }
@container (max-height: 39px) {
  .precip-mid { display: none; }
}
/* Grid line at the middle tick, styled like every other panel's grid. */
.precip-grid { stroke: rgba(36, 49, 61, .14); stroke-width: 1; vector-effect: non-scaling-stroke; }
/* Highest bar in view: a dot on its top and its rate. */
/* Peak markers: dot plus value on the highest precipitation bar and the
   strongest gust in view. */
.precip-peak, .gust-peak { position: absolute; z-index: 3; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border: 1.5px solid #fff; border-radius: 50%; background: var(--precip); }
.gust-peak { background: var(--gust); pointer-events: none; }
.precip-peak b, .gust-peak b { position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%); padding: 0 3px; border-radius: 4px; background: rgba(255, 255, 255, .78); color: #0b3fb3; font-size: 12px; font-weight: 600; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.gust-peak b { color: #6e4300; }
/* At the edges the label sits beside its point, 4.5 px clear of it, so a
   point on the plot edge still leaves the label clear of the edge. */
.precip-peak.is-start b, .gust-peak.is-start b { left: 8px; transform: none; }
.precip-peak.is-end b, .gust-peak.is-end b { left: auto; right: 8px; transform: none; }
.precip-peak.is-side b, .gust-peak.is-side b { bottom: auto; top: 50%; left: 8px; right: auto; transform: translateY(-50%); }
.precip-peak.is-side.is-end b, .gust-peak.is-side.is-end b { left: auto; right: 8px; }

/* Hour labels under the temperature panel (00 · 06 · 12 · 18); a 6-hour
   slot without room for its label keeps a short tick. */
.hour-ticks { grid-column: 2; grid-row: -2; position: relative; overflow: hidden; }
.time-tick { position: absolute; top: 0; transform: translateX(-50%); color: var(--tick-ink); font-size: 12px; line-height: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.time-tick.is-quiet { width: 1px; height: 4px; overflow: hidden; background: var(--major-grid); color: transparent; }

/* Cursors span all four panels. Left of the solid "now" line the chart is
   veiled to 40 %; the dashed line marks the selected time. The layer covers
   exactly the plot column, so --x (0–1 of the view) places both lines. */
.cursor-layer { position: absolute; z-index: 4; top: 0; bottom: 0; left: var(--gutter); right: var(--edge); pointer-events: none; }
.past-veil { position: absolute; top: 0; bottom: 0; left: 0; width: calc(var(--x) * 100%); background: rgba(255, 255, 255, .6); }
.now-line { position: absolute; top: 0; bottom: 0; left: calc(var(--x) * 100%); width: 1.5px; margin-left: -.75px; background: var(--now-ink); }
/* "now" chip at the top of the temperature plot, centred on the line (app.js
   keeps it inside the plot). */
.now-tag { position: absolute; top: 21px; left: calc(var(--x) * 100%); transform: translateX(var(--shift, -50%)); padding: 0 4px; border-radius: 3px; background: var(--now-ink); color: #fff; font-size: 12px; font-weight: 700; line-height: 14px; white-space: nowrap; }
/* While another time is selected, the now line and chip stay, greyed out. */
.now-line.is-muted, .now-tag.is-muted { background: var(--now-muted); }
/* The selected time's chip takes the "now" chip's place, centred on the
   dashed line (app.js keeps it inside the plot): a quiet outlined label
   rather than a solid one. */
.sel-tag { position: absolute; top: 21px; left: calc(var(--x) * 100%); transform: translateX(var(--shift, -50%)); padding: 0 5px; border-radius: 999px; background: rgba(255, 255, 255, .92); box-shadow: inset 0 0 0 1px rgba(36, 49, 61, .3); color: var(--select-ink); font-size: 12px; font-weight: 600; line-height: 15px; white-space: nowrap; font-variant-numeric: tabular-nums; animation: chip-in .16s ease-out; }
@keyframes chip-in { from { opacity: 0; } }
/* The line marking the selection runs up to its day's pill in the date
   band above (its bottom edge is 10px below the band's middle). */
.sel-line.reaches-date, .now-line.reaches-date { top: calc(6px - var(--h-day) / 2); }
.meteogram.is-detail .reaches-date { top: 0; }
.sel-line { position: absolute; top: 0; bottom: 0; left: calc(var(--x) * 100%); width: 0; margin-left: -.75px; border-left: 1.5px dashed var(--select-ink); }
/* A dot on each series at the selected time. */
.ring { position: absolute; z-index: 4; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border: 2px solid; border-radius: 50%; background: #fff; pointer-events: none; }
.ring-temp { border-color: var(--temperature); }
.ring-dew { border-color: var(--dew); }
.ring-mean { border-color: var(--wind); }
.ring-gust { border-color: var(--gust); }
.ring-sun { border-color: var(--sun-ink); }
.ring-precip { border-color: var(--precip); }
.ring-freezing { border-color: var(--freezing-level); }
/* Values beside the selection dots (temperature, gusts, freezing level):
   right of the dot, or left of it where the right edge leaves no room. */
.ring-value { position: absolute; top: 50%; left: 9px; transform: translateY(-50%); padding: 0 3px; border-radius: 4px; background: rgba(255, 255, 255, .88); font-size: 12px; font-weight: 700; line-height: 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ring-value:empty { display: none; }
.ring.is-end .ring-value { left: auto; right: 9px; }
.ring-temp .ring-value { color: #a3161b; }
.ring-gust .ring-value { color: #6e4300; }
/* A high/low or peak label under a dot's value gives way to it. */
.is-covered { visibility: hidden; }

/* Tap or drag over the plots to select a time; vertical panning stays native. */
.mg-hit { position: absolute; z-index: 5; top: 0; bottom: 0; left: var(--gutter); right: var(--edge); touch-action: pan-y pinch-zoom; cursor: crosshair; }

/* Bottom dock: the pill, fixed to the bottom of the viewport above the
   safe area. */
.dock { position: fixed; z-index: 10; right: 0; bottom: 0; left: 0; display: grid; gap: 4px; padding: 0 max(8px, env(safe-area-inset-right)) calc(8px + env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left)); pointer-events: none; }
.dock > * { width: 100%; max-width: 560px; margin: 0 auto; pointer-events: auto; }
/* Opaque, hairline-bordered, no blur or shadow. Every target is 56 × 56. */
.pill { min-height: var(--pill-h); display: flex; align-items: stretch; overflow: hidden; border: 1px solid var(--zero-line); border-radius: calc(var(--pill-h) / 2); background: var(--canvas); touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.pill.is-settling { transition: transform .2s ease-out; }
/* Steps and stations stay 56 px wide from a 360 px screen up; the time takes
   the rest. Narrower screens give up to 8 px per target. */
.pill-step { flex: 0 1 56px; min-width: 48px; border: 0; background: transparent; color: var(--ink); font-size: 30px; line-height: 1; }
.pill .location-switch { flex: 0 1 170px; display: flex; min-width: 0; border-right: 1px solid var(--major-grid); border-left: 1px solid var(--major-grid); }
.loc-link { flex: 0 1 56px; min-width: 48px; display: grid; place-items: center; color: var(--tick-ink); font-size: 15px; font-weight: 600; letter-spacing: .02em; text-decoration: none; -webkit-touch-callout: none; }
.loc-link span { min-width: 40px; padding: 6px 6px; border-radius: 18px; text-align: center; }
.loc-link.is-current span { background: var(--ink); color: #fff; }
.pill-time { flex: 1 0 56px; min-width: 56px; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 0; border-right: 1px solid var(--major-grid); background: transparent; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pill-time span { color: var(--tick-ink); font-size: 12px; line-height: 14px; }
.pill-time strong { font-size: 17px; font-weight: 700; line-height: 20px; }
.pill:focus-visible, .pill :focus-visible { outline-offset: -3px; }

/* Sheets */
.forecast-dialog { width: 100%; max-width: 640px; max-height: 85vh; max-height: 85svh; margin: auto auto 0; padding: 16px 16px max(16px, env(safe-area-inset-bottom)); border: 0; border-radius: 12px 12px 0 0; background: #fff; color: var(--ink); font-size: 16px; line-height: 1.5; overflow: auto; }
@media (min-width: 700px) {
  .forecast-dialog { margin: auto; border-radius: 12px; }
}
.forecast-dialog::backdrop { background: rgba(36, 49, 61, .45); }
.forecast-dialog h2 { margin: 0 52px 8px 0; font-size: 20px; line-height: 28px; }
.forecast-dialog h3 { margin-top: 18px; font-size: 17px; }
.forecast-dialog p { margin-top: 8px; }
.forecast-dialog dl { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; margin-top: 6px; }
.forecast-dialog dt { color: var(--secondary-ink); font-weight: 600; }
.dialog-location { color: var(--secondary-ink); }
.dialog-close { position: absolute; top: 8px; right: 8px; width: 44px; height: 44px; border: 0; background: transparent; font-size: 30px; }
.install-app { margin-top: 12px; }
.install-app .install-button { margin-top: 0; background: var(--ink); border-color: var(--ink); color: #fff; }
.install-help { color: var(--secondary-ink); font-size: 15px; }
.sheet-button { min-height: 44px; margin-top: 14px; padding: 8px 14px; border: 1px solid var(--major-grid); border-radius: 6px; background: #fff; font-weight: 600; }
.info-legend { list-style: none; display: grid; gap: 8px; margin-top: 6px; }
.info-legend li { display: grid; grid-template-columns: 44px 1fr; align-items: start; gap: 8px; }
.info-legend li.info-note { display: block; }
.key-line { margin-top: 11px; border-top: 2.5px solid; }
.key-temp { border-color: var(--temperature); }
.key-dew { border-color: var(--dew); border-top-width: 2px; }
.key-freezing { border-color: var(--freezing-level); }
.key-mean { border-color: var(--wind); border-top-width: 2px; border-top-style: dashed; }
.key-gust { border-color: var(--gust); }
.key-arrow { width: 18px; height: 18px; margin-top: 3px; color: var(--wind); transform: rotate(45deg); }
.key-arrow svg { display: block; width: 100%; height: 100%; }
.key-bar { height: 18px; margin-top: 3px; border-top: 1px solid var(--sun-ink); background: var(--sun); }
.key-ramp { display: grid; grid-template-columns: repeat(5, 1fr); height: 14px; margin-top: 5px; }
.key-temp-ramp { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); margin-bottom: 6px; color: var(--secondary-ink); font-size: 12px; line-height: 16px; text-align: center; font-variant-numeric: tabular-nums; }
.key-temp-ramp i { display: block; height: 12px; }
.key-precip { height: 18px; margin-top: 3px; background: linear-gradient(90deg, var(--precip) 0 60%, var(--showers) 60%); }
.key-kinds { display: flex; gap: 3px; align-items: flex-end; margin-top: 3px; }
.key-kinds i { width: 6px; height: 16px; background: var(--precip); }
.key-kinds .kind-showers { background: var(--showers); }
.key-kinds .kind-snow { background: var(--snow); box-shadow: inset 0 0 0 1.25px var(--snow-edge); }
.key-kinds .kind-mix { background: linear-gradient(var(--snow) 50%, var(--precip) 50%); box-shadow: inset 0 0 0 1.25px var(--snow-edge); }
.key-kinds .kind-freezing { background: var(--freezing); }
.key-snow { color: var(--snow-edge); font-size: 18px; line-height: 24px; text-align: center; }
.key-fog { height: 12px; margin-top: 6px; border-radius: 6px; background: var(--fog); }
.key-hatch { height: 18px; margin-top: 3px; background: repeating-linear-gradient(135deg, var(--missing) 0 3px, var(--major-grid) 3px 4.5px); }
.table-dialog { max-width: 1040px; }
.table-scroll { overflow: auto; margin-top: 8px; }
.accessible-table { width: 100%; border-collapse: collapse; font-size: 16px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.accessible-table th, .accessible-table td { padding: 6px 8px; border: 1px solid var(--major-grid); text-align: left; }
.accessible-table thead th { position: sticky; top: 0; background: #f7f8fa; }
.accessible-table tbody th { font-weight: 600; }

/* Loading and errors share the chart grid. */
.meteogram-loading > div { background: var(--missing); border-radius: 4px; }
.meteogram-loading > .loading-label { display: flex; align-items: center; padding: 0 8px; background: transparent; color: var(--secondary-ink); font-size: 14px; font-weight: 600; }
.error-view { margin: 20px 0; }
.error-view nav { display: grid; gap: 8px; margin-top: 18px; }
.error-view a { min-height: 44px; padding: 10px; border: 1px solid var(--major-grid); border-radius: 6px; color: #174ea6; }

@media (max-width: 379px) {
  .fresh-word { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { scroll-behavior: auto !important; transition: none !important; animation: none !important; }
}
