﻿/* ===========================================================================
   Vane — design tokens & base styles
   Source: docs/DESIGN_PLAN.md — color system, typography, focus indicator.
   =========================================================================== */

:root {
  /* Tells the browser to render its OWN chrome (date/month pickers,
     scrollbars, checkboxes, etc.) in dark mode — without this, native
     controls like the Graphs page's month picker stay light-themed
     regardless of Vane's own colors, since that popup isn't ordinary
     CSS-stylable content. Kept in sync with --bg/--text below via the
     same data-theme/prefers-color-scheme rules. */
  color-scheme: dark;

  /* Base (dark-first, see DESIGN_PLAN.md) */
  --bg: #020617;
  --surface: rgba(15, 23, 42, .62);
  --surface-2: rgba(148, 163, 184, .07);
  --border: rgba(148, 163, 184, .14);
  --text: #f1f5f9;
  --text-muted: #94a3b8;
  --grid: rgba(148, 163, 184, .12);

  /* Accent per data source — --accent-primary is configurable via
     skin.conf (accent_color), either as a fixed color or 'season' */
  --accent-primary: #2f8bff;
  --accent-wind: #fb923c;
  --accent-storm: #7c5cff;
  --status-live: #4ade80;

  /* Seasonal accent palette & ambient lighting */
  --season-spring: #10b981;
  --season-spring-glow: rgba(16, 185, 129, 0.22);
  --season-summer: #f59e0b;
  --season-summer-glow: rgba(245, 158, 11, 0.22);
  --season-autumn: #ea580c;
  --season-autumn-glow: rgba(234, 88, 12, 0.24);
  --season-winter: #38bdf8;
  --season-winter-glow: rgba(56, 189, 248, 0.22);
  --season-glow: var(--season-autumn-glow);

  --font-sans: 'Geist', system-ui, -apple-system, sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, monospace;

  --radius-card: 20px;
  --radius-tile: 16px;
  --radius-pill: 999px;
}

