/* ======================================================================
   Погода — светлый «небесный» интерфейс
   Живой фон, управляемый текущей погодой; данные плотные и читаемые.
   ====================================================================== */

:root {
  color-scheme: light;

  /* Base palette — calm paper-slate (deliberately de-blued: less "gismeteo") */
  --bg-0: oklch(97.8% 0.005 255);
  --bg-1: oklch(99.3% 0.003 255);
  --ink: oklch(24% 0.03 264);
  --muted: oklch(45% 0.024 262);
  --soft: oklch(52% 0.02 262);
  --faint: oklch(58% 0.02 262);

  /* Glass surfaces (white translucency on a soft sky) */
  --glass: oklch(100% 0 0 / 0.58);
  --glass-2: oklch(100% 0 0 / 0.8);
  --glass-strong: oklch(100% 0 0 / 0.74);
  --line: oklch(48% 0.025 262 / 0.12);
  --line-strong: oklch(42% 0.035 262 / 0.2);

  /* Accents — confident indigo (not bright sky-blue) + warm amber counterpoint */
  --accent: oklch(51% 0.16 264);
  --accent-warm: oklch(67% 0.14 52);
  --accent-soft: oklch(51% 0.16 264 / 0.11);

  --ok: oklch(56% 0.13 162);
  --warn: oklch(68% 0.14 64);
  --danger: oklch(56% 0.18 25);
  --ok-ink: oklch(48% 0.13 162);
  --warn-ink: oklch(50% 0.13 64);

  /* Weather icon colors (work on light surfaces) */
  --ico-sun: oklch(72% 0.16 70);
  --ico-moon: oklch(62% 0.05 255);
  --ico-cloud: oklch(74% 0.03 250);
  --ico-cloud-dark: oklch(58% 0.04 252);
  --ico-rain: oklch(60% 0.14 240);
  --ico-snow: oklch(72% 0.1 235);
  --ico-bolt: oklch(70% 0.16 75);

  /* Precipitation intensity ramp — shared by the hourly chart bars and legend */
  --rain-light: oklch(77% 0.075 236);
  --rain-mod: oklch(62% 0.13 240);
  --rain-heavy: oklch(50% 0.16 250);

  /* Dynamic sky — overwritten per-condition by JS via body[data-sky] */
  --sky-top: oklch(88% 0.06 235);
  --sky-bottom: oklch(98% 0.012 235);
  --sky-glow: oklch(90% 0.09 95 / 0.55);
  --sky-glow-2: oklch(85% 0.08 230 / 0.45);

  --shadow-lift: 0 20px 45px -28px oklch(45% 0.06 255 / 0.45);
  --radius: 16px;
  --radius-sm: 11px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  --font-display: "Fraunces", "Fraunces Fallback", ui-serif, Georgia, serif;
  --font-body: "Geist", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", monospace;

  font-family: var(--font-body);
}

@font-face {
  font-family: "Fraunces Fallback";
  src: local("Georgia");
  size-adjust: 94%;
  ascent-override: 95%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* Condition-driven sky tints (set on <body data-sky data-daytime>) */
body[data-sky="clear"][data-daytime="day"] {
  --sky-top: oklch(82% 0.1 230);
  --sky-glow: oklch(92% 0.1 95 / 0.7);
}
body[data-sky="partly"] { --sky-top: oklch(86% 0.06 232); }
body[data-sky="cloudy"],
body[data-sky="fog"] {
  --sky-top: oklch(88% 0.02 250);
  --sky-glow: oklch(90% 0.02 250 / 0.4);
  --sky-glow-2: oklch(85% 0.02 250 / 0.3);
}
body[data-sky="rain"],
body[data-sky="drizzle"],
body[data-sky="thunder"] {
  --sky-top: oklch(80% 0.04 245);
  --sky-glow: oklch(82% 0.05 240 / 0.45);
  --sky-glow-2: oklch(78% 0.05 250 / 0.4);
}
body[data-sky="snow"],
body[data-sky="sleet"] {
  --sky-top: oklch(90% 0.02 235);
  --sky-glow: oklch(96% 0.02 235 / 0.6);
}
body[data-daytime="night"] {
  --sky-top: oklch(58% 0.07 262);
  --sky-bottom: oklch(90% 0.02 250);
  --sky-glow: oklch(70% 0.07 280 / 0.4);
  --sky-glow-2: oklch(72% 0.06 230 / 0.35);
}

* {
  box-sizing: border-box;
}

html {
  background: var(--bg-0);
  /* Резервируем место под вертикальный скроллбар всегда. Иначе на десктопе с
     классическим (не overlay) скроллбаром высокие вкладки (Сейчас/Анализ) имеют
     полосу, а короткие (Карта/Прогноз) — нет, и при переключении ширина контента
     прыгала (~15px). Со stable ширина консистентна на всех вкладках. */
  scrollbar-gutter: stable;
}

body {
  min-width: 320px;
  margin: 0;
  color: var(--ink);
  background: var(--bg-0);
  font-size: 0.9rem;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* ----------------------------------------------------- App loader (анти-CLS) */
/* Полноэкранный лоадер на время первой загрузки — прикрывает смену шрифтов в
   шапке и переток подвала/контента. Стили грузятся до первого кадра (styles.css
   render-blocking), поэтому лоадер виден сразу. Снимается классом body.app-loaded. */
#appLoader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background: var(--bg-0);
  will-change: opacity;
  /* Плавное исчезновение ~0.5с (на композиторе, без рывков). Контент под лоадером
     уже стабилен (ждём свежие данные), поэтому развёртка ровная. */
  transition: opacity 500ms var(--ease), visibility 500ms var(--ease);
}
/* will-change:auto after the fade releases the compositor layer the loader held
   for its opacity transition — no reason to keep it alive for the whole session. */
body.app-loaded #appLoader { opacity: 0; visibility: hidden; pointer-events: none; will-change: auto; }
.loader-card { display: grid; justify-items: center; gap: 10px; }
.loader-weather { width: 96px; height: auto; }
.lw-rays { transform-box: view-box; transform-origin: 46px 46px; animation: lwSpin 9s linear infinite; }
.lw-cloud { animation: lwBob 3s ease-in-out infinite; }
.lw-drop { animation: lwRain 1.1s ease-in infinite; }
.lw-drop2 { animation-delay: 0.27s; }
.lw-drop3 { animation-delay: 0.54s; }
@keyframes lwSpin { to { transform: rotate(360deg); } }
@keyframes lwBob { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } }
@keyframes lwRain { 0% { opacity: 0; transform: translateY(-4px); } 35% { opacity: 1; } 100% { opacity: 0; transform: translateY(9px); } }
@media (prefers-reduced-motion: reduce) {
  .lw-rays, .lw-cloud, .lw-drop { animation: none; }
}

/* ---------------------------------------------------------------- Sky */
.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: linear-gradient(180deg, var(--sky-top), var(--sky-bottom) 70%);
  transition: background 1200ms var(--ease);
}

.sky__mesh,
.sky__aurora {
  position: absolute;
  inset: -20%;
  transition: opacity 1200ms var(--ease);
}

.sky__mesh {
  background:
    radial-gradient(40% 50% at 18% 12%, var(--sky-glow), transparent 70%),
    radial-gradient(45% 55% at 85% 8%, var(--sky-glow-2), transparent 72%),
    radial-gradient(60% 60% at 60% 100%, oklch(40% 0.06 250 / 0.5), transparent 70%);
  filter: blur(8px);
  animation: drift 28s ease-in-out infinite alternate;
}

.sky__aurora {
  background:
    conic-gradient(from 120deg at 30% 30%, transparent, var(--sky-glow) 25%, transparent 50%),
    conic-gradient(from 300deg at 75% 20%, transparent, var(--sky-glow-2) 30%, transparent 55%);
  opacity: 0.6;
  filter: blur(40px);
  animation: drift 36s ease-in-out infinite alternate-reverse;
}

.sky__grain {
  position: absolute;
  inset: 0;
  opacity: 0.4;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

@keyframes drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1.02); }
  to { transform: translate3d(3%, 2%, 0) scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .sky__mesh,
  .sky__aurora { animation: none; }
}

/* Live precipitation overlay (drops/flakes injected by JS) */
.sky__precip {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.sky__precip i {
  position: absolute;
  top: -8%;
  display: block;
  will-change: transform;
}

.sky__precip i.drop {
  width: 1.5px;
  height: 18px;
  background: linear-gradient(transparent, oklch(85% 0.05 220 / 0.55));
  animation: fall linear infinite;
}

.sky__precip i.flake {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: oklch(96% 0.01 240 / 0.7);
  animation: flutter linear infinite;
}

@keyframes fall {
  to { transform: translateY(112vh); }
}

@keyframes flutter {
  to { transform: translateY(112vh) translateX(22px); }
}

/* ------------------------------------------------------------- Layout */
.app-shell {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: min(1020px, calc(100% - 36px));
  margin: 0 auto;
  /* Высота во весь экран (dvh — учитывает мобильные панели браузера), чтобы при
     коротком контенте (напр. заглушка карты) подвал прижимался к низу экрана. */
  min-height: 100vh;
  min-height: 100dvh;
  padding: 0 0 28px; /* top breathing room now lives in .app-header */
}
/* Consistent vertical rhythm comes from the flex gap above — drop ad-hoc margins. */
.app-shell > .overview-card,
.app-shell > .status-bar,
.app-shell > .api-panel { margin-top: 0; margin-bottom: 0; }
/* margin-top:auto «съедает» свободное место → подвал уезжает к низу экрана при
   коротком контенте, но остаётся под контентом на длинных вкладках. */
.app-shell > .app-foot { margin-top: auto; padding-top: 16px; }

/* Tab panels reuse the shell's vertical rhythm for the sections they hold. */
.tabpanel {
  display: flex;
  flex-direction: column;
  gap: 16px;
  animation: tabFade 280ms var(--ease, ease) both;
}
.tabpanel[hidden] { display: none; }
@keyframes tabFade {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .tabpanel { animation: none; }
}

button,
input {
  font: inherit;
}

button {
  cursor: pointer;
}

/* Kill the default square mobile tap-highlight on every interactive control —
   it overrode the rounded pills/tiles with a square box on tap (notably .tab).
   Was previously set only on .place-trigger. */
button, a, [role="tab"], [role="button"], summary, label {
  -webkit-tap-highlight-color: transparent;
}

h1, h2, p {
  margin: 0;
}

h1 {
  font-family: var(--font-body);
  font-size: 1.34rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  line-height: 1.05;
}

h2 {
  font-family: var(--font-body);
  font-size: 1.18rem;
  font-weight: 620;
  letter-spacing: -0.015em;
  line-height: 1.15;
}

code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--glass);
}

/* ----------------------------------------------------- Sticky header */
/* Topbar + tabbar travel together as one sticky unit. ВВЕРХУ страницы шапка
   ПРОЗРАЧНА — стеклянные topbar/таб-бар лежат прямо на «живом небе» (никакой
   подложки, поэтому нет светло-серых «уголков-призраков» в вырезах скруглений).
   При скролле под шапку заезжает контент и просвечивал бы сквозь стекло — поэтому
   маскирующая подложка (::before, near-white) ПЛАВНО ПОЯВЛЯЕТСЯ только когда
   страница прокручена (body.is-scrolled, ставит app.js). Над прокрученным
   контентом (не над небом) её углы не выделяются — призраков нет. */
.app-header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 12px;
  padding-bottom: 10px;
  margin-bottom: -10px; /* keep the shell's 16px rhythm to the next panel */
  background: none;
}
.app-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1; /* за topbar/таб-баром, но над прокручиваемым контентом */
  background: linear-gradient(var(--bg-0) 0%, var(--bg-0) 70%, transparent 100%);
  opacity: 0;
  transition: opacity 220ms var(--ease);
  pointer-events: none;
}
body.is-scrolled .app-header::before { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .app-header::before { transition: none; }
}

/* ------------------------------------------------------------- Topbar */
.topbar {
  position: relative;
  z-index: 3; /* lift above the sibling .tabbar so the location popup isn't trapped behind it */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 52px;
  padding: 8px 10px 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: var(--shadow-lift);
}

/* The title block doubles as the city-change control. */
.place-trigger {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  align-items: center;
  gap: 11px;
  padding: 5px 9px 5px 5px;
  margin: -4px -4px -4px -5px;
  border: 1px solid transparent;
  border-radius: 13px;
  background: transparent;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 180ms var(--ease), border-color 180ms var(--ease);
}
.place-trigger:hover {
  background: var(--glass);
  border-color: var(--line);
}
.place-trigger:focus-visible {
  outline: none;
  border-color: color-mix(in oklch, var(--accent), transparent 35%);
  background: var(--glass);
}
.place-trigger[aria-expanded="true"] {
  background: var(--glass);
  border-color: var(--line);
}

.place-trigger__name {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.place-trigger__name h1 {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.place-trigger__chev {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  stroke: var(--soft);
  stroke-width: 2.2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 220ms var(--ease), stroke 180ms var(--ease);
}
.place-trigger:hover .place-trigger__chev { stroke: var(--accent); }
.place-trigger[aria-expanded="true"] .place-trigger__chev { transform: rotate(180deg); }

.app-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: radial-gradient(circle at 35% 30%, var(--accent-soft), transparent 70%), var(--glass);
  border: 1px solid var(--line-strong);
}

.app-mark svg {
  width: 22px;
  height: 22px;
}

.app-mark__ring {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  opacity: 0.5;
}

.app-mark__needle {
  fill: var(--accent);
  transform-origin: 16px 16px;
  animation: needle 9s ease-in-out infinite;
}

.app-mark__hub {
  fill: var(--ink);
}

@keyframes needle {
  0%, 100% { transform: rotate(-18deg); }
  50% { transform: rotate(22deg); }
}

.product-title__text { min-width: 0; }

.eyebrow {
  display: block;
  /* Резервируем строку даже когда координаты ещё пустые (первый визит: boot.js
     ставит пустой eyebrow, город приходит по IP позже). Без min-height строка
     схлопывалась в 0, а затем «выталкивала» заголовок и всю шапку вниз при
     появлении координат — шапка с городом «скакала» при обновлении. */
  min-height: 0.95rem;
  margin: 0 0 3px;
  color: var(--soft);
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.section-kicker,
.status-label {
  display: block;
  margin: 0 0 6px;
  color: var(--soft);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.hero-actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 7px;
}

.icon-button,
.text-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 36px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  color: var(--ink);
  background: var(--glass);
  transition: border-color 200ms var(--ease), background-color 200ms var(--ease),
    color 200ms var(--ease), transform 160ms var(--ease);
}

.icon-button { width: 36px; padding: 0; }
.icon-button.small { min-height: 34px; width: 34px; }
.text-button { padding: 0 10px 0 11px; font-weight: 650; }
.text-button__icon { width: 17px; height: 17px; }
/* Chevron hints the location trigger opens a picker; muted so the pin leads. */
.text-button__chev { width: 14px; height: 14px; margin-left: -2px; color: var(--soft); }
.location-trigger { gap: 6px; }
.location-trigger[aria-expanded="true"] .text-button__chev { transform: rotate(180deg); }
.text-button__chev { transition: transform 200ms var(--ease); }

.icon-button svg,
.text-button svg {
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* All icon-button glyphs share one size so the gear/close/refresh match. */
.icon-button svg { width: 18px; height: 18px; }
.icon-button.small svg { width: 16px; height: 16px; }
.icon-refresh { width: 18px; height: 18px; }

.text-button.primary {
  color: oklch(18% 0.03 252);
  background: var(--accent);
  border-color: transparent;
  font-weight: 750;
}

.icon-button:hover,
.text-button:hover {
  background: var(--glass-2);
  border-color: color-mix(in oklch, var(--accent), transparent 55%);
  transform: translateY(-1px);
}

.text-button.primary:hover {
  color: oklch(18% 0.03 252);
  background: color-mix(in oklch, var(--accent), white 12%);
}

.icon-button:active,
.text-button:active { transform: translateY(0); }

.is-loading .icon-refresh { animation: spin 0.8s linear infinite; }

@keyframes spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------ Overview */
.overview-card {
  display: grid;
  grid-template-columns: minmax(0, 1.18fr) minmax(270px, 0.82fr);
  gap: 1px;
  /* margin-top:0 — как у первого .panel на остальных вкладках (Прогноз/Карта/
     Настройки). Раньше тут было 10px (+ парный сброс `.app-shell > .overview-card`),
     но после переноса карточки внутрь .tabpanel сброс перестал срабатывать и блок
     «Сейчас» оказывался на 10px ниже первых блоков других вкладок — контент прыгал
     по вертикали при переключении разделов. */
  margin-top: 0;
  position: relative;
  isolation: isolate; /* свой stacking-контекст: мини-небо под контентом карточки */
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--line);
  box-shadow: var(--shadow-lift);
}

/* «Живое мини-небо» карточки «Сейчас/Ближайшие 12 часов»: тот же язык, что и у
   фонового .sky — градиент/свечения едут за условием и днём/ночью через --sky-*,
   плюс температурный тон --atmo-temp. Лежит под контентом (z-index:0); панели
   полупрозрачны, поэтому атмосфера и осадки читаются, но текст остаётся контрастным. */
.overview-sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  border-radius: inherit;
  /* Мягкое угловое свечение — как исходный accent-soft из левого-верхнего угла,
     но цветами «живого неба» + тёплый/холодный тон по температуре. Только большие
     радиальные градиенты с плавным спадом в прозрачность — без линейной полосы
     «сверху вниз» и без бандинга. */
  /* Лучшие практики мягкого градиента: «источник света» смещён ВНУТРЬ от угла
     (а не приклеен к 0% 0% — иначе жёсткий блик-кольцо в углу), большой радиус и
     многоступенчатый спад (full → полупрозрачно → 0) для плавности без бандинга. */
  background:
    /* нейтральный почти-белый блик-источник света. Раньше тут было тёплое жёлтое
       свечение (--sky-glow) — оно сталкивалось с холодной синей дымкой и мутило
       переход (тёплый+холодный = серо-зелёная грязь в середине). Убрали; цвет
       теперь несут только температура и условие, в гармонии. */
    radial-gradient(150% 130% at 16% -12%,
      oklch(100% 0.01 95 / 0.55) 0%,
      oklch(100% 0.006 95 / 0.16) 36%,
      transparent 64%),
    /* температурный тон — широкий мягкий из верхне-правой зоны */
    radial-gradient(125% 120% at 104% 2%,
      var(--atmo-temp, transparent) 0%,
      color-mix(in oklch, var(--atmo-temp, transparent), transparent 60%) 40%,
      transparent 78%),
    /* условие-зависимая дымка (--sky-top): ясно→синева, облачно→серость,
       дождь→приглушённый синий, снег→светлое. Мягкий радиал сверху, без полосы. */
    radial-gradient(165% 135% at 50% -34%,
      color-mix(in oklch, var(--sky-top), transparent 64%) 0%,
      color-mix(in oklch, var(--sky-top), transparent 84%) 42%,
      transparent 74%),
    /* затемнение снизу по силе дождя/грозы (--atmo-storm, готовая oklch-строка из JS;
       прозрачно, если сухо/снег) — ливень/гроза делают карточку «грозовее». */
    radial-gradient(185% 140% at 50% 126%,
      var(--atmo-storm, transparent) 0%,
      color-mix(in oklch, var(--atmo-storm, transparent), transparent 55%) 45%,
      transparent 78%);
}
/* Дрейфующее холодное свечение неба поверх базовых угловых градиентов — лёгкая
   жизнь (тёплое свечение уже на базовом слое .overview-sky из левого-верхнего угла). */
.overview-sky::before {
  content: "";
  position: absolute;
  inset: -30%;
  background: radial-gradient(55% 65% at 88% 4%, var(--sky-glow-2), transparent 72%);
  /* без filter:blur — радиальные градиенты уже мягкие; экономим заливку/растеризацию
     на слабых устройствах. Анимация — только transform (дёшево на композиторе). */
  animation: drift 30s ease-in-out infinite alternate;
}
/* Вспышка для грозы — редкая и мягкая. */
.overview-sky::after {
  content: "";
  position: absolute;
  inset: 0;
  background: oklch(98% 0.02 250);
  opacity: 0;
  mix-blend-mode: soft-light;
}
body[data-sky="thunder"] .overview-sky::after { animation: atmoFlash 7s ease-out infinite; }
@keyframes atmoFlash {
  0%, 90%, 100% { opacity: 0; }
  91% { opacity: 0.55; }
  93% { opacity: 0.1; }
  94% { opacity: 0.6; }
  97% { opacity: 0; }
}

/* Осадки ПОВЕРХ контента карточки (дождь/снег; узлы добавляет render/skeletons.js
   → #overviewPrecip). z-index выше панелей (контент z1), чтобы анимация читалась;
   overflow:hidden карточки обрезает «хвосты», pointer-events:none — тапам не мешает. */
.overview-sky__precip {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  border-radius: inherit;
}
.overview-sky__precip i {
  position: absolute;
  top: -14px;
  display: block;
  will-change: transform;
}
.overview-sky__precip i.drop {
  width: 1.6px;
  height: 15px;
  background: linear-gradient(transparent, oklch(72% 0.07 235 / 0.7));
  animation: atmoFall linear infinite;
}
.overview-sky__precip i.flake {
  width: 4.5px;
  height: 4.5px;
  border-radius: 50%;
  background: oklch(98% 0.01 240 / 0.9);
  animation: atmoFlutter linear infinite;
}
@keyframes atmoFall { to { transform: translateY(400px); } }
@keyframes atmoFlutter { to { transform: translateY(400px) translateX(16px); } }

@media (prefers-reduced-motion: reduce) {
  .overview-sky::before,
  .overview-sky::after,
  .overview-sky__precip i { animation: none; }
}

.overview-main,
.advice-panel {
  min-width: 0;
  position: relative;
  z-index: 1; /* контент над мини-небом */
  padding: 18px 20px;
  /* Полупрозрачное стекло (без backdrop-filter — он дорогой и мерцал при смене
     вкладок): пропускает живое мини-небо/осадки карточки, оставляя текст читаемым.
     Стекло с blur оставляем только где осмысленно: topbar/табы/лист поиска/небо. */
  background: oklch(100% 0 0 / 0.55);
}

.overview-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "kicker icon"
    "temp icon"
    "summary summary";
  align-content: start;
  align-items: center;
  gap: 11px 18px;
  background:
    radial-gradient(105% 90% at 0% 0%, var(--accent-soft), transparent 56%),
    oklch(100% 0 0 / 0.55);
}

.now-head {
  display: contents;
}

.now-head .section-kicker {
  grid-area: kicker;
  margin: 0;
}

.temperature-row {
  grid-area: temp;
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-end;
  gap: 15px;
  min-width: 0;
}

.temperature {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-variant-numeric: tabular-nums lining-nums;
  font-size: 5.35rem;
  font-weight: 440;
  letter-spacing: -0.025em;
  line-height: 0.82;
  color: var(--ink);
}

.temperature-side {
  display: grid;
  gap: 5px;
  min-width: 130px;
  padding-bottom: 4px;
}

.weather-chip {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  min-height: 30px;
  padding: 4px 14px;
  border-radius: 999px;
  color: var(--ink);
  /* flat, in the style of the Сейчас tiles: soft blue tint, hairline border, no depth */
  background:
    linear-gradient(0deg, oklch(80% 0.08 245 / 0.16), oklch(80% 0.08 245 / 0.16)),
    var(--glass);
  border: 1px solid oklch(70% 0.08 248 / 0.32);
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: -0.005em;
}

.feels-line {
  color: var(--muted);
  font-size: 0.88rem;
  font-weight: 500;
}

.range-line {
  display: inline-flex;
  gap: 12px;
  font-variant-numeric: tabular-nums;
  font-size: 0.88rem;
  font-weight: 600;
}

.range-lo { color: var(--accent); }
.range-hi { color: var(--accent-warm); }

/* Wrapper around feels + hi/lo. Transparent by default so the desktop
   temperature-side grid treats them as direct children; the mobile layout
   turns it into a single wrapping figures line. */
.now-figures { display: contents; }

.summary {
  grid-area: summary;
  max-width: 70ch;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.42;
}
.summary[hidden] { display: none; }

/* Hero weather icon */
.weather-icon { display: inline-block; line-height: 0; }
.weather-icon--hero {
  grid-area: icon;
  display: grid;
  place-items: center;
  width: 96px;
  height: 96px;
  border-radius: 20px;
  background:
    radial-gradient(72% 72% at 50% 38%, color-mix(in oklch, var(--accent), transparent 86%), transparent 72%),
    var(--glass);
  border: 1px solid var(--line);
}
.weather-icon--hero svg { width: 76px; height: 76px; }

/* Advice + gauge */
.advice-panel {
  display: grid;
  /* Anchor content to the top (not centered): the kicker lines up with «Сейчас»
     on the left, and — crucially — text/AI-swap height changes grow downward
     instead of re-centering the whole block, which made it jump on refresh. */
  align-content: start;
  gap: 14px;
}

.advice-body {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  align-items: center;
  gap: 14px;
}

.advice-icon {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  font-size: 34px;
  line-height: 1;
  animation: advice-bob 3.2s ease-in-out infinite;
}
@keyframes advice-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .advice-icon { animation: none; }
}

.advice-text { min-width: 0; }
.advice-text h2 {
  margin-bottom: 5px;
  font-size: 1.08rem;
  line-height: 1.16;
}
.advice-text p {
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.38;
}

/* Скелетон ожидания ИИ (AI-first): фоллбек-текст лежит в узле как высота строки,
   но прячется под бегущий шиммер до ответа модели. Один и тот же приём для сводки
   12ч, текста карточек-рекомендаций, причины окна прогулки (is-loading на самом
   элементе) и причины спортивного окна (is-loading на чипе). */
.advice-text p.is-loading,
.insight-card__body p.is-loading,
.window-chip__reason.is-loading,
.window-chip.is-loading .window-chip__reason {
  color: transparent;
  border-radius: 6px;
  background: linear-gradient(
    90deg,
    color-mix(in oklch, var(--ink), transparent 90%) 25%,
    color-mix(in oklch, var(--ink), transparent 80%) 50%,
    color-mix(in oklch, var(--ink), transparent 90%) 75%
  );
  background-size: 200% 100%;
  animation: advice-shimmer 1.2s linear infinite;
}
@keyframes advice-shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .advice-text p.is-loading,
  .insight-card__body p.is-loading,
  .window-chip__reason.is-loading,
  .window-chip.is-loading .window-chip__reason { animation: none; }
}
.advice-text .ai-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 6px;
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--accent);
  background: var(--accent-soft);
  vertical-align: 1px;
}
.advice-text .ai-badge svg {
  width: 11px;
  height: 11px;
  fill: currentColor;
}

/* Появление ИИ-текста на месте скелетона/фоллбека: лёгкий подъём с лёгкой
   расфокусировкой → в резкость. Применяется к обёртке-span в сводке 12ч и в
   окне спорта, а также к новому флагу ИИ-сверки. Бейдж «ИИ» всплывает следом,
   как подпись под готовым текстом. Триггерится самим фактом вставки нового
   элемента — повторного запуска вручную не требуется. */
/* Только transform+opacity — их анимирует композитор (плавно на iOS/Android).
   filter: blur() убран намеренно: на мобильных он вызывает рывки при каждом кадре. */
@keyframes ai-reveal {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ai-reveal { animation: ai-reveal 300ms var(--ease) both; }
/* Только span-обёртке нужен inline-block, чтобы сработал transform (на флаге
   .verify-flag он лишний — там блочный flex). */
span.ai-reveal { display: inline-block; }
@keyframes ai-badge-in { from { opacity: 0; } to { opacity: 1; } }
.advice-text .ai-badge { animation: ai-badge-in 320ms var(--ease) 240ms both; }
@media (prefers-reduced-motion: reduce) {
  .ai-reveal,
  .advice-text .ai-badge { animation: none; }
}

/* ----------------------------------------------------------- Statusbar */
.status-bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin: 0;
}
.status-bar .summary {
  flex-basis: 100%;
  order: -1;
  margin: 0 0 2px;
  text-align: center;
  font-size: 0.78rem;
  color: var(--muted);
}

.status-cell {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  min-height: 26px;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
}

.status-cell strong {
  display: inline-flex;
  align-items: center;
  overflow: hidden;
  font-size: 0.72rem;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.status-cell .status-label {
  margin: 0;
  font-size: 0.62rem;
  letter-spacing: 0.05em;
}

.confidence {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.confidence__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--soft);
  box-shadow: 0 0 0 0 currentColor;
}

.confidence[data-tone="ok"] { color: var(--ok-ink); }
.confidence[data-tone="warn"] { color: var(--warn-ink); }
.confidence[data-tone="danger"] { color: var(--danger); }
.confidence[data-tone] .confidence__dot {
  background: currentColor;
  animation: pulse 2.4s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, currentColor, transparent 40%); }
  50% { box-shadow: 0 0 0 5px transparent; }
}