/* Seasonal accents automatically applied when data-season is present */
:root[data-season="spring"] { --accent-primary: var(--season-spring); --season-glow: var(--season-spring-glow); }
:root[data-season="summer"] { --accent-primary: var(--season-summer); --season-glow: var(--season-summer-glow); }
:root[data-season="autumn"] { --accent-primary: var(--season-autumn); --season-glow: var(--season-autumn-glow); }
:root[data-season="winter"] { --accent-primary: var(--season-winter); --season-glow: var(--season-winter-glow); }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"])[data-season="spring"] { --accent-primary: #059669; --season-glow: rgba(5, 150, 105, 0.18); }
  :root:not([data-theme="dark"])[data-season="summer"] { --accent-primary: #d97706; --season-glow: rgba(217, 119, 6, 0.18); }
  :root:not([data-theme="dark"])[data-season="autumn"] { --accent-primary: #c2410c; --season-glow: rgba(194, 65, 12, 0.20); }
  :root:not([data-theme="dark"])[data-season="winter"] { --accent-primary: #0284c7; --season-glow: rgba(2, 132, 199, 0.18); }
}

:root[data-theme="light"][data-season="spring"] { --accent-primary: #059669; --season-glow: rgba(5, 150, 105, 0.18); }
:root[data-theme="light"][data-season="summer"] { --accent-primary: #d97706; --season-glow: rgba(217, 119, 6, 0.18); }
:root[data-theme="light"][data-season="autumn"] { --accent-primary: #c2410c; --season-glow: rgba(194, 65, 12, 0.20); }
:root[data-theme="light"][data-season="winter"] { --accent-primary: #0284c7; --season-glow: rgba(2, 132, 199, 0.18); }


@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #f8fafc;
    --surface: rgba(255, 255, 255, .72);
    --surface-2: rgba(15, 23, 42, .045);
    --border: rgba(15, 23, 42, .09);
    --text: #0f172a;
    --text-muted: #64748b;
    --status-live: #16a34a;
    --grid: rgba(15, 23, 42, .08);
  }
}

/* data-theme always wins over the system preference, in either direction */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f8fafc;
  --surface: rgba(255, 255, 255, .72);
  --surface-2: rgba(15, 23, 42, .045);
  --border: rgba(15, 23, 42, .09);
  --text: #0f172a;
  --text-muted: #64748b;
  --status-live: #16a34a;
  --grid: rgba(15, 23, 42, .08);
}
:root[data-theme="dark"] {
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

/* ---------------------------------------------------------------------------
   Custom scrollbars — the OS-default scrollbar (often a stark light-grey
   bar) clashes with the dark theme, especially inside the NOAA modal.
   scrollbar-color covers Firefox; the ::-webkit-scrollbar rules cover
   Chrome/Edge/Safari. Applied globally so any scrollable element (page,
   modal, sortable table) matches, not just a single spot.
   --------------------------------------------------------------------------- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--text-muted) transparent;
}
*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
*::-webkit-scrollbar-track {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  /* Fallback (no color-mix support): flat, slightly-transparent muted
     tone; browsers with color-mix() get a softer variant of the same
     color instead of a hardcoded rgba. */
  background: rgba(148, 163, 184, .35);
  background: color-mix(in oklch, var(--text-muted) 40%, transparent);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
  background: rgba(148, 163, 184, .6);
  background: color-mix(in oklch, var(--text-muted) 70%, transparent);
  background-clip: padding-box;
}

body {
  /* Fallback for browsers without color-mix(): flat background, no
     gradient — degrades gracefully instead of breaking. */
  background: var(--bg);
  background:
    radial-gradient(1100px 520px at 0% -10%, color-mix(in oklch, var(--accent-primary) 14%, transparent), transparent 65%),
    radial-gradient(850px 420px at 100% 0%, var(--season-glow), transparent 65%),
    var(--bg);
  transition: background-color .3s, border-color .3s;
}

a {
  color: var(--accent-primary);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Accessibility: focus indicator (WCAG 2.2 SC 2.4.13) — see DESIGN_PLAN.md.
   Never outline:none without a replacement.
   --------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------- */

.vane-shell {
  min-height: 100vh;
  display: flex;
}

.vane-sidebar {
  flex: none;
  width: 84px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 18px 0;
  border-right: 1px solid var(--border);
  background: var(--surface);
}

.vane-sidebar .logo-link {
  display: block;
  margin-bottom: 18px;
  border-radius: 12px;
  text-decoration: none;
}
.vane-sidebar .logo-link .logo {
  margin-bottom: 0;
}
.vane-sidebar .logo {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  margin-bottom: 18px;
  cursor: pointer;
  display: block;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.3s ease;
}
.vane-sidebar .logo:hover,
.vane-sidebar .logo-link:hover .logo {
  transform: translateY(-2px) scale(1.08);
  filter: drop-shadow(0 6px 14px rgba(248, 142, 59, 0.4));
}
.vane-sidebar .logo:hover .v-needle-group,
.vane-sidebar .logo-link:hover .v-needle-group {
  animation: vane-gust 1.4s cubic-bezier(0.2, 0.8, 0.2, 1) infinite alternate !important;
  filter: drop-shadow(0 0 6px rgba(248, 142, 59, 0.7));
}
.vane-sidebar .logo:hover .v-lightning,
.vane-sidebar .logo-link:hover .v-lightning {
  animation: vane-spark 1.2s ease-in-out infinite alternate;
}
@keyframes vane-gust {
  0% { transform: rotate(0deg); }
  25% { transform: rotate(18deg); }
  50% { transform: rotate(-10deg); }
  75% { transform: rotate(6deg); }
  100% { transform: rotate(2deg); }
}
@keyframes vane-spark {
  0%, 100% { filter: drop-shadow(0 0 0px transparent); }
  50% { filter: drop-shadow(0 0 8px rgba(139, 92, 246, 0.85)); fill: #a78bfa; }
}

.nav-btn {
  all: unset;
  cursor: pointer;
  width: 64px;
  padding: 10px 0 8px;
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  color: var(--text-muted);
}
.nav-btn svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.nav-btn span {
  font-size: 10px;
  font-weight: 500;
}

/* Nav icon micro-animations on hover / keyboard focus. Targeted by
   data-nav, not href: the current page's own item is a <button> without
   an href, so href selectors never animated it. */
.nav-btn svg * {
  transform-box: fill-box;
  transform-origin: center;
}
.nav-btn:hover svg,
.nav-btn:focus-visible svg {
  color: var(--accent-primary);
}
/* Dashboard: the four panels pop one after another */
.nav-btn[data-nav="dashboard"]:hover rect,
.nav-btn[data-nav="dashboard"]:focus-visible rect {
  animation: vane-nav-pop .5s cubic-bezier(.34, 1.56, .64, 1) both;
}
.nav-btn[data-nav="dashboard"] rect:nth-child(2) { animation-delay: .06s; }
.nav-btn[data-nav="dashboard"] rect:nth-child(3) { animation-delay: .12s; }
.nav-btn[data-nav="dashboard"] rect:nth-child(4) { animation-delay: .18s; }
@keyframes vane-nav-pop {
  0%, 100% { transform: scale(1); }
  45% { transform: scale(.72); }
}
/* Graphs + Telemetry: the line draws itself */
.nav-btn[data-nav="graphs"]:hover path:nth-child(2),
.nav-btn[data-nav="graphs"]:focus-visible path:nth-child(2),
.nav-btn[data-nav="telemetry"]:hover path,
.nav-btn[data-nav="telemetry"]:focus-visible path {
  /* The paths carry pathLength="100" in the templates, so 100 is their
     full length whatever their real length — the line draws during the
     whole animation instead of appearing all at once near the end. */
  stroke-dasharray: 100;
  animation: vane-nav-draw .7s ease-out both;
}
/* Negative offset: both paths are drawn right-to-left in their source
   (they start at x=19 / x=22), so a positive offset would reveal the line
   from the right. -100 reveals it from the path's END = the left side. */
@keyframes vane-nav-draw {
  from { stroke-dashoffset: -100; }
  to { stroke-dashoffset: 0; }
}
/* Archive: the lid lifts */
.nav-btn[data-nav="archive"]:hover rect,
.nav-btn[data-nav="archive"]:focus-visible rect {
  animation: vane-nav-lid .6s ease-in-out both;
}
@keyframes vane-nav-lid {
  0%, 100% { transform: translateY(0); }
  45% { transform: translateY(-3px) rotate(-6deg); }
}
/* Almanac: the moon rocks */
.nav-btn[data-nav="almanac"]:hover path,
.nav-btn[data-nav="almanac"]:focus-visible path {
  animation: vane-nav-rock .8s ease-in-out both;
}
@keyframes vane-nav-rock {
  0%, 100% { transform: rotate(0); }
  35% { transform: rotate(-18deg); }
  70% { transform: rotate(8deg); }
}
@media (prefers-reduced-motion: reduce) {
  .nav-btn svg * { animation: none !important; }
}
/* ---------------------------------------------------------------------------
   Segmented tabs & period selectors (Archive, Graphs)
   --------------------------------------------------------------------------- */
.vane-tabs {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 3px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
  box-sizing: border-box;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.vane-tab-btn,
.period-btn {
  all: unset;
  cursor: pointer;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-muted);
  transition: all .15s ease;
  user-select: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-decoration: none;
  box-sizing: border-box;
  white-space: nowrap;
}

.vane-tab-btn:hover,
.period-btn:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 8%, transparent);
}

.vane-tab-btn.active,
.period-btn.active {
  background: var(--accent-primary);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--accent-primary) 35%, transparent);
}

/* Styled select for months and general dropdowns */
.vane-select-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.vane-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  height: 34px;
  padding: 0 32px 0 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
  outline: none;
  box-sizing: border-box;
}