/* ------------------------------------------------------------- Content */
.content-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  min-width: 0;
}

.left-column,
.right-column {
  display: grid;
  align-content: start;
  gap: 10px;
  min-width: 0;
}

.panel {
  min-width: 0;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass-strong);
  box-shadow: var(--shadow-lift);
}

.panel-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-bottom: 14px;
}

.source-pill {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding: 0 11px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--muted);
  background: var(--glass);
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 500;
  max-width: 100%;
}

/* Hourly combined chart (temperature line + precipitation bars + cards) */
#forecastPanel {
  container: forecast / inline-size;
}

/* Hourly chart — single SVG: temperature spline + area, precip bars,
   icon overlay, time axis. Scrolls horizontally as one unit. */
.hchart {
  width: 100%;
  margin-bottom: 6px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.hchart::-webkit-scrollbar { height: 7px; }
.hchart::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 99px; }

.hchart-inner { position: relative; min-height: 244px; }
.hchart-svg { display: block; }

.hchart-icons { position: absolute; inset: 0; pointer-events: none; }
.hchart-ico {
  position: absolute;
  top: 8px;
  transform: translateX(-50%);
  line-height: 0;
}
.hchart-ico svg { width: 100%; height: 100%; }

/* SVG primitives */
.hchart-axis { stroke: var(--line); stroke-width: 1; }
/* Лёгкие разделители между часами. */
.hchart-div { stroke: var(--line); stroke-width: 1; opacity: 0.5; }
/* «Ощущается» — пунктирная линия по той же шкале, что и температура. */
.hchart-feels {
  fill: none;
  stroke: var(--accent-warm, oklch(72% 0.14 50));
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 4 4;
  opacity: 0.85;
}
.hchart-feels-val {
  fill: var(--accent-warm, oklch(64% 0.14 50));
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.hchart-now {
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 2 4;
  opacity: 0.4;
}
.hchart-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.hchart-dot {
  fill: var(--bg-1);
  stroke: var(--accent);
  stroke-width: 2;
}
.hchart-dot.is-now { fill: var(--accent); }
.hchart-temp {
  fill: var(--ink);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 760;
  font-variant-numeric: tabular-nums;
}
.hchart-temp.is-now { fill: var(--accent); }
.hchart-bar rect {
  fill: var(--rain-light);
  transform-box: fill-box;
  transform-origin: bottom;
}
.hchart-bar[data-i="mod"] rect { fill: var(--rain-mod); }
.hchart-bar[data-i="heavy"] rect { fill: var(--rain-heavy); }
.hchart-bar-val {
  fill: var(--soft);
  font-family: var(--font-body);
  font-size: 10.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.hchart-time {
  fill: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.hchart-time.is-now { fill: var(--accent); font-weight: 600; }


.forecast-brief {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  overflow: hidden;
  margin: 0 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
}

.forecast-stat {
  min-width: 0;
  padding: 10px 12px;
  border-left: 1px solid var(--line);
}

.forecast-stat:first-child { border-left: 0; }

.forecast-stat__label {
  display: block;
  color: var(--soft);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.forecast-stat__value {
  display: block;
  margin-top: 3px;
  overflow: hidden;
  color: var(--ink);
  font-size: 0.98rem;
  font-weight: 760;
  font-variant-numeric: tabular-nums;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.forecast-stat__sub {
  display: block;
  margin-top: 2px;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.74rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@container forecast (max-width: 720px) {
  #forecastPanel .panel-heading {
    align-items: stretch;
  }

  #forecastPanel .segmented {
    width: 100%;
  }
  #forecastPanel .segmented button {
    min-height: 38px;
    font-size: 0.9rem;
  }

  .chart-legend {
    gap: 6px 10px;
    margin-bottom: 10px;
  }

  .chart-legend__sep {
    display: none;
  }
}

/* Chart legend — makes the line/precip encoding self-explanatory. */
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 10px;
  margin: -5px 2px 10px;
  font-size: 0.68rem;
  color: var(--muted);
}
.chart-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.chart-legend__item::before {
  content: "";
  flex: 0 0 auto;
}
.chart-legend__item--line::before {
  width: 16px;
  height: 0;
  border-top: 2.5px solid var(--accent);
  border-radius: 2px;
}
.chart-legend__item--dash::before {
  width: 16px;
  height: 0;
  border-top: 2px dashed var(--accent-warm);
}
.chart-legend__item--bar::before {
  width: 8px;
  height: 10px;
  border-radius: 3px 3px 1px 1px;
}
.chart-legend__item--bar[data-i="light"]::before { background: var(--rain-light); }
.chart-legend__item--bar[data-i="mod"]::before { background: var(--rain-mod); }
.chart-legend__item--bar[data-i="heavy"]::before { background: var(--rain-heavy); }
.chart-legend__unit {
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--soft);
}
.chart-legend__sep {
  width: 1px;
  align-self: stretch;
  min-height: 14px;
  background: var(--line);
}
@media (max-width: 620px) {
  .chart-legend__sep { display: none; }
}


/* Daily — shared-scale temperature range bars (7-day) */
.daily-list { display: grid; gap: 4px; }

.daily-item {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) 2.4em minmax(96px, 1.4fr) 2.4em auto;
  align-items: center;
  gap: 14px;
  min-height: 52px;
  padding: 9px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
  transition: border-color 200ms var(--ease), background 200ms var(--ease);
}

.daily-item:hover { border-color: var(--line-strong); background: var(--glass-2); }

.daily-item.is-today {
  border-color: color-mix(in oklch, var(--accent), transparent 72%);
  background: var(--accent-soft);
}

.daily-day {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.daily-day .weather-icon svg { width: 40px; height: 40px; flex: 0 0 auto; }

.daily-day-text { min-width: 0; }
.daily-day-text b {
  display: block;
  overflow: hidden;
  font-weight: 700;
  text-transform: capitalize;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.daily-day-sub {
  display: block;
  overflow: hidden;
  color: var(--soft);
  font-size: 0.78rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.daily-day-date { color: var(--soft); }
.daily-day-desc { color: var(--soft); }

.daily-temp {
  font-size: 0.96rem;
  font-weight: 760;
  font-variant-numeric: tabular-nums;
  text-align: center;
  white-space: nowrap;
}
.daily-temp--lo { color: var(--soft); }
.daily-temp--hi { color: var(--ink); }

/* shared-scale range track: gradient anchored to the whole scale, clipped
   to each day's min–max segment so colour reads as absolute temperature. */
.dchart-track {
  position: relative;
  height: 8px;
  border-radius: 99px;
  background: color-mix(in oklch, var(--ink), transparent 91%);
}
.dchart-track__freeze {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 1px;
  background: color-mix(in oklch, var(--rain-mod), transparent 30%);
  transform: translateX(-50%);
  z-index: 1;
}
.dchart-track__fill {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    oklch(58% 0.16 250),
    oklch(72% 0.13 200),
    oklch(82% 0.11 130),
    oklch(67% 0.14 52));
}

@container forecast (max-width: 720px) {
  /* Narrow panel: day + temps on the first row, the range bar gets its own
     full-width row, meta below. Day names never fight the bar for space. */
  .daily-item {
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    column-gap: 10px;
    row-gap: 7px;
    padding: 10px 14px;
  }
  .daily-day { grid-column: 1 / 3; grid-row: 1; }
  .daily-temp--lo { grid-column: 3; grid-row: 1; }
  .daily-temp--hi { grid-column: 4; grid-row: 1; }
  .dchart-track { grid-column: 1 / -1; grid-row: 2; }
  .daily-meta { grid-column: 1 / -1; grid-row: 3; }
  .daily-temp { font-size: 0.92rem; }
  .daily-day .weather-icon svg { width: 36px; height: 36px; }
  /* The icon already conveys conditions; drop the wordy description so the
     date never truncates in the narrow day column. */
  .daily-day-desc { display: none; }
}

/* Daily meta: precip + UV + wind chips */
.daily-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px 10px;
}
.daily-meta .rain,
.daily-meta .wind {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.daily-meta .meta-ico {
  width: 13px;
  height: 13px;
  stroke: currentColor;
  stroke-width: 1.9;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.7;
}
.daily-meta .rain { color: var(--rain-light); font-weight: 650; }
.daily-meta .rain[data-i="mod"] { color: var(--rain-mod); }
.daily-meta .rain[data-i="heavy"] { color: var(--rain-heavy); }
.daily-meta .rain[data-i="dry"] { color: var(--soft); }
.daily-meta .wind { color: var(--soft); }

.uv-pill {
  --uv: var(--ok);
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 999px;
  color: var(--uv);
  background: color-mix(in oklch, var(--uv), transparent 86%);
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.uv-pill[data-lvl="low"] { --uv: var(--ok); }
.uv-pill[data-lvl="mod"] { --uv: var(--warn); }
.uv-pill[data-lvl="high"] { --uv: oklch(64% 0.17 45); }
.uv-pill[data-lvl="vhigh"] { --uv: var(--danger); }
.uv-pill[data-lvl="extreme"] { --uv: oklch(52% 0.2 320); }

@container forecast (max-width: 720px) {
  .forecast-brief {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .forecast-stat:nth-child(odd) {
    border-left: 0;
  }

  .forecast-stat:nth-child(n + 3) {
    border-top: 1px solid var(--line);
  }
}

/* ------------------------------------------------------- Details panel */
.details-panel {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass-strong);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}

.details-panel > summary {
  display: flex;
  min-height: 50px;
  align-items: center;
  padding: 0 16px;
  color: var(--ink);
  cursor: pointer;
  font-weight: 650;
  list-style: none;
}

.details-panel > summary::-webkit-details-marker { display: none; }
.details-panel > summary span { display: flex; align-items: center; gap: 10px; }

.summary__icon {
  width: 17px;
  height: 17px;
  stroke: var(--accent);
  stroke-width: 2.4;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 240ms var(--ease);
}

.details-panel[open] .summary__icon { transform: rotate(90deg); }
.details-panel[open] > summary { border-bottom: 1px solid var(--line); }

.details-panel > .panel {
  margin: 12px;
  box-shadow: none;
  background: var(--glass);
}

/* Facts grid */
.fact-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 9px;
}

.fact-item {
  position: relative;
  display: grid;
  gap: 4px;
  min-height: 78px;
  align-content: start;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
}

.fact-item[data-has-tip] { cursor: help; }
.fact-item[data-has-tip]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.fact-head {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--soft);
  font-size: 0.76rem;
  font-weight: 600;
}

.fact-label { flex: 1 1 auto; min-width: 0; }

.fact-head svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.8;
  flex: 0 0 auto;
}

.fact-val {
  display: flex;
  align-items: baseline;
  gap: 2px;
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.fact-sub {
  color: var(--soft);
  font-size: 0.76rem;
  font-weight: 500;
}

.fact-item .wind-arrow {
  display: inline-block;
  margin-left: 4px;
  font-size: 1rem;
  color: var(--accent);
  transition: transform 400ms var(--ease);
}

.fact-trend {
  margin-left: 4px;
  font-size: 1rem;
  font-weight: 800;
}
.fact-trend[data-dir="up"] { color: var(--ok-ink); }
.fact-trend[data-dir="down"] { color: var(--danger); }
.fact-trend[data-dir="steady"] { color: var(--soft); }

/* Cardinal wind direction shown before the speed in the wind tile */
.wind-dir {
  margin-right: 5px;
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--accent);
  font-variant-numeric: normal;
}

/* UV color scale with a marker at the current index */
.uv-bar {
  position: relative;
  display: block;
  height: 6px;
  margin-top: 7px;
  border-radius: 99px;
  background: linear-gradient(90deg,
    oklch(80% 0.16 145), oklch(85% 0.17 95) 38%, oklch(75% 0.17 60) 62%,
    oklch(63% 0.22 25) 83%, oklch(55% 0.2 310));
}
.uv-bar__marker {
  position: absolute;
  top: 50%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--ink);
  transform: translate(-50%, -50%);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

/* Cross-source agreement badge — the core "N/8" signal on every tile */
.agree-badge {
  flex: 0 0 auto;
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 2px 6px;
  border-radius: 99px;
  border: 1px solid var(--line-strong);
  color: var(--soft);
  background: var(--glass-2);
  line-height: 1;
}
.agree-badge[data-tone="ok"] { color: var(--ok-ink); border-color: color-mix(in oklch, var(--ok), transparent 55%); background: color-mix(in oklch, var(--ok), transparent 88%); }
.agree-badge[data-tone="warn"] { color: var(--warn-ink); border-color: color-mix(in oklch, var(--warn), transparent 55%); background: color-mix(in oklch, var(--warn), transparent 88%); }
.agree-badge[data-tone="danger"] { color: var(--danger); border-color: color-mix(in oklch, var(--danger), transparent 55%); background: color-mix(in oklch, var(--danger), transparent 88%); }

/* Rich per-tile tooltip: what it means + source spread */
.fact-tip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  --tip-shift: 0px;
  z-index: 30;
  width: max-content;
  max-width: 240px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--tooltip-bg, #1b2330);
  color: #eef2f8;
  border: 1px solid color-mix(in oklch, #ffffff, transparent 86%);
  box-shadow: 0 12px 30px rgba(8, 12, 20, 0.35);
  pointer-events: none;
  /* display:none (not visibility:hidden) so an off-screen hidden tip never
     expands the document and causes horizontal overflow on mobile. */
  display: none;
  gap: 6px;
}
.fact-item:hover .fact-tip,
.fact-item:focus-within .fact-tip,
.fact-item:focus-visible .fact-tip,
.astro-tile:hover .fact-tip,
.astro-tile:focus-within .fact-tip,
.astro-tile:focus-visible .fact-tip,
.aqi-card:hover .fact-tip,
.aqi-card:focus-within .fact-tip,
.aqi-card:focus-visible .fact-tip {
  display: grid;
  animation: tipFade 150ms var(--ease, ease) both;
}
@keyframes tipFade { from { opacity: 0; } to { opacity: 1; } }
.fact-tip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: calc(50% + var(--tip-shift));
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--tooltip-bg, #1b2330);
}
.fact-tip strong { font-size: 0.84rem; font-weight: 700; }
.fact-tip__what { font-size: 0.78rem; color: #c7d0de; line-height: 1.35; }
.fact-tip__range { font-size: 0.74rem; color: #aab6c8; }
.fact-tip__srcs { display: grid; gap: 2px; }
.fact-tip__src {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.74rem;
  font-variant-numeric: tabular-nums;
}
.fact-tip__src span { color: #aeb9ca; }
.fact-tip__src b { font-weight: 700; }
.fact-tip__agree { font-size: 0.74rem; font-weight: 700; }
.fact-tip__agree[data-tone="ok"] { color: #8fe3a9; }
.fact-tip__agree[data-tone="warn"] { color: #f3c87a; }
.fact-tip__agree[data-tone="danger"] { color: #f29b9b; }

@media (hover: none) {
  /* On touch, the tip would clip at screen edges; keep tap-to-focus but cap width */
  .fact-tip { max-width: min(240px, 70vw); }
}

/* Compare list — dot plots */
.compare-list { display: grid; gap: 4px; }

.compare-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 10px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
}

.compare-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.compare-head b { font-weight: 650; }

.compare-dots {
  position: relative;
  height: 18px;
  margin-top: 7px;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--glass-2), transparent 40%, transparent 60%, var(--glass-2));
}

.compare-dots .axis {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line-strong);
}

.compare-dots .dot {
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--accent);
  border: 1.5px solid var(--bg-1);
  box-shadow: 0 0 0 1px var(--line-strong);
}

.compare-dots .dot.median {
  width: 12px; height: 12px; margin: -6px 0 0 -6px;
  background: var(--ink);
  z-index: 2;
}

.compare-dots .dot.outlier { background: var(--danger); }

.compare-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  font-weight: 650;
  white-space: nowrap;
  text-align: right;
}

.compare-status[data-status="high"] { color: var(--ok-ink); }
.compare-status[data-status="medium"] { color: var(--muted); }
.compare-status[data-status="watch"] { color: var(--warn-ink); }
.compare-status[data-status="low"] { color: var(--danger); }
.compare-status[data-status="single"] { color: var(--soft); }

.compare-outlier {
  margin-top: 4px;
  color: var(--danger);
  font-size: 0.74rem;
}

/* Map */
.map-frame {
  overflow: hidden;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
}

.map-panel iframe {
  display: block;
  width: 100%;
  height: 240px;
  border: 0;
  filter: grayscale(0.3) brightness(0.85) contrast(1.05);
}

.map-caption {
  color: var(--soft);
  font-size: 0.86rem;
  line-height: 1.5;
}

.map-caption { margin-top: 12px; }

/* Заглушка «Карта в разработке» (карта временно отключена, MAP_ENABLED=false). */
.map-wip {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  padding: 18px 16px 26px;
}
.map-wip__art {
  width: min(280px, 80%);
  height: auto;
  margin-bottom: 6px;
}
@media (prefers-reduced-motion: no-preference) {
  .map-wip__cloud { animation: wipBob 3.2s ease-in-out infinite; }
}
@keyframes wipBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
.map-wip__title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--ink);
}
.map-wip__sub {
  max-width: 34ch;
  color: var(--soft);
  font-size: 0.9rem;
  line-height: 1.5;
}
/* Огонь живёт: каждый язык — прямой <path>, scaleY от собственного основания
   (transform-box: fill-box → origin считается от bbox самого пути). На <path>
   это работает надёжно (в отличие от <use>, где раньше анимация не запускалась). */
.map-wip__flame { transform-box: fill-box; transform-origin: bottom center; }
@media (prefers-reduced-motion: no-preference) {
  .map-wip__flame { animation: wipFlicker 1.2s ease-in-out infinite; }
  .map-wip__flame:nth-child(2n) { animation-delay: -0.45s; }
  .map-wip__flame:nth-child(3n) { animation-delay: -0.85s; }
}
@keyframes wipFlicker {
  0%, 100% { transform: scaleY(0.9) scaleX(1.04); }
  50%      { transform: scaleY(1.14) scaleX(0.93); }
}

label {
  display: grid;
  gap: 8px;
  color: var(--muted);
  font-weight: 600;
}

input {
  min-height: 42px;
  width: 100%;
  padding: 0 13px;
  color: var(--ink);
  background: var(--glass);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  transition: border-color 180ms var(--ease), background 180ms var(--ease);
}

input::placeholder { color: var(--faint); }

input:focus,
button:focus-visible {
  outline: 2px solid color-mix(in oklch, var(--accent), transparent 35%);
  outline-offset: 2px;
}

input:focus { border-color: var(--accent); background: var(--glass-2); }

/* -------------------------------------------------------------- Errors */
.error-box {
  display: flex;
  gap: 12px;
  padding: 14px;
  border: 1px solid color-mix(in oklch, var(--danger), transparent 55%);
  border-radius: var(--radius-sm);
  background: color-mix(in oklch, var(--danger), transparent 88%);
}

.error-box__icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  stroke: var(--danger);
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.error-box strong { color: color-mix(in oklch, var(--danger), white 18%); }
.error-box p { margin-top: 4px; color: var(--muted); font-size: 0.88rem; }

/* --------------------------------------------------------------- Foot */
.app-foot {
  display: grid;
  justify-items: center;
  gap: 8px;
  margin-top: 18px;
  padding: 0 4px;
  color: var(--faint);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-align: center;
}

/* ----------------------------------------------------- Loading / boot */
.is-loading { cursor: progress; }

.skeleton {
  color: transparent !important;
  border-radius: 6px;
  background: linear-gradient(100deg, var(--glass) 30%, var(--glass-2) 50%, var(--glass) 70%);
  background-size: 200% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

@keyframes shimmer {
  to { background-position: -200% 0; }
}

/* Staggered reveal on load */
.reveal {
  opacity: 0;
  transform: translateY(14px);
}

body:not(.is-booting) .reveal {
  animation: rise 620ms var(--ease) forwards;
  animation-delay: calc(var(--d, 0) * 80ms);
}

@keyframes rise {
  to { opacity: 1; transform: none; }
}

/* Safety net: once the app is ready, revealed panels are guaranteed visible even
   if the entrance animation never ran. iOS standalone PWAs throttle/skip CSS
   animations while the webview isn't fully visible, which left `.reveal` panels
   stuck at opacity:0 until a tab switch restarted the animations. Added by JS
   after the entrance window and re-asserted on pageshow/visibilitychange. */
body.is-ready .reveal { opacity: 1; transform: none; }

/* Progressive fill: a value that just changed gets a soft crossfade. */
.is-updated { animation: valueFade 420ms var(--ease); }
@keyframes valueFade {
  from { opacity: 0.35; }
  to   { opacity: 1; }
}

/* A panel/section transitioning skeleton -> data plays the rise once. */
.is-filled { animation: rise 520ms var(--ease) both; }

/* "Считаю консенсус" chip shown on consensus panels during the quorum phase. */
.calc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid color-mix(in oklch, var(--accent), transparent 70%);
}
.calc-chip::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: pulse 1.2s ease-in-out infinite;
}
.calc-chip[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; animation: none; }
  .is-updated, .is-filled, .calc-chip::before { animation: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------- Responsive */
@media (max-width: 920px) {
  .overview-card { grid-template-columns: 1fr; }
}

@media (max-width: 620px) {
  body { font-size: 13.5px; }

  .app-shell { width: min(100% - 18px, 1240px); }
  .app-header {
    gap: 8px;
    padding-top: 8px;
  }

  .topbar {
    gap: 8px;
    padding: 8px;
  }

  .place-trigger {
    gap: 9px;
    padding: 5px 7px;
    margin: -4px;
  }

  .app-mark {
    width: 32px;
    height: 32px;
  }

  .eyebrow { max-width: 100%; }
  /* На телефоне приоритет — времени и СТРАНЕ (их и оставляем); координаты прячем,
     чтобы строка не упиралась в край и страна всегда была видна. */
  #placeCoords { display: none; }

  h1 {
    font-size: 1.18rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .hero-actions {
    flex: 0 0 auto;
    justify-content: flex-end;
    gap: 6px;
  }

  /* Touch targets: comfortable 44px minimum on mobile (same fix as #sportSelect).
     Tabs and the header icon buttons were ~33/36px — too small for a thumb.
     The sliding tab indicator (top/bottom:4px) grows with the taller row.
     Popup close buttons (.icon-button.small) stay compact on purpose. */
  .tab {
    min-height: 40px; /* чуть ниже таб-бар, но всё ещё удобно для пальца */
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .tabbar__inner { padding: 3px; }
  .icon-button:not(.small) { min-height: 44px; width: 44px; }

  .overview-main,
  .advice-panel,
  .panel { padding: 16px; }

  /* Mobile (rebuilt from scratch). Two matched-size anchors carry the card:
     the big temperature (left) and a large weather icon (right). Around them,
     packed with no wasted space:
       row 1  eyebrow (left)  ·  condition chip (right, above the icon)
       rows 2-4  big temp with feels + hi/lo tucked tight beneath it
       icon spans rows 2-4 on the right, centred to that stack
     Wrappers dissolve into the grid so each piece lands in its own slot. */
  .overview-main {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "kicker kicker"
      "temp   icon"
      "meta   chip"
      "summary summary";
    align-items: center;
    column-gap: 14px;
    row-gap: 3px;
  }

  .now-head { display: contents; }
  .temperature-row { display: contents; }
  .temperature-side { display: contents; }

  .now-head .section-kicker {
    grid-area: kicker;
    justify-self: start;
    align-self: end;
    margin: 0 0 3px;
  }

  /* Condition: right-aligned, directly under the icon and beside the figures.
     Stays a short label; if the text is long it wraps down to two lines rather
     than stretching (richer detail belongs in the 12-hour block, not here). */
  .temperature-side .weather-chip {
    grid-area: chip;
    justify-self: end;
    align-self: center;
    max-width: 44vw;
    white-space: normal;
    text-align: center;
    line-height: 1.2;
    padding: 5px 11px;
    margin: 0;
  }

  /* Hero number, sized to match the icon's visual mass; sits low so the
     figures tuck right beneath it. */
  .temperature {
    grid-area: temp;
    justify-self: start;
    align-self: end;
    font-size: clamp(5.2rem, 24vw, 6.6rem);
    line-height: 0.82;
  }

  /* Feels + hi/lo stacked tight under the number. */
  .now-figures {
    grid-area: meta;
    display: flex;
    flex-direction: column;
    gap: 2px;
    justify-self: start;
    align-self: start;
    min-width: 0;
  }
  .feels-line { white-space: nowrap; }
  .range-line { white-space: nowrap; }
  .range-lo,
  .range-hi { white-space: nowrap; }

  /* Large icon anchoring the right, bottom-aligned with the number. */
  .weather-icon--hero {
    grid-area: icon;
    justify-self: end;
    align-self: end;
    width: clamp(88px, 24vw, 108px);
    height: clamp(88px, 24vw, 108px);
    border-radius: 22px;
  }
  .weather-icon--hero svg {
    width: clamp(64px, 17.5vw, 80px);
    height: clamp(64px, 17.5vw, 80px);
  }

  .fact-list { grid-template-columns: 1fr 1fr; }

  /* Daily mobile layout is owned by the forecast container query above. */
}

/* Very narrow phones (≤380px): collapse the two-column metric/astro grids to
   a single column so they don't overflow the viewport, and tighten the tab row
   so it fits without horizontal scroll. */
@media (max-width: 380px) {
  .fact-list { grid-template-columns: 1fr; }
  .astro-grid { grid-template-columns: 1fr; }
  .tab { padding: 7px 10px; font-size: 0.8rem; }
}

/* Wide cards (481-620px: foldables, small tablets, narrow windows). The 1fr
   left column would strand the icon at the far edge with a void, so the reading
   collapses to a content-sized cluster, left-aligned, slack as right-hand air. */
@media (min-width: 481px) and (max-width: 620px) {
  .overview-main {
    grid-template-columns: auto auto;
    justify-content: start;
  }
}

/* ======================================================================
   Minutely nowcast banner
   ====================================================================== */
.nowcast {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
}
/* display:flex above beats the UA [hidden] rule, so re-assert it — otherwise the
   banner stays visible (with stale text) when toggled off, e.g. «По часам» with
   no minute-nowcast still showing the «По дням» week headline. */
.nowcast[hidden] { display: none; }
.nowcast[data-tone="warn"] { background: color-mix(in oklch, var(--warn), transparent 88%); }
.nowcast[data-tone="danger"] { background: color-mix(in oklch, var(--danger), transparent 88%); }
.nowcast-text { display: flex; align-items: center; gap: 10px; font-weight: 650; }
.nowcast-icon { width: 18px; height: 18px; flex: 0 0 auto; stroke: var(--accent); stroke-width: 1.8; fill: none; }
.nowcast[data-tone="warn"] .nowcast-icon { stroke: var(--warn); }
.nowcast[data-tone="danger"] .nowcast-icon { stroke: var(--danger); }
.nowcast-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 30px;
  flex: 0 0 auto;
}
.nowcast-bar {
  width: 7px;
  border-radius: 3px;
  background: color-mix(in oklch, var(--accent), transparent 55%);
  min-height: 3px;
}
.nowcast-bar.kind-rain { background: var(--ico-rain); }
.nowcast-bar.kind-snow { background: var(--ico-snow); }
.nowcast-bar.kind-dry { background: var(--line-strong); }

/* ======================================================================
   Air quality + astronomy panel
   ====================================================================== */
.air-grid {
  display: grid;
  grid-template-columns: minmax(220px, 0.9fr) minmax(0, 1.5fr);
  gap: 14px;
}
.aqi-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
}
.aqi-dial {
  flex: 0 0 auto;
  display: grid;
  place-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  text-align: center;
  line-height: 1;
  color: var(--soft);
  background: var(--glass-2);
  border: 3px solid var(--line-strong);
}
.aqi-dial[data-tone="ok"] { border-color: var(--ok); color: var(--ok-ink); }
.aqi-dial[data-tone="warn"] { border-color: var(--warn); color: var(--warn-ink); }
.aqi-dial[data-tone="danger"] { border-color: var(--danger); color: var(--danger); }
.aqi-dial strong { font-family: var(--font-body); font-size: 1.6rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.aqi-dial span { display: block; margin-top: 2px; font-size: 0.6rem; font-weight: 700; letter-spacing: 0.1em; }
.aqi-info { min-width: 0; }
.aqi-info strong { font-size: 1rem; font-weight: 700; }
.aqi-info strong[data-tone="ok"] { color: var(--ok-ink); }
.aqi-info strong[data-tone="warn"] { color: var(--warn-ink); }
.aqi-info strong[data-tone="danger"] { color: var(--danger); }
.aqi-info p { margin-top: 4px; color: var(--muted); font-size: 0.82rem; line-height: 1.4; }
.aqi-detail { color: var(--muted) !important; font-size: 0.76rem !important; }
.aqi-tech {
  margin-top: 4px;
  color: var(--soft);
  font-size: 0.74rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
/* The card carries a source-provenance tooltip on hover/focus, like fact tiles. */
.aqi-card { position: relative; }
.aqi-card[data-has-tip] { cursor: help; }
.aqi-card[data-has-tip]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.astro-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 9px;
  align-content: start;
}
.astro-tile {
  position: relative;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
}
.astro-tile[data-has-tip] { cursor: help; }
.astro-tile[data-has-tip]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.astro-body { display: grid; gap: 2px; min-width: 0; }
.astro-label { color: var(--soft); font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.astro-tile strong { font-size: 1rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.astro-sub { color: var(--muted); font-size: 0.76rem; }

.astro-ico { flex: 0 0 auto; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px; background: color-mix(in oklch, var(--accent), transparent 90%); }
.astro-ico svg { width: 20px; height: 20px; stroke: var(--accent); stroke-width: 1.8; fill: none; stroke-linecap: round; stroke-linejoin: round; }

/* The moon gets a large illustrative visual spanning the row */
.astro-tile--moon { grid-column: 1 / -1; gap: 14px; }
.astro-visual { flex: 0 0 auto; line-height: 0; }
.astro-visual svg { width: 64px; height: 64px; }
.astro-tile--moon strong { font-size: 1.06rem; }

.insight-narrative {
  display: grid;
  gap: 0;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--glass);
  border: 1px solid var(--line);
}
.narrative-row {
  display: grid;
  grid-template-columns: 30px minmax(112px, 0.22fr) minmax(0, 1fr);
  gap: 9px 12px;
  align-items: center;
  padding: 9px 11px;
  border-top: 1px solid var(--line);
}
.narrative-row:first-child { border-top: 0; }
.narrative-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: color-mix(in oklch, var(--accent), transparent 91%);
}
.narrative-icon svg {
  width: 24px;
  height: 24px;
}
.narrative-label {
  color: var(--accent);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
}
.narrative-text {
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.36;
}
@media (max-width: 560px) {
  .narrative-row {
    grid-template-columns: 30px minmax(0, 1fr);
    gap: 3px 9px;
  }
  .narrative-label,
  .narrative-text {
    grid-column: 2;
  }
}

@media (max-width: 720px) {
  .air-grid { grid-template-columns: 1fr; }
}

/* ======================================================================
   Chip navigation
   ====================================================================== */
/* Tab bar — horizontally scrollable on narrow screens. Stickiness is handled
   by the .app-header wrapper so the topbar and tabs move as one unit. */
.tabbar {
  position: relative;
}
.tabbar__inner {
  position: relative;
  isolation: isolate;
  /* Равные колонки — как у сегмент-контрола «по часам/по дням».
     minmax(0, 1fr), а не 1fr: у 1fr-трека неявный min-width:auto = ширине
     контента, поэтому широкая вкладка («Настройки») раздувала свою колонку и
     колонки переставали быть равными. На узком экране скользящий индикатор
     (фикс. ширина, шаг translateX(100%)) уезжал с подписи. minmax(0,…) снимает
     контентный минимум → 4 строго равные колонки → индикатор всегда по центру. */
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass-strong);
  backdrop-filter: blur(12px);
}
/* Скользящий индикатор активного раздела (как у .segmented): едет transform'ом на
   композиторе — плавно, и тень движется ВМЕСТЕ с ним. Раньше фон/тень просто
   переключались между табами — отсюда «прыжок» и отставание тени. */
.tabbar__inner::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: calc((100% - 8px) / 4);
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 3px 10px color-mix(in oklch, var(--accent), transparent 70%);
  transform: translateX(0);
  transition: transform 280ms var(--ease);
}
.tabbar__inner[data-active="forecast"]::before { transform: translateX(100%); }
.tabbar__inner[data-active="map"]::before      { transform: translateX(200%); }
.tabbar__inner[data-active="analysis"]::before  { transform: translateX(300%); }
.tab {
  position: relative;
  z-index: 1;
  padding: 7px 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 0.84rem;
  font-weight: 650;
  white-space: nowrap;
  text-align: center;
  cursor: pointer;
  transition: color 200ms var(--ease);
}
/* Только на устройствах с настоящим ховером: иначе на тач-экране :hover «залипает»
   на последней нажатой вкладке — после свайпа её текст оставался синим. */