.vane-select:hover {
  border-color: var(--accent-primary);
}

.vane-select:focus {
  border-color: var(--accent-primary);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 25%, transparent);
}

.vane-select option {
  background: var(--bg);
  color: var(--text);
  padding: 8px 12px;
}

.vane-select-chevron {
  position: absolute;
  right: 11px;
  pointer-events: none;
  color: var(--text-muted);
  width: 14px;
  height: 14px;
}

.nav-btn.active {
  /* Fallback: flat surface tint if color-mix() is unavailable. */
  background: var(--surface-2);
  background: color-mix(in oklch, var(--accent-primary) 16%, transparent);
  color: var(--accent-primary);
}

.vane-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* No explicit height: 100% here — percentage heights on a flex item
     don't reliably resolve against a parent sized via min-height (as
     .vane-shell is) across browsers, leaving a stray gap at the bottom.
     Flexbox's own default cross-axis stretch (align-items: normal, which
     computes to stretch) already fills .vane-shell's actual rendered
     height correctly without going through percentage resolution. */
}

.vane-content {
  flex: 1;
  overflow: auto;
}

.vane-container {
  max-width: 1240px;
  margin: 0 auto;
  padding: 24px clamp(16px, 3vw, 32px) 40px;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Pushes the shared footer (footer.inc) to the bottom of the viewport on
   short-content pages (e.g. Almanac) instead of it floating right under
   the last card — but on a page with enough content to fill/overflow the
   viewport, this does nothing and the footer just follows normally. */
.vane-footer-sticky {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* ---------------------------------------------------------------------------
   Top bar
   --------------------------------------------------------------------------- */
.vane-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 20px;
}

.vane-header h1 {
  margin: 0;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -.02em;
}

.vane-header .subtitle {
  font-size: 13px;
  color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   Language switcher — custom listbox (see lang-select.js), not a native
   <select>: a native <select>'s <option> can't contain an <img>, and this
   needs real SVG flag icons. .lang-combo is the positioning root for the
   popup; unlike a native select's OS-chrome popup, this one is our own
   markup so it follows the page's theme automatically, no color-scheme
   trick needed.
   --------------------------------------------------------------------------- */
.lang-combo {
  position: relative;
}

.lang-trigger {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  transition: border-color .15s;
}
.lang-trigger:hover {
  border-color: var(--accent-primary);
}
.lang-chevron {
  color: var(--text-muted);
  transition: transform .15s;
}
.lang-trigger[aria-expanded="true"] .lang-chevron {
  transform: rotate(180deg);
}

.lang-flag {
  width: 18px;
  height: 14px;
  border-radius: 2px;
  outline: 1px solid var(--border);
  outline-offset: -1px;
  object-fit: cover;
  flex-shrink: 0;
}

.lang-listbox {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 20;
  min-width: 170px;
  margin: 0;
  padding: 4px;
  list-style: none;
  border-radius: 10px;
  border: 1px solid var(--border);
  /* --surface is translucent; layer it over --bg so the tiles underneath
     don't bleed through the open menu. */
  background: linear-gradient(var(--surface), var(--surface)), var(--bg);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
}

.lang-option {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 12.5px;
  cursor: pointer;
}
.lang-option:hover,
.lang-option:focus {
  background: var(--surface-2);
  outline: none;
}
.lang-option[aria-selected="true"] {
  font-weight: 600;
  color: var(--accent-primary);
}

.pill {
  display: flex;
  align-items: center;
  gap: 9px;
  height: 34px;
  padding: 0 13px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 12.5px;
  font-weight: 500;
}

.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--status-live);
  position: relative;
}
.live-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--status-live);
  animation: vane-pulse 1.8s ease-out infinite;
}
@keyframes vane-pulse {
  0% { transform: scale(1); opacity: .6; }
  100% { transform: scale(2.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .live-dot::after { animation: none; }
}

.season-pill {
  color: var(--accent-primary);
  border-color: color-mix(in oklch, var(--accent-primary) 28%, var(--border));
  background: color-mix(in oklch, var(--accent-primary) 8%, var(--surface));
  display: inline-flex;
  align-items: center;
  gap: 7px;
  cursor: default;
  transition: all .25s ease;
}
.season-pill:hover {
  border-color: var(--accent-primary);
  box-shadow: 0 0 16px var(--season-glow);
  transform: translateY(-1px);
}
.season-icon {
  display: inline-block;
  flex: none;
  animation: vane-breathe 3.5s ease-in-out infinite;
}
@keyframes vane-breathe {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 0px transparent); }
  50% { transform: scale(1.14); filter: drop-shadow(0 0 5px var(--accent-primary)); }
}