@media (hover: hover) {
  .tab:hover { color: var(--accent); }
}
.tab[aria-selected="true"] { color: var(--accent-ink, #fff); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 999px; }

/* Section heading inside the forecast detailed lists */
.detail-kicker {
  margin: 18px 0 8px;
  color: var(--soft);
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.panel-intro {
  margin: -4px 0 14px;
  color: var(--soft);
  font-size: 0.86rem;
  line-height: 1.5;
}

.compare-consensus {
  margin-left: 8px;
  color: var(--accent);
  font-size: 0.76rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ======================================================================
   Animated weather-icon parts
   ====================================================================== */
.ico-spin { animation: ico-spin 22s linear infinite; }
.ico-pulse { animation: ico-pulse 4s ease-in-out infinite; }
.ico-bob { animation: ico-bob 5s ease-in-out infinite; }
.ico-rain { animation: ico-rain 1.1s linear infinite; }
.ico-snow { animation: ico-snow 3.2s ease-in-out infinite; }
.ico-bolt { animation: ico-flash 2.6s steps(1, end) infinite; transform-origin: center; }
.ico-fog { animation: ico-fog 3.4s ease-in-out infinite alternate; }
.ico-twinkle { animation: ico-twinkle 2.6s ease-in-out infinite; }

@keyframes ico-spin { to { transform: rotate(360deg); } }
@keyframes ico-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
@keyframes ico-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.5px); } }
@keyframes ico-rain {
  0% { transform: translateY(-4px); opacity: 0; }
  30% { opacity: 1; }
  100% { transform: translateY(6px); opacity: 0; }
}
@keyframes ico-snow {
  0% { transform: translateY(-3px) translateX(0); opacity: 0; }
  30% { opacity: 1; }
  100% { transform: translateY(7px) translateX(2px); opacity: 0; }
}
@keyframes ico-flash {
  0%, 92%, 100% { opacity: 1; }
  94% { opacity: 0.15; }
  96% { opacity: 1; }
}
@keyframes ico-fog { from { transform: translateX(-2px); } to { transform: translateX(3px); } }
@keyframes ico-twinkle { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .ico-spin, .ico-pulse, .ico-bob, .ico-rain, .ico-snow, .ico-bolt, .ico-fog, .ico-twinkle {
    animation: none;
  }
}

/* ======================================================================
   Location search
   ====================================================================== */
.location-search {
  position: relative;
}

/* Author `display` rules below beat the UA [hidden] rule, so re-assert it for
   every location element we toggle via the hidden attribute. */
.location-pop[hidden],
.location-results[hidden],
.location-field__clear[hidden] { display: none; }

.location-pop {
  /* JS (positionLocationPop) computes the exact viewport coordinates and writes
     them as CSS custom properties. We use position:fixed here so the popup
     escapes the topbar's backdrop-filter stacking context and sits at the
     correct viewport position below the full app-header (incl. tabbar). */
  position: fixed;
  top: var(--location-pop-top, 140px);
  left: var(--location-pop-left, auto);
  right: auto;
  z-index: 80;
  width: var(--location-pop-width, min(340px, calc(100vw - 24px)));
}

/* On desktop the sheet wrapper IS the floating dropdown card. */
.location-sheet {
  display: flex;
  flex-direction: column;
  padding: 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-1);
  box-shadow: 0 22px 50px -24px oklch(45% 0.06 255 / 0.5);
}

/* Grip + header belong to the mobile bottom-sheet; hidden on desktop. */
.location-sheet__grip,
.location-sheet__head { display: none; }

.location-sheet__title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 750;
  color: var(--ink);
}
.location-sheet__close {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
  color: var(--soft);
}
.location-sheet__close svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; }
.location-sheet__close:hover { color: var(--ink); border-color: var(--line-strong); }

/* Search field with leading magnifier icon and trailing clear button. */
.location-field {
  position: relative;
  margin-bottom: 8px;
}
.location-field__icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 17px;
  height: 17px;
  stroke: var(--faint);
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  pointer-events: none;
}
/* 16px keeps iOS Safari from auto-zooming the page when the field is focused. */
.location-field input {
  padding-left: 38px;
  padding-right: 38px;
  font-size: 16px;
}
.location-field:focus-within .location-field__icon { stroke: var(--accent); }

.location-field__clear {
  position: absolute;
  right: 7px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--faint);
}
.location-field__clear svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 2.2; fill: none; stroke-linecap: round; }
.location-field__clear:hover { color: var(--ink); background: var(--glass); }

.location-results {
  display: grid;
  /* minmax(0,1fr): колонка не шире контейнера. Без этого grid-колонка тянулась
     по max-content самой длинной строки (регион + город), строки вылезали за
     ширину листа и список скроллился вбок — названия городов обрезались слева. */
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  max-height: 300px;
  overflow-y: auto;
  overflow-x: hidden; /* страховка: никакого горизонтального скролла/обрезания */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.location-result {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  min-width: 0; /* flex-элемент должен иметь право сжиматься, иначе переполнение */
  padding: 10px 11px;
  border: 0;
  width: 100%;
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  text-align: left;
  font-weight: 600;
}

.location-result:hover,
.location-result:focus-visible { background: var(--accent-soft); }
.location-result:active { background: color-mix(in oklch, var(--accent), transparent 84%); }
/* Имя города — приоритет: занимает доступное место, обрезается только в крайнем
   случае. Одна строка с троеточием, чтобы строки были одинаковой высоты. */
.location-result b {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 650;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Регион — вторичный: сжимается и обрезается первым, не «съедает» имя города. */
.location-result span {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 46%;
  color: var(--soft);
  font-size: 0.78rem;
  font-weight: 500;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.location-hint { padding: 10px 4px; color: var(--soft); font-size: 0.82rem; }

.location-geo {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 11px;
  margin-bottom: 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--glass);
  color: var(--accent);
  font-weight: 650;
  text-align: left;
}
.location-geo:hover { background: var(--accent-soft); border-color: color-mix(in oklch, var(--accent), transparent 55%); }
.location-geo svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; flex: 0 0 auto; }

/* While the user is typing we hide the browse view (geo + favorites) and show
   the results list instead, so the picker never feels cluttered. */
.location-pop.is-searching .location-geo,
.location-pop.is-searching .location-favorites { display: none; }

/* City chips — favorites + "add current" laid out as wrapping pills. */
.location-favorites {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.location-favorites:empty { display: none; }

.location-fav-label {
  flex: 0 0 100%;
  margin: 0 2px 2px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--soft);
}

.location-chip {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  border-radius: 999px;
  background: var(--glass);
  border: 1px solid var(--line);
  transition: border-color 160ms var(--ease), background 160ms var(--ease);
}
.location-chip:hover { border-color: var(--line-strong); }
.location-chip__pick {
  min-width: 0;
  padding: 7px 6px 7px 14px;
  border: 0;
  background: transparent;
  color: var(--ink);
  text-align: left;
  font-weight: 600;
  font-size: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.location-chip__pick:hover { color: var(--accent); }
.location-chip__del {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 32px;
  padding: 0;
  margin-right: 3px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--faint);
  font-size: 0.95rem;
  line-height: 1;
}
.location-chip__del:hover { color: var(--danger); }

/* "Add current city" reads as a dashed, accent-tinted call to action. */
.location-chip--add {
  border-style: dashed;
  border-color: color-mix(in oklch, var(--accent), transparent 55%);
  background: transparent;
}
.location-chip--add .location-chip__pick { padding-right: 14px; color: var(--accent); }
.location-chip--add:hover { background: var(--accent-soft); border-color: var(--accent); }

/* ----------------------------------------------------------------------
   Mobile: the picker becomes a full-width bottom sheet over a dimmed
   backdrop. The page behind it is scroll-locked (body.location-open) so the
   app stays static. JS skips coordinate positioning at this breakpoint.
   ---------------------------------------------------------------------- */
@media (max-width: 620px) {
  /* position:fixed + сохранённый top (JS) надёжно фиксирует страницу под листом
     на iOS — один overflow:hidden там не держал и давал скачок при выборе города. */
  body.location-open {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    overflow: hidden;
  }

  .location-pop {
    inset: 0;
    top: 0;
    left: 0;
    width: auto;
    z-index: 200;
    display: flex;
    align-items: flex-end;
    /* --sheet-dim (set by the swipe handler) fades the backdrop as you drag. */
    background: oklch(20% 0.03 255 / calc(0.42 * var(--sheet-dim, 1)));
    animation: location-backdrop-in 200ms var(--ease) backwards;
  }

  .location-sheet {
    width: 100%;
    /* Cap to the (keyboard-aware) pop height so the sheet always fits above the
       on-screen keyboard; inner results scroll for overflow. */
    max-height: 100%;
    overflow: hidden;
    padding: 8px 16px calc(16px + env(safe-area-inset-bottom));
    border: 0;
    border-radius: 22px 22px 0 0;
    box-shadow: 0 -18px 50px -20px oklch(30% 0.05 255 / 0.45);
    /* backwards: стартовый кадр виден сразу — без мерцания при появлении (PWA/iOS). */
    animation: location-sheet-in 280ms var(--ease) backwards;
    /* Snap-back when a downward drag is released without crossing the threshold. */
    transition: transform 240ms var(--ease);
    touch-action: pan-y;
  }

  /* The fixed top region (grip, head, field, geo, favorites) keeps its size;
     the results list grows into the remaining space and scrolls. */
  .location-sheet__grip,
  .location-sheet__head,
  .location-field,
  .location-geo,
  .location-favorites { flex: 0 0 auto; }
  .location-results {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
  }

  .location-sheet__grip {
    display: block;
    width: 40px;
    height: 4px;
    margin: 4px auto 12px;
    border-radius: 999px;
    background: var(--line-strong);
  }
  .location-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
  }

  .location-field input { height: 48px; }
  .location-geo { padding: 13px; }
  .location-result { padding: 13px 12px; }
  .location-results { max-height: 52vh; }
  .location-chip__pick { padding: 9px 6px 9px 15px; font-size: 0.9rem; }
}

@keyframes location-sheet-in {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}
@keyframes location-backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .location-pop,
  .location-sheet { animation: none; }
}

/* Производительность на слабых устройствах: липкие стеклянные поверхности
   (шапка + переключатель разделов) пересчитывают backdrop-filter на КАЖДОМ кадре
   прокрутки — на бюджетных GPU это главный источник лагов. На телефонах режем
   радиус размытия (визуально почти незаметно, нагрузка падает заметно). Карты не
   касаемся. Блок идёт после базовых правил, чтобы выиграть каскад. */
@media (max-width: 620px) {
  .topbar { backdrop-filter: blur(10px) saturate(1.2); }
  .tabbar__inner { backdrop-filter: blur(8px); }
}

/* ======================================================================
   Segmented toggle (hourly / daily, etc.)
   ====================================================================== */
.segmented {
  position: relative;
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr; /* равные колонки → одинаковая ширина кнопок */
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
  isolation: isolate;
}

/* Скользящий индикатор активного сегмента (для 2 кнопок). */
.segmented::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc((100% - 6px) / 2);
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 4px 12px -4px color-mix(in oklch, var(--accent), transparent 40%);
  transform: translateX(0);
  transition: transform 300ms var(--ease);
}
.segmented[data-active="daily"]::before { transform: translateX(100%); }

.segmented button {
  position: relative;
  z-index: 1;
  min-height: 30px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-weight: 650;
  font-size: 0.85rem;
  cursor: pointer;
  transition: color 200ms var(--ease);
}

.segmented button.is-active { color: oklch(99% 0 0); }

@media (prefers-reduced-motion: reduce) {
  .segmented::before,
  .tabbar__inner::before { transition: none; }
}

/* ======================================================================
   Source-trust / verification panel
   ====================================================================== */
.verify-consensus {
  --tone: var(--ok);
  display: grid;
  gap: 4px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border: 1px solid color-mix(in oklch, var(--tone), var(--line) 60%);
  border-left: 3px solid var(--tone);
  border-radius: 12px;
  background: color-mix(in oklch, var(--tone), transparent 92%);
  font-variant-numeric: tabular-nums lining-nums;
}
.verify-consensus[data-tone="ok"] { --tone: var(--ok); }
.verify-consensus[data-tone="warn"] { --tone: var(--warn); }
.verify-consensus[data-tone="danger"] { --tone: var(--danger); }

.verify-consensus__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.verify-consensus__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--tone);
  flex: none;
}
.verify-consensus__label {
  font-weight: 600;
  font-size: 0.94rem;
  color: var(--ink);
}
.verify-consensus__disputes {
  font-size: 0.86rem;
  color: var(--muted);
}

/* ИИ-калибровка источников — компактная сводка «проверил/изменил/расчёт».
   Появляется только когда ИИ реально отработал (прокси-режим). Анализ-only. */
.verify-ai {
  margin: 10px 0 4px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
}
.verify-ai__head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.92rem;
  margin-bottom: 6px;
}
.verify-ai__badge {
  font-size: 0.64rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: #fff;
  background: var(--accent);
  border-radius: 5px;
  padding: 2px 6px;
}
.verify-ai__line {
  margin: 4px 0 0;
  font-size: 0.84rem;
  color: var(--muted);
  line-height: 1.45;
}
.verify-ai__line span {
  color: var(--soft);
  font-weight: 700;
}

.trust-list { display: grid; gap: 8px; }

.trust-item {
  --tone: var(--ok);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--glass);
  transition: border-color 200ms var(--ease);
}
.trust-item[data-tone="ok"] { --tone: var(--ok); }
.trust-item[data-tone="warn"] { --tone: var(--warn); }
.trust-item[data-tone="danger"] { --tone: var(--danger); }
.trust-item:hover { border-color: color-mix(in oklch, var(--tone), transparent 62%); }

/* Reliability donut */
.trust-ring {
  position: relative;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
}
.trust-ring__svg { width: 50px; height: 50px; transform: rotate(-90deg); }
.trust-ring__track { fill: none; stroke: var(--glass-2); stroke-width: 5; }
.trust-ring__val {
  fill: none;
  stroke: var(--tone);
  stroke-width: 5;
  stroke-linecap: round;
  transition: stroke-dashoffset 800ms var(--ease);
}
.trust-ring__num {
  position: absolute;
  font-family: var(--font-body);
  font-variant-numeric: tabular-nums;
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
}

.trust-main { display: grid; gap: 7px; min-width: 0; }
.trust-row { display: flex; align-items: center; gap: 9px; min-width: 0; }
.trust-name { font-weight: 700; }
.trust-anchor {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.trust-verdict {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  padding: 3px 10px;
  border-radius: 999px;
  color: var(--tone);
  background: color-mix(in oklch, var(--tone), transparent 88%);
  font-weight: 700;
  font-size: 0.8rem;
  white-space: nowrap;
}
.trust-verdict .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

.trust-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  color: var(--soft);
  font-size: 0.76rem;
  font-variant-numeric: tabular-nums;
}
.trust-meta b { color: var(--muted); font-weight: 700; }

/* Per-source bias vs METAR observations (forecast-vs-fact history) */
.trust-bias {
  margin-top: 6px;
  font-size: 0.76rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--ok);
}
.trust-bias::before { content: "↔ "; opacity: 0.7; }
.trust-bias[data-tone="warn"] { color: var(--warn-ink); }
.verify-flag.obs::before { content: "📡"; }

.verify-flags {
  display: grid;
  gap: 6px;
  margin-top: 12px;
}
.verify-flag {
  display: flex;
  gap: 9px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in oklch, var(--warn), transparent 60%);
  background: color-mix(in oklch, var(--warn), transparent 90%);
  color: var(--ink);
  font-size: 0.85rem;
  line-height: 1.4;
}
.verify-flag::before { content: "⚠"; color: var(--warn); }
.verify-flag.ai {
  border-color: color-mix(in oklch, var(--accent), transparent 55%);
  background: var(--accent-soft);
}
.verify-flag.ai::before { content: "✦"; color: var(--accent); }

/* ======================================================================
   Radar map control panel (classes from map.js)
   ====================================================================== */
.radar-shell {
  position: relative;
  height: 400px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--glass-2);
}
.radar-shell .leaflet-container { background: var(--glass-2); font-family: var(--font-body); }

/* Controls are split: a layer segment floats top-center, the timeline + legend
   sit in a thin bar along the bottom, leaving the map center unobstructed. */
.radar-ctrl {
  position: absolute;
  z-index: 1100; /* above Leaflet marker pane (600) so the grid sits behind controls */
  border: 1px solid var(--line-strong);
  background: var(--glass-strong);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-lift);
}
.radar-ctrl--top {
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  border-radius: 999px;
}
.radar-ctrl--bottom {
  left: 50%;
  transform: translateX(-50%);
  bottom: 12px;
  width: min(480px, calc(100% - 20px));
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px 10px;
  padding: 7px 11px;
  border-radius: 13px;
}

.radar-tab {
  padding: 5px 12px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 650;
  cursor: pointer;
  transition: color 160ms var(--ease), background 160ms var(--ease);
}
.radar-tab:hover { color: var(--accent); }
.radar-tab.is-active { background: var(--accent); color: oklch(99% 0 0); }
.radar-tab[disabled] { opacity: 0.4; cursor: not-allowed; }

.radar-tabs { display: flex; gap: 3px; }

.radar-player { display: flex; align-items: center; gap: 10px; flex: 1 1 220px; min-width: 160px; }
.radar-player[hidden] { display: none; }
.radar-slider { flex: 1; accent-color: var(--accent); cursor: pointer; }
.radar-time {
  flex: 0 0 auto;
  min-width: 44px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--ink);
}
.radar-time[data-future="1"] { color: var(--accent); }

/* Кнопка Play/Pause — гоняет таймлайн (кадры-предсказания или часы прогноза). */
.radar-play {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--accent);
  color: oklch(99% 0 0);
  cursor: pointer;
  transition: background 160ms var(--ease), transform 120ms var(--ease), opacity 160ms var(--ease);
}
.radar-play:hover { transform: scale(1.06); }
.radar-play:disabled { opacity: 0.4; cursor: not-allowed; }
.radar-play svg { width: 15px; height: 15px; fill: currentColor; }
.radar-play.is-playing { background: oklch(58% 0.16 25); }

/* Predictive grid markers (Open-Meteo): temperature pills, wind arrows,
   cloud-cover discs placed at grid points over the map. */
.mapgrid-icon { background: transparent; border: 0; }
.mapgrid-temp {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 22px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--c);
  color: oklch(99% 0 0);
  font: 800 12px/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 2px oklch(22% 0.05 250 / 0.55);
  box-shadow: 0 1px 5px oklch(30% 0.05 250 / 0.35), 0 0 0 1px oklch(100% 0 0 / 0.35) inset;
  white-space: nowrap;
}
.mapgrid-wind {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.mapgrid-wind svg {
  width: 24px;
  height: 24px;
  fill: currentColor;
  filter: drop-shadow(0 1px 2px oklch(25% 0.04 250 / 0.4));
}
.mapgrid-wind b {
  padding: 1px 4px;
  border-radius: 5px;
  background: var(--glass-2);
  color: var(--ink);
  font: 700 10px/1 var(--font-mono);
  box-shadow: 0 1px 3px oklch(30% 0.05 250 / 0.25);
}
/* Clouds: numeric percent chip over the real satellite / cloud-field layer. */
.mapgrid-cloudnum {
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  min-width: 30px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: oklch(100% 0 0 / 0.82);
  color: oklch(38% 0.02 250);
  font: 800 11px/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 4px oklch(30% 0.05 250 / 0.3), 0 0 0 1px oklch(40% 0.02 250 / 0.12) inset;
  white-space: nowrap;
}
.mapgrid-cloudnum i { font-size: 0.7em; font-style: normal; margin-left: 1px; opacity: 0.7; }
.mapgrid-cloudnum[data-d="hi"] { background: oklch(34% 0.02 250 / 0.86); color: oklch(98% 0 0); }
.mapgrid-cloudnum[data-d="mid"] { background: oklch(72% 0.02 250 / 0.84); color: oklch(24% 0.02 250); }

/* Smooth interpolated field (temperature / cloud cover) painted under the digits. */
.mapgrid-heat { will-change: transform; }

/* Precipitation drops — colour by intensity; dry points shown as faint dots. */
.mapgrid-precip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
}
.mapgrid-precip svg {
  width: 22px;
  height: 22px;
  fill: currentColor;
  filter: drop-shadow(0 1px 2px oklch(25% 0.06 250 / 0.4));
}
.mapgrid-precip[data-i="light"] { color: oklch(80% 0.08 230); }
.mapgrid-precip[data-i="rain"] { color: oklch(62% 0.16 230); }
.mapgrid-precip[data-i="heavy"] { color: oklch(70% 0.17 145); }
.mapgrid-precip[data-i="storm"] { color: oklch(60% 0.2 25); }
.mapgrid-precip b {
  padding: 1px 4px;
  border-radius: 5px;
  background: var(--glass-2);
  color: var(--ink);
  font: 700 10px/1 var(--font-mono);
  box-shadow: 0 1px 3px oklch(30% 0.05 250 / 0.25);
}
.mapgrid-dry {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: oklch(55% 0.02 250 / 0.32);
}
/* Mode description is redundant with the tabs + the caption below the map. */
.radar-status { display: none; }

/* Per-mode color legend — its own row in the bottom bar. */
.radar-legend {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 12px;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted);
}
.radar-legend[hidden] { display: none; }
.radar-legend__item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.radar-legend__item i {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  border: 1px solid var(--line);
}
.radar-legend__bar {
  width: 100%;
  height: 9px;
  border-radius: 99px;
  border: 1px solid var(--line);
}
.radar-legend__scale {
  display: flex;
  justify-content: space-between;
  width: 100%;
  margin-top: -2px;
  color: var(--soft);
  font-variant-numeric: tabular-nums;
}

/* Hourly/daily switch lives in the panel heading */
.panel-heading .segmented { flex: 0 0 auto; }

@media (max-width: 620px) {
  .trust-row { flex-wrap: wrap; }
  .trust-verdict { margin-left: 0; }
  /* Taller, portrait-oriented map on phones. */
  .radar-shell { height: min(72vh, 600px); min-height: 440px; }
}

/* ======================================================================
   Insights / smart recommendations
   ====================================================================== */
/* Tone channel — one variable drives icon + tint per element. */
.insight-lead,
.insight-card,
.window-chip {
  --tone: var(--accent);
  cursor: default;
}
[data-tone="ok"] { --tone: var(--ok); }
[data-tone="warn"] { --tone: var(--warn); }
[data-tone="danger"] { --tone: var(--danger); }
[data-tone="info"] { --tone: var(--accent); }

/* Panel rhythm — four groups stacked with a single consistent gap, hairline
   separators between them so the panel reads as one considered block. */
#insightsPanel > .insight-lead,
#insightsPanel > .insight-cards,
#insightsPanel > .insight-group { margin: 0; }
#insightsPanel {
  container: insights / inline-size;
  padding: 14px 16px 16px;
  scroll-margin-top: 156px;
}
#insightsPanel .panel-heading {
  align-items: flex-start;
  margin-bottom: 10px;
}
#insightsPanel > .insight-lead + .insight-cards {
  margin-top: 12px;
}
#insightsPanel > .insight-cards + .insight-group,
#insightsPanel > .insight-group + .insight-group {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.insight-subhead {
  margin: 0 0 7px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--soft);
}