.icon-btn {
  all: unset;
  cursor: pointer;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  transition: border-color .2s ease, transform .2s ease, background-color .2s ease;
}
.icon-btn:hover {
  border-color: var(--accent-primary);
  transform: scale(1.05);
}

/* ---------------------------------------------------------------------------
   Cards & tiles
   --------------------------------------------------------------------------- */
.card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
  border-radius: var(--radius-card);
  background: var(--surface);
  border: 1px solid var(--border);
  backdrop-filter: blur(14px);
  transition: border-color .25s ease, box-shadow .25s ease, transform .25s ease;
}
.card:hover {
  border-color: color-mix(in oklch, var(--accent-primary) 22%, var(--border));
  box-shadow: 0 10px 30px -10px var(--season-glow);
}

.tile {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  border-radius: var(--radius-tile);
  background: var(--surface);
  border: 1px solid var(--border);
  backdrop-filter: blur(14px);
  transition: border-color .25s ease, box-shadow .25s ease, transform .25s ease;
}
.tile:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklch, var(--accent-primary) 34%, var(--border));
  box-shadow: 0 10px 24px -8px var(--season-glow);
}

.tile-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.tile-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.tile-icon {
  width: 28px;
  height: 28px;
  flex: none;
  transition: transform .3s cubic-bezier(.34, 1.56, .64, 1), filter .3s ease;
}
/* Host for a Lottie animation that replaces a static icon (vane-lottie.js);
   width/height are set inline from the icon it replaces. */
.lottie-icon {
  display: inline-block;
  flex: none;
  line-height: 0;
  vertical-align: middle;
}
/* Optional weewx-skymap-almanac output (almanac.html.tmpl). */
.vane-skymap-wrap {
  display: flex;
  justify-content: center;
}
.vane-skymap-wrap svg {
  display: block;
  width: 100%;
  max-width: 620px;
  height: auto;
  font-family: var(--font-sans);
}
.vane-moon-symbol {
  display: inline-flex;
  flex: none;
  line-height: 0;
}
.vane-moon-symbol svg {
  width: 40px;
  height: 40px;
}

/* The display above would otherwise beat the [hidden] attribute that
   vane-lottie.js uses to swap between Lottie and SVG — both then showed. */
.lottie-icon[hidden] {
  display: none;
}

/* ---------------------------------------------------------------------------
   Interactive Micro-Animations on Tile Hover
   --------------------------------------------------------------------------- */
/* Wind: rotor spins at steady 4s, accelerates on tile hover to 0.8s while mast stays fixed! */
.wind-rotor {
  transform-origin: 0px 0px;
}
.tile[data-sensor="windSpeed"]:hover .wind-rotor,
.tile[data-sensor="wind"]:hover .wind-rotor {
  animation: vane-rotor-fast 0.8s linear infinite !important;
}
@keyframes vane-rotor-fast {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* Humidity & Dewpoint: droplet bounce */
.tile[data-sensor="humidity"]:hover .tile-icon,
.tile[data-sensor="dewpoint"]:hover .tile-icon {
  animation: vane-droplet 1.3s ease-in-out infinite;
}
@keyframes vane-droplet {
  0%, 100% { transform: translateY(0) scale(1, 1); }
  30% { transform: translateY(-4px) scale(0.92, 1.08); }
  60% { transform: translateY(1.5px) scale(1.06, 0.94); }
  80% { transform: translateY(0) scale(0.98, 1.02); }
}

/* Barometer: Dynamic Light / Dark Theme Adaptability */
:root {
  --baro-face: url(#baroFaceDark);
  --baro-ticks: #94A3B8;
  --baro-hub: #FFFFFF;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --baro-face: url(#baroFaceLight);
    --baro-ticks: #64748B;
    --baro-hub: #0F172A;
  }
  :root:not([data-theme="dark"]) .baro-face {
    fill: url(#baroFaceLight) !important;
  }
  :root:not([data-theme="dark"]) .baro-ticks line {
    stroke: #64748b !important;
    opacity: 0.9 !important;
  }
  :root:not([data-theme="dark"]) .baro-hub {
    fill: #0f172a !important;
  }
}

:root[data-theme="light"] {
  --baro-face: url(#baroFaceLight);
  --baro-ticks: #64748B;
  --baro-hub: #0F172A;
}
:root[data-theme="light"] .baro-face {
  fill: url(#baroFaceLight) !important;
}
:root[data-theme="light"] .baro-ticks line {
  stroke: #64748b !important;
  opacity: 0.9 !important;
}
:root[data-theme="light"] .baro-hub {
  fill: #0f172a !important;
}

:root[data-theme="dark"] {
  --baro-face: url(#baroFaceDark);
  --baro-ticks: #94A3B8;
  --baro-hub: #FFFFFF;
}
:root[data-theme="dark"] .baro-face {
  fill: url(#baroFaceDark) !important;
}
:root[data-theme="dark"] .baro-ticks line {
  stroke: #94a3b8 !important;
  opacity: 0.8 !important;
}
:root[data-theme="dark"] .baro-hub {
  fill: #ffffff !important;
}

.baro-face {
  fill: var(--baro-face, url(#baroFaceDark));
  transition: fill .3s ease;
}
.baro-ticks line {
  stroke: var(--baro-ticks, #94a3b8);
  transition: stroke .3s ease;
}
.baro-hub {
  fill: var(--baro-hub, #ffffff);
  transition: fill .3s ease;
}

/* Barometer: only needle twitches on hover, housing and dial stay completely still! */
.baro-needle {
  transform-origin: 0px 0px;
}
.tile[data-sensor="barometer"]:hover .baro-needle,
.tile[data-sensor="pressure"]:hover .baro-needle {
  animation: vane-needle-fast 0.6s ease-in-out infinite !important;
}
@keyframes vane-needle-fast {
  0%, 100% { transform: rotate(15deg); }
  25% { transform: rotate(-25deg); }
  75% { transform: rotate(35deg); }
}

/* Rain / Precipitation: accelerated rainfall flow on hover */
.tile[data-sensor="rain"]:hover .rain-streaks line {
  stroke-width: 3.2px;
  filter: drop-shadow(0 0 3px #38bdf8);
}

/* Cloud base: ceilometer beam intensifies and scans on hover */
.tile[data-sensor="cloudbase"]:hover .cb-beam {
  filter: drop-shadow(0 0 5px #38bdf8);
}

/* Evapotranspiration: vapor accelerates upward on hover */
.et-vapor {
  transition: transform 0.3s ease;
}
.tile[data-sensor="ET"]:hover .et-vapor {
  animation: vane-vapor-rise 0.8s ease-in-out infinite !important;
}
@keyframes vane-vapor-rise {
  0%, 100% { transform: translateY(0); opacity: 0.85; }
  50% { transform: translateY(-4px); opacity: 1; filter: drop-shadow(0 0 4px #38bdf8); }
}

/* Rain / Precipitation: gentle drop slide */
.tile[data-sensor="rain"]:hover .tile-icon,
.tile[data-sensor="rainRate"]:hover .tile-icon {
  animation: vane-rain 1s ease-in-out infinite;
}
@keyframes vane-rain {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(3px) scale(0.96, 1.04); }
}

/* Solar Radiation & UV: radiant sun pulse */
.tile[data-sensor="radiation"]:hover .tile-icon,
.tile[data-sensor="UV"]:hover .tile-icon {
  animation: vane-sun-pulse 2s ease-in-out infinite;
}
@keyframes vane-sun-pulse {
  0%, 100% { transform: scale(1) rotate(0deg); filter: drop-shadow(0 0 0px transparent); }
  50% { transform: scale(1.15) rotate(15deg); filter: drop-shadow(0 0 8px rgba(245, 158, 11, 0.6)); }
}

/* Lightning / Storm: rapid electrical twitch & glow */
.tile[data-sensor="lightning"]:hover .tile-icon {
  animation: vane-lightning 0.75s ease-in-out infinite;
}
@keyframes vane-lightning {
  0%, 100% { transform: scale(1) rotate(0deg); filter: drop-shadow(0 0 0px transparent); }
  15% { transform: scale(1.18) rotate(-7deg); filter: drop-shadow(0 0 8px rgba(124, 92, 255, 0.8)); }
  30% { transform: scale(1.05) rotate(5deg); }
  45% { transform: scale(1.22) rotate(-4deg); filter: drop-shadow(0 0 10px rgba(124, 92, 255, 0.9)); }
  60% { transform: scale(1) rotate(0deg); filter: drop-shadow(0 0 0px transparent); }
}

/* Sun / Sunrise: warm rising and radiant pulse */
.tile[data-sensor="sun"]:hover .tile-icon {
  animation: vane-sun-rise 2s ease-in-out infinite;
}
@keyframes vane-sun-rise {
  0%, 100% { transform: translateY(0) scale(1); filter: drop-shadow(0 0 0px transparent); }
  50% { transform: translateY(-3px) scale(1.12); filter: drop-shadow(0 0 8px rgba(245, 158, 11, 0.5)); }
}

/* Cloudbase & ET / Fog: gentle floating drift */
.tile[data-sensor="cloudbase"]:hover .tile-icon,
.tile[data-sensor="ET"]:hover .tile-icon {
  animation: vane-cloud-drift 2.4s ease-in-out infinite;
}
@keyframes vane-cloud-drift {
  0%, 100% { transform: translateX(0) scale(1); }
  50% { transform: translateX(3px) scale(1.08); }
}

/* Thermometer / Temperature: gentle pulse */
.tile[data-sensor="outTemp"]:hover .tile-icon {
  animation: vane-temp-pulse 1.8s ease-in-out infinite;
}
@keyframes vane-temp-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}

/* Hero Weather Condition: floating breathing effect */
.hero-condition-icon {
  display: inline-block;
  vertical-align: middle;
  transition: transform .4s ease;
  animation: vane-float 4.5s ease-in-out infinite;
}
@keyframes vane-float {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-3px) rotate(2deg); }
}

@media (prefers-reduced-motion: reduce) {
  .tile[data-sensor]:hover .tile-icon,
  .hero-condition-icon,
  .season-icon {
    animation: none !important;
    transform: none !important;
  }
}

.tile-value {
  display: flex;
  align-items: baseline;
  gap: 5px;
}
.tile-value .num {
  font-size: 30px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.tile-value .unit {
  font-size: 13px;
  line-height: 1;
  color: var(--text-muted);
}

.tile-sub {
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--text-muted);
}

.tile-bar {
  height: 4px;
  border-radius: 4px;
  background: var(--surface-2);
  overflow: hidden;
  margin-top: auto;
}
.tile-bar > span {
  display: block;
  height: 100%;
  border-radius: 4px;
}

/* ---------------------------------------------------------------------------
   Semicircular gauge (Vocht-tegel e.a.) — value arc pre-computed in
   vane_extras.py (_gauge_arc, same "trig belongs in Python, not Cheetah"
   principle as the windrose/wind-vector). The background track is a fixed
   path (always the same full semicircle, radius doesn't change) so it's
   just hardcoded in the template rather than recomputed per tile.
   --------------------------------------------------------------------------- */
.gauge-svg {
  width: 100%;
  max-width: 160px;
  height: auto;
  display: block;
  margin: 2px auto -6px;
  overflow: visible;
}
.gauge-track {
  fill: none;
  stroke: var(--surface-2);
  stroke-width: 10;
  stroke-linecap: round;
}
/* Today's min-max band (vane_gauge_range_band) drawn as a blue segment
   on top of the neutral gray .gauge-track — the track stays the fixed,
   full-scale context (e.g. 0-100% humidity), the band shows where
   today's actual range sits within it. */
.gauge-band {
  fill: none;
  stroke: var(--accent-primary);
  stroke-width: 10;
  stroke-linecap: round;
  opacity: .55;
}
.gauge-value {
  fill: none;
  stroke-width: 10;
  stroke-linecap: round;
  transition: d .3s ease;
}

/* Straight radial tick/needle (vane_gauge_tick) — thinner than
   .gauge-value on purpose: a clean position marker crossing the track,
   not a thick blob riding on top of it. */
.gauge-tick {
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  transition: d .3s ease;
}

.tile-range {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  margin-top: auto;
}

.tile-stats-stacked {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11.5px;
  margin-top: auto;
  padding-top: 4px;
}
.tile-stats-stacked .stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  color: var(--text-muted);
}
.tile-stats-stacked .stat-val {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: var(--text);
}

.tile-spark {
  width: 100%;
  height: 28px;
  display: block;
  overflow: visible;
  margin-top: auto;
}

.tile-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-muted);
  margin-top: auto;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
  align-self: flex-start;
}

.tiles-grid {
  flex: 1.45 1 420px;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
}

.card > .tiles-grid {
  flex: none;
  width: 100%;
}

.hero-card {
  flex: 1 1 340px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 18px;
}

.hero-value {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}
.hero-value .num {
  font-size: clamp(64px, 8vw, 88px);
  line-height: .9;
  font-weight: 600;
  letter-spacing: -.045em;
  font-variant-numeric: tabular-nums;
}
.hero-value .unit {
  font-size: 24px;
  font-weight: 500;
  color: var(--text-muted);
  margin-top: 6px;
}

.hero-meta {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px 14px;
}
@media (max-width: 640px) {
  .hero-meta {
    grid-template-columns: repeat(2, 1fr);
  }
}
.hero-meta .k {
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.hero-meta .v {
  font-size: 15px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.records-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
}
.records-row {
  display: grid;
  grid-template-columns: 1fr auto 44px;
  align-items: baseline;
  gap: 8px;
}
.records-row .r-val {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.records-row .r-date {
  font-size: 12px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.empty-state {
  padding: 24px;
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
}

.row-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.uppercase-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.archive-table tbody tr:nth-child(even) {
  background: var(--surface-2);
}
.archive-table .range-bar {
  position: relative;
  height: 6px;
  border-radius: 6px;
  background: var(--surface-2);
  min-width: 90px;
}
.archive-table .range-bar > span {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 6px;
  /* background is set inline per row — color = absolute temperature scale
     (vane_temp_color), not month-relative */
}

/* ---------------------------------------------------------------------------
   Windrose (Archive page) — sector wedges are pre-computed SVG paths from
   vane_extras.py (trig doesn't belong in Cheetah), this is just the static
   ring/label chrome around them.
   --------------------------------------------------------------------------- */
.windrose-svg {
  width: 100%;
  max-width: 260px;
  height: auto;
  display: block;
  margin: 4px auto;
}
.windrose-ring {
  fill: none;
  stroke: var(--border);
}
.windrose-label,
.windrose-label-value {
  font-family: var(--font-mono);
  text-anchor: middle;
  /* Halo behind the glyphs (stroke painted under the fill) so labels
     stay legible where a wedge/polygon passes directly behind them,
     instead of just hoping text-muted has enough contrast. */
  paint-order: stroke;
  stroke: var(--bg);
  stroke-width: 4px;
  stroke-linejoin: round;
}
.windrose-label {
  font-size: 11px;
  font-weight: 600;
  fill: var(--text);
}
.windrose-label-value {
  font-size: 9px;
  fill: var(--text-muted);
}
.windrose-svg path[data-tooltip] {
  cursor: default;
  stroke: var(--bg);
  stroke-width: 1;
  transition: opacity .12s;
}
.windrose-svg path[data-tooltip]:hover {
  opacity: 1 !important;
}

/* ---------------------------------------------------------------------------
   Wind vector radar (Dashboard) — avg speed vs avg gust per 8 main compass
   points, overlaid as two polygons (points pre-computed in vane_extras.py,
   same "no chart-library" approach as the windrose above).
   --------------------------------------------------------------------------- */
.windvector-polygon {
  fill-opacity: 0.18;
  stroke-width: 2;
}
.windvector-legend {
  display: flex;
  justify-content: center;
  gap: 16px;
  font-size: 12.5px;
  color: var(--text-muted);
}
.windvector-legend .dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
}
.windvector-marker {
  stroke: var(--bg);
  stroke-width: 1.5;
  cursor: default;
  transition: r .12s;
}
.windvector-marker:hover {
  r: 6;
}

/* ---------------------------------------------------------------------------
   Shared hover tooltip for the windrose/wind-vector SVG charts — a single
   floating element (static/js/chart-tooltip.js) positioned near the
   cursor, since native SVG <title> tooltips are slow/unstyled and easy
   to mistake for "hover does nothing".
   --------------------------------------------------------------------------- */
.vane-chart-tooltip {
  position: fixed;
  z-index: 50;
  pointer-events: none;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--text);
  box-shadow: 0 8px 24px rgba(2, 6, 23, .35);
  opacity: 0;
  transform: translateY(2px);
  transition: opacity .1s, transform .1s;
  white-space: nowrap;
}
.vane-chart-tooltip.visible {
  opacity: 1;
  transform: translateY(0);
}

/* ---------------------------------------------------------------------------
   Rain 7-day chart (Dashboard)
   --------------------------------------------------------------------------- */
.rain-week-chart {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  height: 96px;
  margin-top: 8px;
}
.rain-week-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  height: 100%;
  min-width: 0;
}
.rain-week-val {
  font-size: 10px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  line-height: 1;
  text-align: center;
}
.rain-week-val.is-zero {
  visibility: hidden;
}
.rain-week-bar-wrap {
  width: 100%;
  flex: 1;
  display: flex;
  align-items: flex-end;
}
.rain-week-bar {
  width: 100%;
  min-height: 2px;
  border-radius: 3px 3px 0 0;
  transition: height .2s ease;
}
.rain-week-col .tile-sub.is-today {
  color: var(--accent-primary);
  font-weight: 700;
}

/* ---------------------------------------------------------------------------
   Rain calendar heatmap (Archive page) — grid-column-start is set inline
   only on the month's first day cell (vane_weekday()), CSS auto-flow
   handles the rest of the weeks on its own.
   --------------------------------------------------------------------------- */
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
  margin-top: 10px;
}
.cal-head {
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding-bottom: 2px;
}
.cal-cell {
  aspect-ratio: 1;
  border-radius: 7px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 2px 1px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  box-sizing: border-box;
}
.cal-cell .cal-date {
  font-size: 11px;
  font-weight: 500;
  line-height: 1.1;
}
.cal-cell .cal-val {
  font-size: 8.5px;
  font-weight: 700;
  line-height: 1;
  opacity: 0.95;
}
/* Marks "today" independent of the rain-intensity fill color, so a
   colored (rained) cell that happens to be today isn't ambiguous with a
   plain "selected" look — the ring says "today", the fill says "rain". */
.cal-cell.is-today {
  outline: 2px solid var(--accent-primary);
  outline-offset: -2px;
}

.vane-modal {
  /* The browser's own default (dialog:not([open]) { display: none }) is
     what actually hides this outside of showModal() — this rule must
     only take over layout for the open state, or it overrides that
     default and the dialog renders as permanent inline content at the
     bottom of the page instead of a hidden overlay. */
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg);
  color: var(--text);
  padding: 0;
  /* Wide enough for a full NOAA fixed-width line at the modal's 12px
     monospace font, so the horizontal scrollbar only shows up on
     genuinely narrow (mobile) viewports instead of every time. */
  width: min(840px, 95vw);
  max-height: 80vh;
  box-shadow: 0 20px 60px rgba(2, 6, 23, .45);
}
.vane-modal[open] {
  display: flex;
}
.vane-modal::backdrop {
  background: rgba(2, 6, 23, .6);
  backdrop-filter: blur(2px);
}
.vane-modal header {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-muted);
}
.vane-modal header button {
  all: unset;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
  color: var(--text-muted);
  padding: 2px 6px;
  border-radius: 6px;
}
.vane-modal header button:hover {
  background: var(--surface-2);
  color: var(--text);
}
.vane-modal pre {
  flex: 1;
  min-height: 0;
  margin: 0;
  padding: 18px;
  overflow: auto;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  white-space: pre;
}

.vane-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px;
  padding-top: 6px;
  font-size: 12px;
  color: var(--text-muted);
}
.vane-footer a {
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.15s ease;
}
.vane-footer a:hover {
  color: var(--accent-primary);
  text-decoration: underline;
}

.vane-footer-wide {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  padding: 20px 22px;
  border-radius: var(--radius-card);
  background: var(--surface);
  border: 1px solid var(--border);
}
.vane-footer-wide .footer-col {
  flex: 1 1 200px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.vane-footer-wide .footer-lines {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  color: var(--text-muted);
}
.vane-footer-wide .footer-lines a {
  color: var(--text-muted);
}
.vane-footer-wide .footer-lines a:hover {
  color: var(--accent-primary);
}