/* Lead — the single most important thing right now. */
.insight-lead {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: 9px;
  align-items: center;
  min-height: 58px;
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  /* flat tone-tinted, Сейчас-tile style (colour comes from --tone per insight) */
  background:
    linear-gradient(0deg, color-mix(in oklch, var(--tone), transparent 86%), color-mix(in oklch, var(--tone), transparent 86%)),
    var(--glass);
  border: 1px solid color-mix(in oklch, var(--tone), transparent 70%);
}
.insight-lead__icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  color: var(--tone);
  background: color-mix(in oklch, var(--tone), transparent 91%);
}
.insight-lead__icon svg { width: 18px; height: 18px; }
.insight-lead__icon svg [fill="none"],
.insight-lead__icon svg path,
.insight-lead__icon svg circle {
  stroke: currentColor;
  stroke-width: 1.9;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.insight-lead__text {
  min-width: 0;
}
.insight-lead__text strong {
  display: block;
  font-size: 0.84rem;
  font-weight: 700;
  line-height: 1.18;
}
.insight-lead__text p {
  margin-top: 2px;
  max-width: 68ch;
  color: var(--muted);
  font-size: 0.76rem;
  line-height: 1.3;
}
.insight-trust svg {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  stroke: var(--accent);
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Recommendation cards — a clean, even grid; tone reads through a subtle tint
   plus the coloured icon chip, never a side stripe. */
.insight-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 8px;
}
.insight-card {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: 9px;
  align-items: center;
  min-height: 58px;
  padding: 9px 11px;
  cursor: default;
  border-radius: var(--radius-sm);
  /* flat tone-tinted, Сейчас-tile style (colour comes from --tone per insight) */
  background:
    linear-gradient(0deg, color-mix(in oklch, var(--tone), transparent 86%), color-mix(in oklch, var(--tone), transparent 86%)),
    var(--glass);
  border: 1px solid color-mix(in oklch, var(--tone), transparent 70%);
  transition: none;
}
.insight-card:hover {
  box-shadow: none;
}
.insight-card__icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  color: var(--tone);
  background: color-mix(in oklch, var(--tone), transparent 91%);
}
.insight-card__icon svg { width: 18px; height: 18px; }
.insight-card__icon svg path,
.insight-card__icon svg circle {
  stroke: currentColor;
  stroke-width: 1.9;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.insight-card__body { min-width: 0; }
.insight-card__body h3 {
  margin: 0;
  font-size: 0.84rem;
  font-weight: 700;
  line-height: 1.18;
}
.insight-card__body p {
  margin: 2px 0 0;
  color: var(--muted);
  font-size: 0.76rem;
  line-height: 1.3;
}

/* Best / worst outdoor windows — two chips side by side. */
.insight-windows {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.window-chip {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  /* flat tone-tinted, Сейчас-tile style (colour comes from --tone per window) */
  background:
    linear-gradient(0deg, color-mix(in oklch, var(--tone), transparent 86%), color-mix(in oklch, var(--tone), transparent 86%)),
    var(--glass);
  border: 1px solid color-mix(in oklch, var(--tone), transparent 70%);
}
.window-chip[data-kind="best"] { --tone: var(--ok); }
.window-chip[data-kind="worst"] { --tone: var(--warn); }
.window-chip[data-kind="sport"] { --tone: var(--accent); }
.window-chip[data-kind="none"] { --tone: var(--soft); } /* заглушка «нет окна» */
/* Цвет чипа «лучшее время» по качеству окна (как тир в попапе): great→зелёный,
   good→синий, ok→янтарный. Перебивает цвет по типу (идёт ниже по каскаду), поэтому
   «отлично» одинаково зелёное и для прогулки, и для спорта. «Худшее» тира не имеет —
   остаётся янтарным. */
.window-chip[data-tier="great"] { --tone: var(--ok); }
.window-chip[data-tier="good"]  { --tone: var(--accent); }
.window-chip[data-tier="ok"]    { --tone: var(--warn); }

/* «Прогулка» | «Спорт» — two top-aligned columns, each with its own header.
   Stays two-up even on narrow screens (the user asked for distinct columns). */
.insight-windows.insight-windows--cols {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 10px;
}
.window-col { display: grid; gap: 7px; min-width: 0; align-content: start; }
.window-col__chips { display: grid; gap: 7px; min-width: 0; }
.window-col > .insight-subhead { margin: 0; }
#sportSelect {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  /* min-height keeps it a comfortable tap target (>=44px) and visually on a par
     with the tone-tinted window chips it sits between. */
  min-height: 44px;
  padding: 8px 34px 8px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in oklch, var(--accent), transparent 70%);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6b86' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 11px center / 16px,
    linear-gradient(0deg, color-mix(in oklch, var(--accent), transparent 86%), color-mix(in oklch, var(--accent), transparent 86%)),
    var(--glass);
  font: inherit;
  /* 16px (1rem): below this iOS Safari zooms the page when the control is tapped,
     exactly the jump the location search input avoids. Keep it >=16px on mobile. */
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
#sportSelect:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* (Скелетон причины окна — см. общий блок шиммера выше.) */
.window-chip svg {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  color: var(--tone);
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.window-chip__text { display: grid; min-width: 0; line-height: 1.25; font-variant-numeric: tabular-nums; }
.window-chip__label {
  color: var(--soft);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.window-chip__text strong {
  font-variant-numeric: tabular-nums;
  font-size: 0.88rem;
  font-weight: 700;
}
.window-chip__reason { color: var(--muted); font-size: 0.76rem; }

@media (max-width: 560px) {
  .insight-cards,
  .insight-windows,
  .insight-windows.insight-windows--cols {
    grid-template-columns: 1fr;
  }
  #insightsPanel {
    padding: 14px;
    scroll-margin-top: 206px;
  }
  .insight-lead {
    align-items: center;
    min-height: 0;
    padding: 9px 10px;
  }
  .insight-card {
    min-height: 0;
    padding: 9px 10px;
  }
}

@container insights (max-width: 720px) {
  .insight-cards,
  .insight-windows,
  .insight-windows.insight-windows--cols {
    grid-template-columns: 1fr;
  }

  .narrative-row {
    grid-template-columns: 30px minmax(0, 1fr);
    gap: 3px 9px;
  }

  .narrative-label,
  .narrative-text {
    grid-column: 2;
  }
}

/* ===== MAP-LEGEND-BLOCK START (subagent) ===== */
/* Слой-зависимая легенда карты: одна легенда на активный слой.
   Осадки/Ветер — дискретные ячейки; Облака/Температура — непрерывная
   градиентная шкала с делениями; Ветер дополнительно несёт индикатор
   направления (стрелка «куда дует» + румб). */

/* Подпись над легендой — что именно показывает шкала. */
.radar-legend__caption {
  flex: 1 1 100%;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--soft);
}

/* Строка «скорость + направление» для слоя ветра. */
.radar-legend__row {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
}

/* Градиентная шкала (облака/температура): полоса + деления под ней. */
.radar-legend--bar { gap: 3px 12px; }
.radar-legend--bar .radar-legend__bar {
  width: 100%;
  height: 10px;
  border-radius: 99px;
  border: 1px solid var(--line);
}
.radar-legend__ticks {
  position: relative;
  width: 100%;
  height: 0.95rem;
  font-size: 0.66rem;
  font-variant-numeric: tabular-nums;
  color: var(--soft);
}
.radar-legend__tick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  white-space: nowrap;
}
/* Крайние деления держим в пределах полосы (не уезжают за край). */
.radar-legend__tick:first-child { transform: translateX(0); }
.radar-legend__tick:last-child { transform: translateX(-100%); }
.radar-legend__unit {
  flex: 0 0 auto;
  margin-left: auto;
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--soft);
  font-variant-numeric: tabular-nums;
}

/* Индикатор направления ветра: компасная стрелка + румб. */
.radar-wind-arrow {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  padding: 2px 8px 2px 4px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--glass);
}
.radar-wind-arrow__svg {
  width: 17px;
  height: 17px;
  fill: var(--accent);
  transition: transform 320ms var(--ease);
  transform-origin: 50% 50%;
}
.radar-wind-arrow__label {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
}
/* ===== MAP-LEGEND-BLOCK END ===== */

/* Cookie consent banner (informational model) */
.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: 12px;
  width: min(680px, calc(100vw - 24px));
  padding: 12px 14px;
  border-radius: 14px;
  background: var(--surface, oklch(99% 0.005 250));
  border: 1px solid var(--hairline, oklch(90% 0.01 250));
  box-shadow: 0 10px 30px oklch(40% 0.05 264 / 0.18);
  font-size: 13px;
  line-height: 1.4;
}
.cookie-banner__text { margin: 0; flex: 1 1 auto; color: var(--ink, oklch(30% 0.02 264)); }
.cookie-banner__text a { color: var(--accent, oklch(51% 0.16 264)); }
.cookie-banner__btn {
  flex: 0 0 auto;
  padding: 8px 16px;
  border: none;
  border-radius: 10px;
  background: var(--accent, oklch(51% 0.16 264));
  color: #fff;
  font-weight: 600;
  cursor: pointer;
}
.cookie-banner__btn:hover { filter: brightness(1.05); }
@media (max-width: 520px) {
  .cookie-banner { flex-direction: column; align-items: stretch; text-align: left; }
  .cookie-banner__btn { width: 100%; }
}

/* ===== PWA install prompt ===== */
@keyframes pwa-slide-up {
  from { opacity: 0; transform: translateX(-50%) translateY(24px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes pwa-icon-pop {
  0%   { transform: scale(0.72); }
  65%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}

.pwa-install {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%) translateY(24px);
  z-index: 1010;
  width: min(440px, calc(100vw - 24px));
  padding: 16px 16px 14px;
  border-radius: 18px;
  background: var(--surface, oklch(99.3% 0.003 255));
  border: 1px solid var(--hairline, oklch(91% 0.008 255));
  box-shadow:
    0 2px 6px oklch(40% 0.05 264 / 0.06),
    0 8px 24px oklch(40% 0.07 264 / 0.12),
    0 28px 52px -12px oklch(38% 0.08 264 / 0.16);
  opacity: 0;
  pointer-events: none;
  transition: none;
}
.pwa-install--visible {
  pointer-events: auto;
  animation: pwa-slide-up 0.38s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@media (prefers-reduced-motion: reduce) {
  .pwa-install--visible {
    animation: none;
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

.pwa-install__inner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.pwa-install__icon-wrap {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(
    135deg,
    oklch(88% 0.08 240 / 0.55),
    oklch(94% 0.05 200 / 0.35)
  );
  box-shadow:
    0 0 0 1.5px oklch(72% 0.12 240 / 0.22),
    inset 0 1px 0 oklch(100% 0 0 / 0.45);
}
.pwa-install--visible .pwa-install__icon {
  animation: pwa-icon-pop 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) 0.18s both;
}
@media (prefers-reduced-motion: reduce) {
  .pwa-install--visible .pwa-install__icon {
    animation: none;
  }
}
.pwa-install__icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  display: block;
}
.pwa-install__body {
  flex: 1 1 auto;
  min-width: 0;
  padding-top: 2px;
}
.pwa-install__title {
  margin: 0 0 3px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink, oklch(24% 0.03 264));
  line-height: 1.2;
}
.pwa-install__sub {
  margin: 0;
  font-size: 13px;
  color: var(--muted, oklch(45% 0.024 262));
  line-height: 1.45;
}
.pwa-install__close {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: color-mix(in oklch, var(--ink, oklch(24% 0.03 264)), transparent 88%);
  color: var(--muted, oklch(45% 0.024 262));
  cursor: pointer;
  transition: background 0.15s;
  padding: 0;
}
.pwa-install__close:hover {
  background: color-mix(in oklch, var(--ink, oklch(24% 0.03 264)), transparent 82%);
}

.pwa-install__actions {
  display: flex;
  gap: 8px;
  margin-top: 14px;
}
.pwa-install__btn {
  flex: 1 1 auto;
  padding: 9px 14px;
  border: none;
  border-radius: 11px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.15s, transform 0.1s;
}
.pwa-install__btn:active {
  transform: scale(0.97);
}
.pwa-install__btn--primary {
  background: linear-gradient(
    135deg,
    var(--accent, oklch(51% 0.16 264)),
    oklch(46% 0.18 280)
  );
  color: #fff;
  box-shadow: 0 2px 10px oklch(51% 0.16 264 / 0.32);
}
.pwa-install__btn--primary:hover {
  filter: brightness(1.07);
}
.pwa-install__btn--dismiss {
  background: color-mix(in oklch, var(--ink, oklch(24% 0.03 264)), transparent 92%);
  color: var(--muted, oklch(45% 0.024 262));
}
.pwa-install__btn--dismiss:hover {
  filter: brightness(0.95);
}

/* Mobile: full-width sheet, safe-area bottom inset */
@media (max-width: 480px) {
  .pwa-install {
    left: 0;
    right: 0;
    bottom: 0;
    transform: translateY(100%);
    width: 100%;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  }
  .pwa-install--visible {
    animation: none;
    opacity: 1;
    transform: translateY(0);
  }
  @media (prefers-reduced-motion: no-preference) {
    .pwa-install--visible {
      animation: pwa-sheet-up 0.38s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
    }
  }
}
@keyframes pwa-sheet-up {
  from { opacity: 0; transform: translateY(100%); }
  to   { opacity: 1; transform: translateY(0); }
}
/* ===== PWA install prompt END ===== */

/* ======================================================================
   Legal pages (privacy policy, cookies section)
   Matches the app's card-based light theme — same tokens, no new colours.
   ====================================================================== */

.legal-page {
  background: var(--bg-0);
  min-height: 100dvh;
}

.legal-shell {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(720px, calc(100% - 36px));
  margin: 0 auto;
  padding: 16px 0 56px;
}

/* Top bar — mirrors the app's .topbar look */
.legal-topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 56px;
  padding: 10px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: var(--shadow-lift);
  position: sticky;
  top: 12px;
  z-index: 20;
}

.legal-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  min-height: 34px;
  padding: 0 12px 0 8px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--glass);
  color: var(--accent);
  font-size: 0.84rem;
  font-weight: 650;
  text-decoration: none;
  transition: background 180ms var(--ease), border-color 180ms var(--ease), transform 160ms var(--ease);
}
.legal-back:hover {
  background: var(--glass-2);
  border-color: color-mix(in oklch, var(--accent), transparent 55%);
  transform: translateY(-1px);
}
.legal-back:active { transform: translateY(0); }
.legal-back svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  stroke-width: 2.2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.legal-topbar__title {
  min-width: 0;
}

.legal-topbar__title .section-kicker {
  margin-bottom: 3px;
}

.legal-topbar__title h1 {
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Revision date */
.legal-rev {
  padding: 0 2px;
  color: var(--soft);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  margin: 0;
}

/* Section cards — same as .panel */
.legal-card {
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass-strong);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-lift);
  line-height: 1.65;
}

.legal-card .section-kicker {
  margin-bottom: 4px;
}

.legal-card h2 {
  font-size: 1.08rem;
  font-weight: 600;
  margin: 0 0 10px;
  color: var(--ink);
}

.legal-card p {
  color: var(--ink);
  margin: 0 0 10px;
}
.legal-card p:last-child { margin-bottom: 0; }

.legal-card ul {
  margin: 8px 0 0;
  padding-left: 1.4em;
  color: var(--ink);
}
.legal-card ul li {
  margin-bottom: 5px;
  line-height: 1.55;
}
.legal-card ul li:last-child { margin-bottom: 0; }

/* Cookies section — subtly highlighted with an accent left-border */
.legal-card--accent {
  border-left: 3px solid var(--accent);
  background:
    linear-gradient(color-mix(in oklch, var(--accent), transparent 95%),
                    color-mix(in oklch, var(--accent), transparent 95%)),
    var(--glass-strong);
  scroll-margin-top: 80px;
}

/* Links inside cards */
.legal-card a {
  color: var(--accent);
  text-decoration: none;
}
.legal-card a:hover { text-decoration: underline; }

/* Page footer */
.legal-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 0 0;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-align: center;
}
.legal-foot a {
  color: var(--muted);
  text-decoration: none;
}
.legal-foot a:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* Responsive: collapse topbar on mobile */
@media (max-width: 520px) {
  .legal-shell {
    width: min(100% - 18px, 720px);
    padding-top: 10px;
    gap: 10px;
  }

  .legal-topbar {
    gap: 10px;
    padding: 8px 12px;
    top: 8px;
    flex-wrap: nowrap;
  }

  .legal-topbar__title h1 {
    font-size: 1rem;
  }

  .legal-card {
    padding: 16px;
  }
}

/* Footer legal row */
.app-foot__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline, oklch(90% 0.01 250));
  font-size: 12px;
  color: var(--muted, oklch(55% 0.02 264));
}
.app-foot__legal a { color: var(--muted, oklch(55% 0.02 264)); text-decoration: none; }
.app-foot__legal a:hover { color: var(--accent, oklch(51% 0.16 264)); text-decoration: underline; }

/* Геолокация не подтверждена (город определён по IP — возможно неточно при VPN):
   синяя пульсация на заголовке-триггере и кнопке «Определить моё местоположение»,
   чтобы пользователь задал точную локацию. Класс .needs-location снимается при
   явном выборе города (selectPlace). */
@keyframes geo-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--accent), transparent 45%); }
  50%      { box-shadow: 0 0 0 5px color-mix(in oklch, var(--accent), transparent 100%); }
}
.place-trigger.needs-location {
  border-color: var(--accent);
  animation: geo-pulse 2s ease-in-out infinite;
}
.location-geo.needs-location {
  border-color: var(--accent);
  background: var(--accent-soft);
  animation: geo-pulse 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .place-trigger.needs-location,
  .location-geo.needs-location { animation: none; }
  /* без анимации всё равно явно выделяем синим */
  .location-geo.needs-location { box-shadow: 0 0 0 2px color-mix(in oklch, var(--accent), transparent 55%); }
}

/* ======================================================================
   Часы в шапке (локальное время выбранного города) + попап «когда лучше»
   со слотами для прогулки/спорта.
   ====================================================================== */

/* Строка eyebrow: часы · координаты · страна. Разделитель « · » даёт каждый
   последующий непустой сегмент через ::before — тогда скрытые/пустые сегменты
   (первый визит, скрытые на мобильном координаты) не плодят лишних точек. */
.eyebrow__clock {
  color: var(--ink);
  font-weight: 650;
}
#placeCoords:not(:empty)::before,
.eyebrow__region:not(:empty)::before {
  content: " · ";
  color: var(--soft);
  font-weight: 500;
}

/* Время + бейдж «завтра»/«лучшее» в одной строке чипа окна. */
.window-chip__time {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 5px;
}
.window-chip__tag {
  font-variant-numeric: normal;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in oklch, var(--accent), transparent 84%);
  color: var(--accent);
  white-space: nowrap;
}

/* Кликабельный чип (есть несколько окон): курсор, ховер, фокус, подсказка. */
.window-chip--more {
  cursor: pointer;
  transition: border-color 140ms var(--ease), background 140ms var(--ease), transform 120ms var(--ease);
}
.window-chip--more:hover {
  border-color: color-mix(in oklch, var(--tone), transparent 45%);
}
.window-chip--more:active { transform: translateY(0.5px); }
.window-chip--more:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.window-chip__more {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-variant-numeric: normal;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--tone);
  white-space: nowrap;
}
.window-chip__more svg {
  width: 12px;
  height: 12px;
  stroke: currentColor;
  stroke-width: 2.4;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Попап со слотами ───────────────────────────────────────────────── */
.slots-pop[hidden] { display: none; }

.slots-pop {
  position: fixed;
  top: var(--slots-pop-top, 90px);
  left: var(--slots-pop-left, 12px);
  width: var(--slots-pop-width, min(360px, calc(100vw - 24px)));
  z-index: 90;
}

/* Десктоп: сам лист — плавающая карточка под чипом. */
.slots-sheet {
  display: flex;
  flex-direction: column;
  max-height: min(70vh, 520px);
  padding: 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-1);
  box-shadow: 0 24px 56px -26px oklch(45% 0.06 255 / 0.55);
  /* backwards: применяем стартовый кадр СРАЗУ при появлении (display: none→flex),
     иначе один кадр виден конечное состояние — мерцание (особенно в PWA на iOS). */
  animation: slots-card-in 160ms var(--ease) backwards;
}

.slots-sheet__grip { display: none; }
.slots-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.slots-sheet__title {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 750;
  color: var(--ink);
}
.slots-sheet__close {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
  color: var(--soft);
  cursor: pointer;
}
.slots-sheet__close svg { width: 17px; height: 17px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; }
.slots-sheet__close:hover { color: var(--ink); border-color: var(--line-strong); }

/* Переключатель активностей — чипы переносятся на несколько рядов, чтобы ВСЕ виды
   были видны и легко нажимались (без неудобной горизонтальной прокрутки). */
.slots-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}
/* Прогулка — отдельный попап без переключателя (см. renderSwitcher). */
.slots-switcher[hidden] { display: none; }
.slots-switch {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
  color: var(--muted);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 120ms var(--ease), background 120ms var(--ease), color 120ms var(--ease);
}
.slots-switch__icon { width: 15px; height: 15px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.slots-switch:hover { color: var(--ink); border-color: var(--line-strong); }
.slots-switch[aria-selected="true"] {
  background: var(--accent-soft);
  border-color: color-mix(in oklch, var(--accent), transparent 55%);
  color: var(--accent);
}

/* Список окон. */
.slots-list {
  display: grid;
  gap: 7px;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  min-height: 0;
}

.slot-row {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr);
  align-items: start;
  gap: 10px;
  padding: 10px 11px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--glass);
}
.slot-row[data-tier="great"] { --tone: var(--ok); }
.slot-row[data-tier="good"]  { --tone: var(--accent); }
.slot-row[data-tier="ok"]    { --tone: var(--warn); }
.slot-row[data-best="true"] {
  border-color: color-mix(in oklch, var(--tone), transparent 55%);
  background:
    linear-gradient(0deg, color-mix(in oklch, var(--tone), transparent 90%), color-mix(in oklch, var(--tone), transparent 90%)),
    var(--glass);
}
.slot-row__dot {
  width: 10px;
  height: 10px;
  margin-top: 5px;
  border-radius: 999px;
  background: var(--tone);
}
.slot-row__main { min-width: 0; }
.slot-row__time {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  line-height: 1.2;
}
.slot-row__time strong {
  font-size: 0.95rem;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* Несколько интервалов с одним советом — каждый со своей меткой «завтра». */
.slot-row__range {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
}
.slot-row__sep {
  color: var(--faint);
  font-weight: 700;
}
.slot-row__tier {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--tone);
}
.slot-row__reason {
  margin: 3px 0 0;
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--muted);
}
.slot-tag {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 999px;
  white-space: nowrap;
}
.slot-tag--tomorrow { background: color-mix(in oklch, var(--accent), transparent 84%); color: var(--accent); }
.slot-tag--best { background: color-mix(in oklch, var(--ok), transparent 82%); color: var(--ok); }

.slots-empty {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding: 12px;
  color: var(--muted);
  font-size: 0.85rem;
}
.slots-empty__icon { width: 20px; height: 20px; stroke: var(--soft); stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.slots-empty p { margin: 0; }

@keyframes slots-card-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
/* Проявление подложки до её рабочей плотности (0.42 при --sheet-dim = 1). Явный
   to обязателен: с одним from некоторые движки интерполируют 0→0 и фон не виден. */
@keyframes slots-backdrop-fade {
  from { opacity: 0; }
  to { opacity: 0.42; }
}

/* Мобильный: попап превращается в выезжающий снизу sheet со свайпом вниз. */
@media (max-width: 620px) {
  body.slots-open { overflow: hidden; }

  /* Шапка/таббар скрыты под тёмной подложкой попапа — гасим их backdrop-filter на
     время открытия, чтобы GPU не пересчитывал размытие за кадром анимации листа. */
  body.slots-open .topbar,
  body.slots-open .tabbar__inner { backdrop-filter: none; }

  .slots-pop {
    inset: 0;
    top: 0;
    left: 0;
    width: auto;
    z-index: 200;
    display: flex;
    align-items: flex-end;
    background: transparent;
  }
  /* Затемнение фона — отдельный композитный слой. opacity меняется на GPU без
     repaint (в отличие от анимации background-color по --sheet-dim, которая
     перерисовывала весь экран на каждом кадре свайпа → ~20fps). */
  .slots-pop::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    background: oklch(20% 0.03 255);
    opacity: calc(0.42 * var(--sheet-dim, 1));
    will-change: opacity;
    animation: slots-backdrop-fade 200ms var(--ease) backwards;
  }
  /* Плавный возврат затемнения после неполного свайпа — только на снап-бэке
     (во время перетаскивания transition не нужен, слежение мгновенное). */
  .slots-pop.is-snapping::before { transition: opacity 240ms var(--ease); }

  .slots-sheet {
    position: relative;
    z-index: 1; /* над композитным слоем затемнения (.slots-pop::before) */
    width: 100%;
    max-height: 84vh;
    padding: 8px 16px calc(16px + env(safe-area-inset-bottom));
    border: 0;
    border-radius: 22px 22px 0 0;
    box-shadow: 0 -18px 50px -20px oklch(30% 0.05 255 / 0.45);
    /* backwards: стартовый кадр (translateY(100%)) виден сразу — без мерцания. */
    animation: location-sheet-in 280ms var(--ease) backwards;
    /* Свой композитный слой: сдвиг/снап-бэк листа — чистый transform на GPU. */
    will-change: transform;
    /* Снап-бэк после неполного свайпа вешаем inline в release() (slots-popup.js),
       а НЕ постоянным transition: постоянный transform-transition сосуществует с
       анимацией появления и на iOS WebKit рисует лишний кадр — мерцание при открытии. */
    touch-action: pan-y;
  }
  .slots-sheet__grip {
    display: block;
    width: 40px;
    height: 4px;
    margin: 4px auto 12px;
    border-radius: 999px;
    background: var(--line-strong);
  }
  .slots-sheet__head { margin-bottom: 12px; }
  .slots-list { flex: 1 1 auto; max-height: none; }
  .slot-row { padding: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .slots-pop,
  .slots-sheet { animation: none; }
  .window-chip--more { transition: none; }
}

/* ─── Settings: Web Push notifications (Настройки panel) ────────────────────── */
.settings-card .panel-intro { margin-bottom: 4px; }
.settings-group[hidden] { display: none; }

.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-top: 1px solid var(--line);
}
.setting-row--master { border-top: none; padding-top: 10px; }
.settings-group .setting-row:first-child { border-top: none; }

.setting-row__label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  cursor: default;
}
.setting-row__label strong { font-weight: 600; color: var(--ink); font-size: 0.96rem; }
.setting-row__label small { color: var(--soft); font-size: 0.83rem; line-height: 1.35; }

.setting-row__ctl { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }

/* Toggle switch */
.switch {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
  width: 46px;
  height: 27px;
  cursor: pointer;
}
.switch input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.switch__track {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--line-strong);
  transition: background 0.18s ease;
}
.switch__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px oklch(40% 0.05 255 / 0.32);
  transition: transform 0.18s ease;
}
.switch input:checked + .switch__track { background: var(--accent); }
.switch input:checked + .switch__track::after { transform: translateX(19px); }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch input:disabled { cursor: not-allowed; }
.switch input:disabled + .switch__track { opacity: 0.45; }

.setting-select {
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--glass);
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  padding: 6px 10px;
  cursor: pointer;
}
.setting-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.source-pill[data-tone="ok"] { color: var(--ok-ink); }

.setting-note {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: 0.87rem;
  line-height: 1.4;
  background: var(--accent-soft);
  color: var(--ink);
}
.setting-note[data-tone="warn"] { background: oklch(68% 0.14 64 / 0.14); color: var(--warn-ink); }
.setting-note[data-tone="ok"] { background: oklch(56% 0.13 162 / 0.14); color: var(--ok-ink); }
.setting-note[hidden] { display: none; }

/* ─── Settings: «Локации» management ────────────────────────────────────────── */
.location-chip--home {
  border-color: color-mix(in oklch, var(--accent-warm), transparent 45%);
  background: color-mix(in oklch, var(--accent-warm), transparent 90%);
}
.location-chip--home .location-chip__pick { color: var(--accent-warm); }

/* Soft secondary button (used by home actions + add-current). */
.btn-soft {
  appearance: none;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--glass);
  color: var(--ink);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  padding: 8px 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease;
}
.btn-soft:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.btn-soft:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.loc-home {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 0 14px;
  border-bottom: 1px solid var(--line);
}
.loc-home__label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.loc-home__label strong { font-weight: 600; color: var(--ink); }
.loc-home__label small { color: var(--soft); font-size: 0.83rem; }

.loc-list { display: flex; flex-direction: column; }
.loc-empty { color: var(--soft); font-size: 0.88rem; line-height: 1.45; padding: 14px 0 4px; margin: 0; }

.loc-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
}
.loc-row:last-child { border-bottom: none; }
.loc-row--home { background: color-mix(in oklch, var(--accent-warm), transparent 94%); border-radius: var(--radius-sm); padding-inline: 8px; }
.loc-row__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  padding: 2px 0;
}
.loc-row__main b { font-weight: 600; color: var(--ink); font-size: 0.92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.loc-row__main span { color: var(--soft); font-size: 0.79rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.loc-row__main:hover b { color: var(--accent); }
.loc-row__ctl { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.loc-icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--faint);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.14s ease, background 0.14s ease;
}
.loc-icon:hover:not(:disabled) { color: var(--accent); background: var(--accent-soft); }
.loc-icon.is-on { color: var(--accent-warm); }
.loc-icon--del:hover:not(:disabled) { color: var(--danger); background: oklch(56% 0.18 25 / 0.1); }
.loc-icon:disabled { opacity: 0.3; cursor: default; }
.loc-add { margin-top: 14px; }
