/*
 * Oiva-kartta
 *
 * Suunnitteluperiaate: kromi on neutraali, väri on varattu datalle.
 * Kartta on jo täynnä väriä, joten painikkeet ja pinnat ovat harmaita
 * ja läpikuultavia — ainoat kylläiset värit ovat arvosanat, jolloin ne
 * erottuvat vilkaisulla. Sama sääntö pitää tumman tilan hallussa.
 *
 * Käyttöliittymä on kartta. Kaikki muu kelluu sen päällä ja avautuu
 * vasta pyydettäessä, jotta näkyvissä on mahdollisimman paljon karttaa.
 */

:root {
  color-scheme: light dark;

  /* Neutraali asteikko. Sävy on hieman sinertävä, jotta se ei riitele
     karttaruutujen lämpimän beigen kanssa. */
  --n-0: #ffffff;
  --n-50: #f7f8fa;
  --n-100: #eef0f4;
  --n-200: #e1e5eb;
  --n-300: #cbd2db;
  --n-500: #636c7a;
  --n-900: #171c24;

  --surface: rgb(255 255 255 / 0.84);
  --surface-solid: var(--n-0);
  --surface-sunken: var(--n-50);
  --border: rgb(23 28 36 / 0.1);
  --text: var(--n-900);
  --text-dim: var(--n-500);

  /* Arvosanavärit. Jokaisen kontrasti --grade-inkiä vasten on mitattu
     ja vähintään 4,5:1 (WCAG AA), jotta kirjain erottuu myös pienessä
     pinnissä ja nipun luku 13 pikselin koossa.

     Mitatut suhteet valkoista vasten: oivallinen 5,87 · hyvä 4,59 ·
     korjattavaa 4,59 · huono 5,85 · ei tietoa 4,59. Kolme näistä
     alitti aiemmin rajan (4,20 / 4,24 / 3,83), ja axe-core löysi
     niistä rikkeitä nipuissa. Jos muutat arvoa, laske suhde uudelleen
     — väite on tässä kommentissa, ja väärä väite on pahempi kuin
     puuttuva. */
  --grade-oivallinen: #157347;
  --grade-hyva: #46841e;
  --grade-korjattavaa: #aa6500;
  --grade-huono: #c2261b;
  --grade-unknown: #6e7682;
  --grade-ink: #fff;

  --accent: #0b6bcb;
  --accent-ink: #fff;

  /* Suosikin väri. Ainoa kylläinen väri joka ei ole arvosana — siksi se
     on selvästi asteikon ulkopuolelta, jottei sitä lueta arvosanaksi. */
  --favourite: #d6336c;

  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-full: 999px;

  --shadow-1: 0 1px 2px rgb(23 28 36 / 0.08), 0 2px 8px rgb(23 28 36 / 0.06);
  --shadow-2: 0 2px 6px rgb(23 28 36 / 0.1), 0 12px 32px rgb(23 28 36 / 0.14);

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);

  --glass: blur(20px) saturate(180%);
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Inter, system-ui, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --n-0: #0e1116;
    --n-50: #151a21;
    --n-100: #1c222b;
    --n-200: #262e39;
    --n-300: #39434f;
    --n-500: #98a2af;
    --n-900: #f2f5f8;

    --surface: rgb(20 25 32 / 0.88);
    --surface-solid: #11161d;
    --surface-sunken: #151a21;
    --border: rgb(255 255 255 / 0.1);

    /* Tummalla taustalla samat sävyt näyttäisivät sameilta, joten ne
       kirkastetaan. Sävy pysyy, kylläisyys nousee. */
    --grade-oivallinen: #2fa96a;
    --grade-hyva: #7cc23a;
    --grade-korjattavaa: #e0961f;
    --grade-huono: #f0554a;
    --grade-unknown: #8b95a3;
    --grade-ink: #1a1a1a;

    --accent: #63a4ff;
    --accent-ink: #0e1116;
    --favourite: #ff6b9d;

    --shadow-1: 0 1px 2px rgb(0 0 0 / 0.5);
    --shadow-2: 0 2px 8px rgb(0 0 0 / 0.5), 0 16px 40px rgb(0 0 0 / 0.45);
  }
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  /* dvh seuraa Safarin katoavaa osoiteriviä; vh jättäisi palkin alle
     jäävän kaistaleen tavoittamattomiin. */
  height: 100dvh;
  overflow: hidden;
  font-family: var(--font);
  color: var(--text);
  background: var(--surface-solid);
  -webkit-text-size-adjust: 100%;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
}

#map {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--surface-sunken);
}

.leaflet-container {
  font-family: var(--font);
  background: var(--surface-sunken);
}

/* OpenStreetMap-data on ODbL-lisensoitua ja vaatii lähdeviittauksen,
   joten attribuutio on näkyvissä vaikka muu kromi on piilotettu. */
.leaflet-control-attribution {
  padding: 2px 8px !important;
  border-radius: var(--r-full) 0 0 0;
  background: var(--surface) !important;
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  color: var(--text-dim) !important;
  font-size: 0.75rem !important;
}

.leaflet-control-attribution a {
  color: var(--text-dim) !important;
}

.leaflet-bottom.leaflet-right {
  margin-bottom: var(--safe-b);
  margin-right: var(--safe-r);
}

/* --- Tietosuojaseloste (T2) ---------------------------------------------- */

/*
 * Selosteella on oma sivunsa, joten se ei peri kartan kokosivuasettelua.
 * `html, body { height: 100dvh; overflow: hidden }` on kartalle oikein
 * muttei tekstisivulle, jolla on vieritettävää.
 */
body.sivu {
  height: auto;
  min-height: 100dvh;
  overflow: auto;
  background: var(--surface-solid);
}

.seloste-sivu {
  max-width: 44rem;
  margin: 0 auto;
  padding: calc(var(--safe-t) + 1.5rem) calc(var(--safe-r) + 1.25rem)
    calc(var(--safe-b) + 3rem) calc(var(--safe-l) + 1.25rem);
}

.seloste__takaisin {
  display: inline-block;
  margin-bottom: 1.25rem;
  color: var(--accent);
  font-size: 0.875rem;
  font-weight: 600;
  text-decoration: none;
}

.seloste-sivu h1 {
  margin: 0 0 1rem;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
  /* Pitkä yhdyssana ("Tietosuojaseloste", "Dataskyddsbeskrivning") valui
     200 %:n tekstillä 393 px:n näytön reunan yli (A20). */
  overflow-wrap: anywhere;
  hyphens: auto;
}

.seloste-sivu .chips {
  margin-bottom: 1.5rem;
}

.seloste h2 {
  margin: 1.75rem 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
  /* "PERSONUPPGIFTSANSVARIG" katkesi reunaan 200 %:n tekstillä (A20). */
  overflow-wrap: anywhere;
  hyphens: auto;
}

.seloste p {
  margin: 0 0 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.6;
}

.seloste__johdanto {
  font-size: 1rem;
}

.seloste__paivitetty {
  margin-top: 1.5rem;
  color: var(--text-dim);
  font-size: 0.8125rem;
}

/* --- Työkalupalkki ------------------------------------------------------ */

.toolbar {
  position: absolute;
  /* Paneelien (520) yläpuolella, koska valikkopaneeli on otsakkeen
     sisällä (A39): muuten karttalappu (500) maalautuisi sen päälle.
     Tuontikehote (530) jää yhä kaikkien paneelien päälle. */
  z-index: 525;
  inset: calc(var(--safe-t) + 10px) calc(var(--safe-r) + 12px) auto calc(var(--safe-l) + 12px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  pointer-events: none;
}

.toolbar > * {
  pointer-events: auto;
}

/*
 * Sanamerkki.
 *
 * Aiemmin tässä oli vihreä "O"-laatta ja sen vieressä nimi. Samalla
 * rivillä oli silloin kolme visuaalista kieltä: vihreä laatta, vaalea
 * pilleri ja tummat työkalupainikkeet. Vihreä on tässä sovelluksessa
 * varattu arvosanoille, joten sen käyttö tunnuksessa vei huomiota
 * juuri siltä tiedolta jota varten sovellus on olemassa.
 *
 * Nyt jäljellä on pelkkä nimi samassa laatikossa kuin työkalut:
 * sama pinta, sama pyöristys (`--r-md`), sama varjo. Täysin pyöreä
 * pilleri erottui työkalupainikkeiden pehmeistä neliöistä omaksi
 * muodokseen; sama kulma sitoo ne yhdeksi riviksi.
 *
 * NIMI ON VALIKKOPAINIKE (A39). Laatikko on painikkeessa eikä
 * otsikossa: otsikon `overflow: hidden` leikkaisi painikkeen varjon ja
 * kohdistusrenkaan. Korkeus on sama kuin työkaluilla, 42 px.
 */
.brand {
  display: flex;
  margin: 0;
  font-size: 1rem;
  font-weight: 640;
  letter-spacing: -0.02em;

  /*
   * SANAMERKKI KUTISTUU, TYÖKALUT EIVÄT (A9).
   *
   * Fonttikoot ovat rem-yksiköissä, joten selaimen suurempi
   * oletusfonttikoko kasvattaa myös tunnusta. MITATTU 320 pikselin
   * näytöllä: juurikoolla 24 px `.toolbar__actions` ulottui kohtaan
   * 329 ja laskurimerkki 333 — eli neljäs painike oli ruudun
   * ulkopuolella ja sivu sai vaakavierityksen.
   *
   * Tunnus on sovelluksen nimi eikä toiminto; sen katkaiseminen on
   * halvempi hinta kuin painikkeen katoaminen. Sama periaate kuin
   * 380 pikselin mediakyselyssä, joka kutistaa tunnusta eikä
   * painikkeita — vain nyt se toimii myös fonttikoon kasvaessa.
   * Katkaisu on `.brand__nimi`ssä: nuoli jää aina näkyviin.
   */
  min-width: 0;
}

.brand__painike {
  display: flex;
  align-items: center;
  gap: 3px;
  min-width: 0;
  min-height: 42px;
  padding: 8px 9px 8px 15px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow-1);
  color: var(--text);
  font: inherit;
  letter-spacing: inherit;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}

.brand__painike:hover {
  background: var(--surface-solid);
}

/* Auki sama käänteinen tila kuin työkalupainikkeilla. */
.brand__painike[aria-expanded='true'] {
  border-color: transparent;
  background: var(--n-900);
  color: var(--n-0);
}

.brand__nimi {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.brand__nuoli {
  flex: 0 0 auto;
  width: 1.125em;
  height: 1.125em;
  color: var(--text-dim);
  transition: transform 0.15s ease;
}

.brand__painike[aria-expanded='true'] .brand__nuoli {
  color: inherit;
  transform: rotate(180deg);
}

/* Ohituslinkki näppäimistökäyttäjille */
.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 10000;
  padding: 10px 16px;
  background: var(--surface-solid);
  color: var(--accent);
  border: 2px solid var(--accent);
  border-radius: var(--r-md);
  font-weight: 600;
  text-decoration: none;
  box-shadow: var(--shadow-2);
}

.skip-link:focus {
  top: 16px;
}

/*
 * Ruudunlukijalle, ei silmälle. Käytetään hakutulosten yhteenvetoon
 * (A4): sama tieto on jo näkyvissä tuloslistassa, joten toistaminen
 * ruudulla olisi kohinaa — mutta live-alueeksi tarvitaan lyhyt teksti
 * eikä koko listaa.
 *
 * `clip-path` eikä `display: none`: piilotettu elementti ei ole
 * saavutettavuuspuussa lainkaan, eikä live-alue voi silloin ilmoittaa
 * mitään.
 */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.toolbar__actions {
  display: flex;
  gap: 8px;
  /* Työkalut eivät kutistu eivätkä kasva: sormenpäätä ei voi
     pienentää, ja tunnus antaa periksi ensin (A9). */
  flex: 0 0 auto;
}

/*
 * Kapealla näytöllä neljä painiketta ja tunnus eivät mahdu rinnakkain:
 * 320 pikselin näytöllä oikeanpuoleisin leikkautui reunan yli.
 * Sanamerkki kutistuu, ei katoa — pelkkä tyhjä pilleri olisi huonompi
 * kuin pieni nimi. Painikkeet pysyvät 42 pikselissä, koska sormenpäätä
 * ei voi pienentää.
 */
@media (max-width: 380px) {
  .brand {
    font-size: 0.875rem;
  }

  .brand__painike {
    padding: 7px 7px 7px 11px;
    border-radius: var(--r-sm);
  }

  .toolbar__actions {
    gap: 6px;
  }
}

/* Neliömäinen työkalupainike. Aktiivinen tila on käänteinen: tumma
   pinta ja vaalea ikoni, jotta sen näkee vilkaisulla kartan päältä. */
.tool {
  position: relative;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow-1);
  color: var(--text);
  transition: background 0.15s ease, color 0.15s ease;
}

.tool svg {
  width: 21px;
  height: 21px;
}

.tool:hover {
  background: var(--surface-solid);
}

.tool.is-on,
.tool[aria-expanded='true'] {
  border-color: transparent;
  background: var(--n-900);
  color: var(--n-0);
}

.tool[aria-pressed='true'] {
  color: var(--accent);
}

.tool[aria-pressed='true'].is-on {
  color: var(--n-0);
}

.tool[aria-busy='true'] svg {
  animation: spin 1.1s linear infinite;
}

/*
 * Työkaluvihje (A10).
 *
 * Vain osoitinlaitteilla: kosketusnäytöllä `:hover` jää päälle
 * napautuksen jälkeen, ja kupla peittäisi karttaa ilman että kukaan
 * pyysi sitä. Puhelin on tämän sovelluksen pääalusta, joten vihje on
 * nimenomaan työpöydän apu.
 *
 * `aria-hidden`-vastine syntyy itsestään: painikkeella on `aria-label`,
 * joka varjostaa sisällön saavutettavuuspuussa, joten pseudoelementti
 * ei tule luetuksi. Tämä on mitattu CDP:llä eikä oletettu.
 */
@media (hover: hover) and (pointer: fine) {
  .tool[data-vihje]::after {
    content: attr(data-vihje);
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1;
    padding: 5px 9px;
    border-radius: var(--r-sm);
    background: var(--n-900);
    color: var(--n-0);
    font-size: 0.75rem;
    font-weight: 500;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
  }

  .tool[data-vihje]:hover::after,
  .tool[data-vihje]:focus-visible::after {
    opacity: 1;
  }

  /* Kartan kulmassa vihje nousee ylös: alla on lähdeviittaus (A39). */
  .tool--kartta[data-vihje]::after {
    top: auto;
    bottom: calc(100% + 8px);
  }
}

/*
 * PAIKANNA KARTAN OIKEASSA ALAKULMASSA (A39).
 *
 * Lähdeviittauksen yläpuolella. `--attribuutio` on sen mitattu korkeus:
 * suurella tekstillä viittaus rivittyy kahdelle riville, ja kiinteä
 * etäisyys jättäisi painikkeen sen päälle. Alareunan pino jättää
 * oikealle tilaa painikkeelle (`.alareuna`), ja `--alavara` mitataan
 * myös painikkeesta, joten paneelit ja popupit päättyvät sen yläpuolelle.
 */
.tool--kartta {
  position: absolute;
  z-index: 500;
  right: calc(var(--safe-r) + 12px);
  bottom: calc(var(--safe-b) + var(--attribuutio, 18px) + 10px);
}

.tool__badge {
  position: absolute;
  top: -5px;
  right: -5px;
  display: grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--r-full);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.6875rem;
  font-weight: 700;
}

.tool__badge[hidden] {
  display: none;
}

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

/* --- Paneelit ----------------------------------------------------------- */

/*
 * Suodattimet ja asetukset avautuvat työkalupainikkeen alta. Kapealla
 * näytöllä paneeli venyy reunasta reunaan, leveällä se pysyy painikkeen
 * levyisenä kolumnina.
 */
.panel {
  position: absolute;
  z-index: 520;
  top: calc(var(--safe-t) + 64px);
  right: calc(var(--safe-r) + 12px);
  left: calc(var(--safe-l) + 12px);
  max-width: 340px;
  margin-left: auto;
  display: grid;
  /* Sarake ei kasva sisältönsä luonnollista leveyttä leveämmäksi (A20).
     Pelkkä `display: grid` tekee sarakkeesta `auto`, jonka vähimmäisleveys
     on levein rivittymätön lapsi: 200 %:n tekstillä hakukenttä venytti
     hakupaneelin sisällön 400 px:iin ja siirtopainikkeet
     suodatinpaneelin 391 px:iin 338 px:n tilassa, ja paneeli vieritti
     sivuttain (WCAG 1.4.4, 1.4.10). */
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow-2);
  animation: panel-in 0.18s cubic-bezier(0.32, 0.72, 0, 1);

  /* Paneeli kasvoi kielivalitsimen myötä, ja ruotsiksi tekstit ovat
     pisimmät: lähdetiedot ja vastuuvapaus valuivat ruudun alapuolelle
     eikä niihin päässyt käsiksi mitenkään. Mikään ei rikkoutunut —
     sisältöä ei vain ollut. Korkeusraja ja vieritys hoitavat sen.

     `overscroll-behavior: contain` estää kartan siirtymisen silloin kun
     vieritys osuu paneelin päähän. */
  max-height: calc(100dvh - var(--safe-t) - var(--safe-b) - 88px);

  /* PANEELI PÄÄTTYY ALAREUNAN PINON YLÄPUOLELLE (A18). Paneeli peitti
     tilarivin ja tuontikehotteen: kehotteen painikkeisiin pääsi
     sarkaimella, vaikka paneeli peitti ne kokonaan (WCAG 2.4.11).
     `--alavara` on pinon viemä tila (A19). Alaraja 40dvh pitää paneelin
     käytettävänä, kun suuri teksti kasvattaa pinon korkeaksi; silloin
     kehote on silti paneelin päällä, ks. `.status.status--tuonti`. */
  max-height: max(40dvh, calc(100dvh - var(--safe-t) - 64px - max(var(--alavara, 28px), var(--safe-b) + 24px)));
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.panel[hidden] {
  display: none;
}

/*
 * Valikko avautuu nimen alta vasemmalle (A39). Se on otsakkeen sisällä
 * sarkainjärjestyksen vuoksi, joten sijainti on suhteessa näkymään eikä
 * otsakkeeseen — samat etäisyydet kuin muilla paneeleilla.
 */
.panel--valikko {
  position: fixed;
  margin-left: 0;
  margin-right: auto;
}

/* Ensimmäinen osio-otsikko ei tarvitse erotinviivaa yläpuolelleen. */
.panel--valikko > .panel__title--section:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

.panel__title {
  margin: 0;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.panel__note {
  margin: 0;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.6875rem;
  line-height: 1.5;
}

/* Versio omalle rivilleen: se on eri asia kuin lähdeviittaus, ja
   ODbL edellyttää että lähde on näkyvissä eikä huku muun sekaan. */
.panel__versio {
  display: block;
  margin-top: 4px;
  /* opacity: 0.75; poistettu riittävän kontrastin (AA) varmistamiseksi (H-23) */
  font-variant-numeric: tabular-nums;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chip {
  padding: 6px 13px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: transparent;
  color: var(--text-dim);
  font-size: 0.8438rem;
  font-weight: 550;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}

.chip:hover {
  color: var(--text);
}

.chip.is-on {
  border-color: transparent;
  background: var(--n-900);
  color: var(--n-0);
}

.chip__dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}

.chip__dot[data-grade="oivallinen"] { background: var(--grade-oivallinen); }
.chip__dot[data-grade="hyva"] { background: var(--grade-hyva); }
.chip__dot[data-grade="korjattavaa"] { background: var(--grade-korjattavaa); }
.chip__dot[data-grade="huono"] { background: var(--grade-huono); }

/* --- Haku --------------------------------------------------------------- */

.search {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
}

/*
 * Kohdistusilmaisin hakukentälle (A12).
 *
 * `.search__input:focus` poistaa selaimen oman renkaan (`outline: none`),
 * ja korvaava merkki oli pelkkä yhden pikselin reunavärin vaihdos
 * `rgba(23,28,36,0.1)` → `rgb(11,107,203)`. Se on WCAG 2.4.7:n
 * mielessä näkyvä, mutta selvästi ohuempi kuin poistettu rengas.
 *
 * Kahden pikselin kehä tekee siitä yhtä vahvan. Väri on aksentti, jonka
 * kontrasti paneelin pintaa vasten on mitattu 5,28:1.
 */
.search:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}

.search__icon {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  color: var(--text-dim);
}

.search__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 11px 0;
  border: 0;
  background: none;
  color: var(--text);
  /* 16 px estää Safaria zoomaamasta kenttään napautettaessa. */
  font: inherit;
  font-size: max(1rem, 16px);
}

.search__input:focus {
  outline: none;
}

/* Safarin oma tyhjennysristi on kartan päällä liian huomaamaton. */
.search__input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 17px;
  height: 17px;
  background: var(--text-dim);
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 100 20 10 10 0 000-20zm3.5 12.1-1.4 1.4L12 13.4l-2.1 2.1-1.4-1.4L10.6 12 8.5 9.9l1.4-1.4L12 10.6l2.1-2.1 1.4 1.4L13.4 12z'/%3E%3C/svg%3E")
    center / contain no-repeat;
  cursor: pointer;
}

.search__results {
  display: grid;
  gap: 10px;
  max-height: min(56dvh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.search__results:empty {
  display: none;
}

/* Osio erottaa lähellä olevat koko maan osumista: edelliset ovat
   kartalla ja niillä on etäisyys, jälkimmäisillä on vain osoite. */
.search__section {
  display: grid;
  gap: 2px;
}

.search__label {
  margin: 0 0 2px;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.search__note {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.7812rem;
  line-height: 1.45;
}

/* Lähellä-osion avaus (A23). Kohde 24 px, tasattu rivien tekstiin. */
.search__lisaa {
  justify-self: start;
  min-height: 24px;
  margin: 2px 0 6px 42px;
  padding: 2px 7px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--accent);
  font-size: 0.7812rem;
  font-weight: 600;
}

.search__lisaa:hover {
  background: var(--surface-sunken);
}

/*
 * Epäonnistunut haku erottuu tyhjästä tuloksesta (T-4). Sama väri kuin
 * `.status--error`illa, koska kyse on samasta asiasta eikä arvosanasta:
 * kielto koskee tuoreus- ja aukiolomerkintöjä, jotka esiintyvät
 * arvosanalapun vieressä.
 */
.search__note--error {
  color: var(--grade-huono);
}

/* --- Paikkalista -------------------------------------------------------- */

/*
 * Sama tieto kuin kartalla, mutta luettavassa järjestyksessä: lähin
 * ensin. Lista vierii paneelin sisällä, jotta paneeli itse ei kasva
 * ruudun mittaiseksi.
 */
.list {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  max-height: min(56dvh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  list-style: none;
}

.row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 8px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  text-align: left;
  transition: background 0.12s ease;
}

a.row {
  color: inherit;
  text-decoration: none;
}

.row:hover {
  background: var(--surface-sunken);
}

/* Koko maan osuma vie Oiva-raporttiin, ei kartalle: aineistossa ei ole
   koordinaatteja, joten nuoli kertoo että tästä poistutaan sivulta. */
.row__out {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: var(--text-dim);
}

/* Koko maan hakuosuma: raportti ja karttasiirtymä ovat kaksi eri
   toimintoa, joten ne ovat sisaruksia (I-5). Painike linkin sisällä
   olisi kelvotonta merkkausta, eikä sitä voi kiertää. */
.tulos {
  display: grid;
  gap: 0;
}

/* Sisennys kohdistaa toiminnon rivin TEKSTIIN eikä sen lappuun:
   rivin täyte 8 + lapun leveys 24 + väli 10 = 42 px. */
.tulos__kartalle {
  justify-self: start;
  /* WCAG 2.2 kohdekoko (2.5.8) on 24 x 24 CSS-pikseliä. Täyte yksin ei
     riitä pieneen tekstiin, joten korkeus on nimenomainen. */
  min-height: 24px;
  margin: 0 0 4px 42px;
  padding: 2px 7px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--accent);
  font-size: 0.7188rem;
  font-weight: 600;
}

.tulos__kartalle:hover {
  background: var(--surface-sunken);
}

.row.is-selected {
  background: var(--surface-sunken);
  box-shadow: inset 0 0 0 1px var(--border);
}

.row__text {
  display: grid;
  gap: 1px;
  min-width: 0;
  flex: 1 1 auto;
}

/* Nimi ja metarivi rivittyvät eivätkä katkea kolmeen pisteeseen (A20).
   Mitattuna 200 %:n tekstillä jokaisen listarivin metarivi (450/450)
   ja kolmasosa nimistä (155/450) katkesi, jolloin arvosanan nimi ja
   tuoreus jäivät näkymättä. Normaalilla tekstillä 393 px:n näytöllä
   muutos koskee 3 nimeä ja 7 metariviä 450:stä. */
.row__name {
  font-size: 0.8438rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  overflow-wrap: anywhere;
}

.row__meta {
  color: var(--text-dim);
  font-size: 0.7188rem;
  overflow-wrap: anywhere;
}

.row__distance {
  flex: 0 0 auto;
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.list[hidden] {
  display: none;
}

/* Osion otsikko listan sisällä: suosikit ennen lähellä olevia. */
.list__label {
  margin: 6px 0 1px;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.list__label:first-child {
  margin-top: 0;
}

/* Suosikkiosion siirtopainike (A37). Sama muoto kuin Lähellä-osion
   avauksella (A23): toiminto eikä rivi, kohde 24 px, tasattu rivien
   tekstiin. */
.list__siirto {
  min-height: 24px;
  margin: 0 0 4px 42px;
  padding: 2px 7px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--accent);
  font-size: 0.7812rem;
  font-weight: 600;
  text-align: left;
}

.list__siirto:hover {
  background: var(--surface-sunken);
}

.row__heart {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: var(--favourite);
}

/* Alareunan häivytys kertoo, että listaa on enemmän kuin näkyy. */
.list.is-scrollable {
  mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
}

.list__empty {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.8125rem;
}

.list__empty[hidden] {
  display: none;
}

/* --- Asetusten valitsimet ---------------------------------------------- */

.toggle {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  cursor: pointer;
}

.toggle input {
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  margin: 1px 0 0;
  accent-color: var(--accent);
}

.toggle__text {
  display: grid;
  gap: 2px;
  font-size: 0.8438rem;
  font-weight: 550;
  line-height: 1.35;
}

/* Tallennettujen tietojen poisto (T3). Sama laputyyli kuin muillakin
   toiminnoilla, mutta oma luokkansa: tämä ei ole valinta jolla on
   päällä/pois-tila, vaan kertatoiminto. */
.panel__linkki {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
}

.panel__poisto {
  display: grid;
  gap: 6px;
  margin: 0;
  justify-items: start;
}

.panel__poisto small {
  color: var(--text-dim);
  font-size: 0.7188rem;
  line-height: 1.4;
}

/* Toimintopainikkeiden tekstit rivittyvät (A20). Valintalaput ovat
   lyhyitä sanoja, mutta "Import favourites from a file" oli 200 %:n
   tekstillä 359 px leveä 306 px:n tilassa, eikä `.chip` rivity.

   TOIMINTO EI NÄYTÄ POIS PÄÄLTÄ OLEVALTA LAPULTA (A35). Toiminnoilla ei
   ollut omaa tyyliä, joten "Vie suosikit tiedostoon" näytti samalta kuin
   valitsematon paikkatyyppi, ja kehotteen "Tuo" erosi "Älä tuo"
   -painikkeesta vain kohdistusrenkaalla. */
.chip--toiminto {
  border-color: var(--border);
  background: var(--surface-sunken);
  color: var(--accent);
  white-space: normal;
  text-align: left;
}

.chip--toiminto:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* Poistotoiminto erottuu muista (A16). Ennen poisto näytti samalta kuin
   tuontipainikkeet sen yläpuolella, ja yksi napautus hävitti kaikki
   suosikit. Punainen on sama kuin virheillä, eli merkitys on jo tuttu. */
.chip--vaara {
  border-color: var(--grade-huono);
  color: var(--grade-huono);
}

.chip--vaara:hover {
  color: var(--grade-huono);
}

.panel__vahvistus {
  display: grid;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--grade-huono);
  border-radius: var(--r-sm);
}

.panel__vahvistus[hidden] {
  display: none;
}

.panel__vahvistus-teksti {
  margin: 0;
  font-size: 0.8438rem;
  line-height: 1.45;
}

.panel__vahvistus-toiminnot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
}

/* Varsinainen poisto on täytetty: se on ainoa peruuttamaton painallus.
   `--grade-ink` on valkoinen vaalealla ja lähes musta tummalla teemalla,
   jolloin kontrasti riittää molemmissa. */
.panel__vahvistus .chip--vaara {
  border-color: transparent;
  background: var(--grade-huono);
  color: var(--grade-ink);
}

/* Paneelin tilarivi (A16). Tyhjänäkin se on olemassa, jotta ilmoitus
   kuuluu; negatiivinen marginaali kumoaa paneelin välin tyhjältä riviltä. */
.panel__tila {
  margin: 0;
  font-size: 0.8438rem;
  font-weight: 550;
  line-height: 1.45;
}

.panel__tila:empty {
  margin-top: -12px;
}

.panel__tila--virhe {
  color: var(--grade-huono);
}

/* Kumoamispainikkeen rivi (A34). */
.panel__kumoa {
  margin: 0;
}

/* Suosikkien siirto (I-3, I-10). Kolme painiketta kääriytyy kapealla
   näytöllä; selite on omalla rivillään kuten poisto-osiossakin. */
.panel__siirto {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
}

.panel__siirto small {
  flex: 1 0 100%;
  color: var(--text-dim);
  font-size: 0.7188rem;
  line-height: 1.4;
}

/* Varoitus erottuu selitteestä painolla eikä värillä: oranssi jäisi
   läpikuultavalla paneelilla pienessä tekstissä kontrastirajan alle. */
.panel__siirto .panel__varoitus {
  color: var(--text);
  font-weight: 600;
}

.panel__varoitus[hidden] {
  display: none;
}

/* Tuontiehdotus on tilarivin sukulainen: sama pinta ja sama paikka,
   mutta se kysyy eikä kerro, joten siinä on painikkeet (I-10). */
.status--tuonti {
  display: grid;
  gap: 10px;
}

.tuonti__teksti {
  margin: 0;
}

.tuonti__toiminnot {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 0;
}

.status--tuonti[hidden] {
  display: none;
}

.toggle__text small {
  color: var(--text-dim);
  font-size: 0.7188rem;
  font-weight: 450;
  line-height: 1.4;
}

/* --- Kartan lappu (A21) ------------------------------------------------ */

/* Työkalupalkin alla vasemmalla: popupin panorointivara (104 px ylhäältä)
   pitää popupit sen alapuolella, ja paneelit (z-index 520) peittävät sen,
   kun ne ovat auki. */
.karttalappu {
  position: absolute;
  z-index: 500;
  top: calc(var(--safe-t) + 64px);
  left: calc(var(--safe-l) + 12px);
  max-width: calc(100% - 24px);
  padding: 5px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--surface);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow-1);
  color: var(--text);
  font-size: 0.75rem;
  font-weight: 600;
}

.karttalappu[hidden] {
  display: none;
}

/* --- Alareunan pino (A19) ---------------------------------------------- */

/*
 * Pilleri, tilarivi ja tuontikehote pinotaan alhaalta ylös. Ennen jokainen
 * oli sijoitettu erikseen `left: 50%` ja `translateX(-50%)` -parilla, mikä
 * rajasi leveyden puoleen ruudusta: tilarivi oli 197 px leveä 393 px:n
 * näytöllä, vaikka `max-width` salli 340 px. Viestit rivittyivät 2–7
 * riville, ja 200 %:n tekstillä offline-kortti kasvoi 415 px korkeaksi
 * pillerin päälle.
 *
 * Pinolla ei ole omaa `z-index`iä eikä muutakaan pinoamiskontekstin
 * luovaa ominaisuutta: lasten `z-index` vertautuu siksi paneeleihin
 * samassa kontekstissa kuin ennenkin. Tyhjä pinon alue ei ota
 * napautuksia, joten kartta toimii sen alla.
 */
.alareuna {
  position: absolute;
  left: calc(var(--safe-l) + 16px);
  /* Tila Paikanna-painikkeelle (A39): 12 + 42 + 10 px. */
  right: calc(var(--safe-r) + 64px);
  bottom: calc(var(--safe-b) + 30px);
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 10px;
  pointer-events: none;
}

.alareuna > * {
  pointer-events: auto;
}

/* --- Kelluva "seuraa sijaintia" ---------------------------------------- */

.pill {
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 16px 9px 13px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--surface);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow-2);
  color: var(--accent);
  font-size: 0.875rem;
  font-weight: 600;
}

.pill[hidden] {
  display: none;
}

.pill svg {
  width: 17px;
  height: 17px;
}

/* --- Tilakortti --------------------------------------------------------- */

/*
 * Näytetään vain kun on jotain kerrottavaa. Onnistunut haku puhuu
 * karttamerkeillä, eikä siitä tarvitse erikseen ilmoittaa.
 */
.status {
  z-index: 500;
  max-width: min(340px, 100%);
  padding: 11px 16px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow-2);
  color: var(--text);
  font-size: 0.8438rem;
  line-height: 1.45;
  text-align: center;
}

.status[hidden] {
  display: none;
}

/* Ostosivun palautuslomake (public/osta.js). */
.osto__lomake {
  display: grid;
  gap: 10px;
  max-width: 360px;
}

.osto__lomake label {
  display: grid;
  gap: 4px;
}

.osto__lomake input {
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font: inherit;
}

.seloste__tila:empty {
  display: none;
}

/* Muut maat lisenssillä: sama laatikko kuin tilarivi, linkki omalla rivillään. */
.muut-maat p {
  margin: 0 0 6px;
}

.muut-maat a {
  color: var(--accent);
  font-weight: 600;
}

/* Tanskan smiley: virallinen vihreä valkoisella (Fødevarestyrelsenin ehdot). */
.pin[data-grade='dk'],
.badge[data-grade='dk'] {
  background: #fff;
  color: #007953;
  box-shadow: inset 0 0 0 2px #007953;
}

.kasvo {
  width: 1.15em;
  height: 1.15em;
  display: block;
}

.status--error {
  background: var(--surface-solid);
  color: var(--grade-huono);
}

/* Palvelimen tekninen selitys toissijaisena rivinä (A27). */
.status__lisatieto {
  display: block;
  margin-top: 4px;
  color: var(--text-dim);
  font-size: 0.75rem;
}

/* Kehote vaatii vastauksen, joten se on paneelien yläpuolella (A18).
   Kaksiosainen valitsin, koska `.status` on tiedostossa myöhemmin ja
   voittaisi yksiosaisen `.status--tuonti`n. */
.status.status--tuonti {
  z-index: 530;
}

/* --- Karttamerkit ------------------------------------------------------- */

/*
 * Pisara: pyöristetty neliö 45° kierrettynä, jolloin yksi terävä kulma
 * osoittaa tarkkaan sijaintiin. Sisältö kierretään takaisin, jotta
 * kirjain pysyy pystyssä.
 */
.pin {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 2px solid var(--n-0);
  border-radius: 50% 50% 3px 50%;
  transform: rotate(45deg);
  background: var(--grade-unknown);
  box-shadow: 0 2px 5px rgb(0 0 0 / 0.3);
  color: var(--grade-ink);
  font-size: 0.8125rem;
  font-weight: 700;
  transition: transform 0.15s ease;
}

.pin > span {
  transform: rotate(-45deg);
  line-height: 1;
}

.pin[data-grade='oivallinen'] { background: var(--grade-oivallinen); }
.pin[data-grade='hyva'] { background: var(--grade-hyva); }
.pin[data-grade='korjattavaa'] { background: var(--grade-korjattavaa); }
.pin[data-grade='huono'] { background: var(--grade-huono); }

.pin--active {
  transform: rotate(45deg) scale(1.25);
}

/*
 * Suosikkipinni saa sydämen ylänurkkaan. Pinni on kierretty 45°, joten
 * merkki asetetaan kierretyssä koordinaatistossa vasempaan ylänurkkaan
 * — ruudulla se osuu pinnin yläreunaan.
 */
.pin--favourite::after {
  content: '';
  position: absolute;
  top: -7px;
  left: -7px;
  width: 15px;
  height: 15px;
  background: var(--favourite);
  /* Sama sydän kuin popupissa ja listassa, jotta merkki on tunnistettava
     samaksi asiaksi. Maski kääntyy pinnin mukana, mutta sydän on lähes
     symmetrinen eikä 45 astetta haittaa sitä. */
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21 3.6 12.6a5.5 5.5 0 0 1 7.8-7.8l.6.6.6-.6a5.5 5.5 0 0 1 7.8 7.8Z'/%3E%3C/svg%3E")
    center / contain no-repeat;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21 3.6 12.6a5.5 5.5 0 0 1 7.8-7.8l.6.6.6-.6a5.5 5.5 0 0 1 7.8 7.8Z'/%3E%3C/svg%3E")
    center / contain no-repeat;
  transform: rotate(-45deg);
  filter: drop-shadow(0 0 1px var(--n-0)) drop-shadow(0 0 1px var(--n-0));
}

.pin {
  position: relative;
}

/*
 * Nippu: pyöreä, jotta sen erottaa yhdestä kohteesta jo muodosta.
 * Väri on joukon huonoin arvosana — sama sääntö kuin Oivan omassa
 * kokonaisarvosanassa, yhtä tasoa ylempänä.
 */
.cluster {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border: 2px solid var(--n-0);
  border-radius: 50%;
  background: var(--grade-unknown);
  box-shadow: 0 2px 6px rgb(0 0 0 / 0.3);
  color: var(--grade-ink);
  font-size: 0.8125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.cluster[data-grade='oivallinen'] { background: var(--grade-oivallinen); }
.cluster[data-grade='hyva'] { background: var(--grade-hyva); }
.cluster[data-grade='korjattavaa'] { background: var(--grade-korjattavaa); }
.cluster[data-grade='huono'] { background: var(--grade-huono); }

/* Oma sijainti: sininen piste valkoisella kehyksellä. */
.pin-me {
  width: 16px;
  height: 16px;
  border: 2.5px solid var(--n-0);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 1px rgb(11 107 203 / 0.35), 0 1px 4px rgb(0 0 0 / 0.4);
}

/* --- Popup -------------------------------------------------------------- */

.leaflet-popup-content-wrapper {
  padding: 0;
  border-radius: var(--r-md);
  background: var(--surface-solid);
  color: var(--text);
  box-shadow: var(--shadow-2);
}

.leaflet-popup-content {
  margin: 0;
  width: auto !important;
  min-width: 220px;

  /* KORKEUSRAJA JA VIERITYS (A15). Popup on 496–730 px korkea, eikä
     sillä ollut ylärajaa. Suurella tekstillä se oli korkeampi kuin
     ruutu: Leaflet panoroi karttaa niin paljon, että kohteen pinni jäi
     näkymän ulkopuolelle, uusi haku ei palauttanut kohdetta, ja popup
     sulkeutui merkin mukana 0,5 sekunnissa. 162 px on Leafletin
     panorointivara ylhäällä (104) sekä pinni ja kärki; alareunan vara
     on `--alavara`, jonka app.js mittaa alareunan pinosta (A19).
     `vh` on varalla selaimelle, joka ei tunne `dvh`:ta. */
  max-height: calc(100vh - 162px - var(--alavara, 28px));
  max-height: calc(100dvh - 162px - var(--alavara, 28px));
  overflow-y: auto;
  overscroll-behavior: contain;
}

.leaflet-popup-tip {
  background: var(--surface-solid);
}

.leaflet-popup-close-button {
  color: var(--text-dim) !important;
  padding: 8px 8px 0 0 !important;
}

.popup {
  position: relative;
  padding: 13px 15px 14px;
}

/*
 * Suosikin merkintä popupissa. Oikea ylänurkka on varattu Leafletin
 * sulkuruksille, joten sydän istuu sen vasemmalle puolelle.
 */
.popup__heart {
  position: absolute;
  top: 9px;
  right: 34px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--text-dim);
  transition: color 0.15s ease, transform 0.15s ease;
}

.popup__heart svg {
  width: 19px;
  height: 19px;
}

.popup__heart.is-on {
  color: var(--favourite);
}

.popup__heart:active {
  transform: scale(0.88);
}

/* Nimi ei saa mennä sydämen alle. */
.popup__name {
  padding-right: 58px;
}

.popup__name {
  margin: 0;
  font-size: 0.9688rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.popup__kind {
  margin: 2px 0 0;
  color: var(--text-dim);
  font-size: 0.8125rem;
}

.popup__grade {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 11px 0 0;
  font-size: 0.8438rem;
  font-weight: 600;
}

.popup__date {
  font-weight: 500;
  color: var(--text-dim);
}

.popup__note {
  margin: 9px 0 0;
  padding: 7px 9px;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--text-dim);
  font-size: 0.7812rem;
  line-height: 1.4;
}

.leaflet-container .popup__link,
.popup__link {
  display: inline-block;
  margin-top: 11px;
  color: var(--accent);
  font-size: 0.8438rem;
  font-weight: 600;
  text-decoration: none;
}

.leaflet-container .popup__link:hover,
.popup__link:hover {
  text-decoration: underline;
}

/* Lounaslinkki, kohteen omat sivut ja navigointi omille riveilleen: ne
   ovat eri asia kuin Oiva-raportti, ja kaikki neljä voivat olla
   näkyvissä yhtä aikaa.

   VALITSIN ON KAKSIOSAINEN TARKOITUKSELLA. Yllä oleva
   `.leaflet-container .popup__link` on tarkkuudeltaan (0,2,0), ja popup
   renderöityy AINA `.leaflet-container`in sisällä. Yksiosainen
   `.popup__nav` (0,1,0) hävisi sille, joten sääntö oli kuollut juuri
   siinä ainoassa paikassa jossa sillä on merkitystä: mitattuna
   `display` oli `inline-block`, ja kotisivu- ja navigointilinkki
   renderöityivät samalle riville nollan pikselin välillä
   ("raflaamo.fi →Navigoi →"). Mittaa renderöidystä DOM:sta, älä CSS:stä. */
.leaflet-container .popup__link--lounas,
.leaflet-container .popup__link--www,
.leaflet-container .popup__link--puhelin,
.popup__link--lounas,
.popup__link--www,
.popup__link--puhelin {
  display: block;
  /* Kohde 24 px (WCAG 2.5.8) ilman välistyspoikkeusta: 18 px:n linkit
     25 px:n välein läpäisivät vain puolen pikselin marginaalilla (A26). */
  margin-top: 4px;
  padding: 3px 0;
}

/* Toimintorivi (A26): navigointi ja jako painikkeina samalla rivillä.
   Kohdekoko 44 px. Sama tarkkuusperiaate kuin yllä (T-08): popup on aina
   `.leaflet-container`in sisällä, ja Leafletin oma `.leaflet-container a`
   voittaisi yksiosaisen valitsimen värin. */
.popup__toiminnot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 0;
}

.leaflet-container .popup__toiminto,
.popup__toiminto {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--accent);
  font-size: 0.8438rem;
  font-weight: 600;
  text-decoration: none;
}

.leaflet-container .popup__toiminto:hover,
.popup__toiminto:hover {
  text-decoration: underline;
}

/* Lisätiedot tiivistelminä (A26). Tiivistelmä on otsikko eikä
   varoituslaatikko: aiemmat tarkastukset olivat epävarmuusvaroituksen
   näköisessä harmaassa laatikossa. */
.popup__osio {
  margin-top: 10px;
  font-size: 0.8rem;
}

.popup__osio > summary {
  padding: 3px 0;
  cursor: pointer;
  font-weight: 600;
  color: var(--text);
  line-height: 1.4;
}

.popup__osio[open] > summary {
  margin-bottom: 4px;
}

/* Kohteen omat sivut ovat lisätieto eivätkä viranomaislähde, joten ne
   eivät kilpaile Oiva-raportin kanssa huomiosta. */
.popup__link--www {
  font-weight: 500;
}

/* Erikseen arvioidut toiminnot: mistä kohteen arvosana tulee. */
.popup__activities {
  display: grid;
  gap: 5px;
  margin: 7px 0 0;
  padding: 0;
  list-style: none;
}

.popup__activities li {
  display: flex;
  align-items: center;
  gap: 8px;
}

.popup__activities .badge {
  width: 20px;
  height: 20px;
  border-radius: 6px;
  font-size: 0.6875rem;
}

.popup__activity {
  font-size: 0.7812rem;
  font-weight: 550;
  line-height: 1.3;
}

/*
 * Nipun sisältö popupissa. Tarvitaan silloin kun kohteet ovat samassa
 * pisteessä — ostoskeskuksessa zoomaus ei koskaan erota niitä, joten
 * lista on ainoa tapa päästä yksittäiseen kohteeseen käsiksi.
 *
 * Leikkaava nimi ("Ravintola Mom…") tekisi listasta hyödyttömän juuri
 * siellä missä sitä eniten tarvitaan, joten popup saa kiinteän leveyden
 * eikä kutistu sisällön mukaan. Leafletin oletusmaksimi on 300 px.
 */
.popup--cluster {
  width: 272px;
}

.popup .list {
  max-height: 232px;
  margin-top: 9px;
}

.popup .row {
  padding: 6px;
}

/* --- Arvosanamerkki ----------------------------------------------------- */

/*
 * Kirjain A–D on Ruokaviraston virallinen koodaus, ja se toimii myös
 * ilman väriä — värisokea käyttäjä lukee saman tiedon.
 */
.badge {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 7px;
  background: var(--grade-unknown);
  color: var(--grade-ink);
  font-size: 0.75rem;
  font-weight: 700;
}

.badge[data-grade='oivallinen'] { background: var(--grade-oivallinen); }
.badge[data-grade='hyva'] { background: var(--grade-hyva); }
.badge[data-grade='korjattavaa'] { background: var(--grade-korjattavaa); }
.badge[data-grade='huono'] { background: var(--grade-huono); }

/* --- Selite ------------------------------------------------------------- */

/* Arvosanalappujen alla (A35), joten erotinviivaa ei tarvita. */
.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 0;
}

.legend__item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.legend dt {
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  border-radius: 6px;
  background: var(--grade-unknown);
  color: var(--grade-ink);
  font-size: 0.6875rem;
  font-weight: 700;
}

.legend dt[data-grade='oivallinen'] { background: var(--grade-oivallinen); }
.legend dt[data-grade='hyva'] { background: var(--grade-hyva); }
.legend dt[data-grade='korjattavaa'] { background: var(--grade-korjattavaa); }
.legend dt[data-grade='huono'] { background: var(--grade-huono); }

.legend dd {
  margin: 0;
  font-size: 0.7188rem;
  font-weight: 550;
}

.banner {
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--grade-korjattavaa) 16%, transparent);
  color: var(--text);
  font-size: 0.7812rem;
  line-height: 1.45;
}

.banner[hidden] {
  display: none;
}

/* --- Hakuosuman korostus ------------------------------------------------ */

@keyframes highlight-pulse {
  0%   { transform: rotate(45deg) scale(1); }
  25%  { transform: rotate(45deg) scale(1.4); }
  50%  { transform: rotate(45deg) scale(1); }
  75%  { transform: rotate(45deg) scale(1.4); }
  100% { transform: rotate(45deg) scale(1); }
}

.pin--highlight {
  animation: highlight-pulse 0.8s ease;
}

.pin--highlight.pin--active {
  animation: highlight-pulse-active 0.8s ease;
}

@keyframes highlight-pulse-active {
  0%   { transform: rotate(45deg) scale(1.25); }
  25%  { transform: rotate(45deg) scale(1.55); }
  50%  { transform: rotate(45deg) scale(1.25); }
  75%  { transform: rotate(45deg) scale(1.55); }
  100% { transform: rotate(45deg) scale(1.25); }
}
/* --- Tuoreustieto ------------------------------------------------------- */

/*
 * Ikäluokka on teksti neutraalilla lapulla, ei värillinen piste.
 *
 * MIKSI EI ARVOSANAVÄREJÄ. Ensimmäinen versio käytti --grade-*-värejä
 * sellaisenaan, jolloin C-arvosanan viereen tuli Oivallinen-vihreä
 * merkki ("C · ● 22.7.2026", mitattu tuotantoaineistosta). Vilkaisulla
 * luettu kartta on juuri se käyttötapa jonka takia arvosanat ovat
 * ainoat kylläiset värit — toinen samanvärinen asteikko vieressä
 * hävittää sen erottelun, ja virhe on tosiasiaväite ravintolasta.
 *
 * MIKSI TEKSTI. Piste kantoi tiedon pelkällä värillä, ja sen
 * saavutettava nimi oli tyhjä (mitattu CDP:llä: role=generic name="").
 * Lyhyt teksti kelpaa sellaisenaan ruudunlukijalle eikä vaadi
 * aria-labelia, joka ylikirjoittaisi näkyvän tekstin.
 */
.freshness {
  display: inline-block;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--n-100);
  color: var(--text-dim);
  font-size: 0.6875rem;
  font-weight: 600;
  white-space: nowrap;
  vertical-align: baseline;
}

/* Neutraali askelma: mitä vanhempi tarkastus, sitä vahvempi lappu.
   Sävyt tulevat --n-*-asteikosta eivätkä --grade-*-asteikosta. */
.freshness--vanha {
  background: var(--n-200);
  color: var(--text);
}

.freshness--vanhentunut {
  background: var(--n-300);
  color: var(--text);
}

/* --- Aluekonteksti ------------------------------------------------------ */

.popup__area {
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Keskipiste erottimena. CSS:ssä ei ole \u-escapea: JS:stä tuttu
   '\u00b7' jäsentyy paenneeksi u-kirjaimeksi ja tulostaa ruudulle
   merkkijonon "u00b7". Merkki kirjoitetaan siksi sellaisenaan, ja
   test/merkkaus.test.js hylkää \u-alkuiset escapet. */
.popup__area::before {
  content: '·';
  margin: 0 4px;
}

/* --- Aukioloajat -------------------------------------------------------- */

/*
 * Auki/kiinni on TEKSTIÄ neutraalilla lapulla, ei värikoodi.
 *
 * Arvosanat ovat käyttöliittymän ainoat kylläiset värit, jotta ne
 * erottuvat vilkaisulla. Vihreä "Auki" ja punainen "Kiinni" olisivat
 * toinen asteikko samoilla väreillä aivan arvosanan vieressä — sama
 * virhe kuin tuoreuspisteessä, joka poistettiin (T-04).
 */
.popup__hours {
  color: var(--text-dim);
}

.aukiolo__tila {
  display: inline-block;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--n-100);
  color: var(--text-dim);
  font-size: 0.6875rem;
  font-weight: 600;
}

.aukiolo__tila--auki {
  background: var(--n-200);
  color: var(--text);
}

.aukiolo {
  list-style: none;
  margin: 0;
  padding: 0;
}

.aukiolo li {
  display: flex;
  gap: 8px;
  justify-content: space-between;
  padding: 1px 0;
}

/* Päivä ei saa kutistua ajan alta kapealla ruudulla. */
.aukiolo__paiva {
  flex: 0 0 auto;
}

.aukiolo__ajat {
  text-align: right;
}

.aukiolo__aina,
.aukiolo__poikkeus {
  margin: 2px 0 0;
}

.aukiolo__poikkeus {
  font-style: italic;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* Animaation tilalle staattinen korostus. Se ei kuitenkaan ole
     staattinen, jos `app.js` poistaa luokan `animationend`in perusteella:
     yllä oleva `animation-duration: 0.01ms` laukaisee tapahtuman heti, ja
     mitattuna korostus näkyi 16 ms — yhden ruudun. `app.js` katsoo siksi
     `matchMedia`ia ja käyttää ajastinta. */
  .pin--highlight,
  .pin--highlight.pin--active {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
  }

  /*
   * LATAUSILMAISIN ILMAN LIIKETTÄ (A5).
   *
   * Yllä oleva `* { animation-duration: 0.01ms }` tappaa myös
   * `.tool[aria-busy='true'] svg`:n pyörähdyksen. Mitattuna
   * (`reducedMotion: 'reduce'`, pyörähdyskulma 400 ms:n aikana):
   *
   *   no-preference  duration 1.1s   iterations infinite   liikkui true
   *   reduce         duration 1e-05s iterations 1          liikkui false
   *
   * `render()` näyttää tekstin "Haetaan paikkoja…" vain kun kartalla ei
   * ole vielä yhtään kohdetta. Kun haku uusitaan ja kohteita on, pyörivä
   * ikoni oli AINOA palaute — ja liikeherkkyysasetuksella sitä ei ollut
   * lainkaan. Nielsenin ensimmäinen heuristiikka, ja se osui juuri siihen
   * ryhmään joka on asetuksensa itse valinnut.
   *
   * Tilalle liikkumaton mutta yksiselitteinen merkki: aksenttivärinen
   * kehä ja ikoni. Kahden pikselin kehä erottuu selaimen omasta yhden
   * pikselin kohdistusrenkaasta.
   */
  .tool[aria-busy='true'] {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent);
  }

  .tool[aria-busy='true'] svg {
    color: var(--accent);
  }
}

/* Yhdistetyn paneelin toinen osa: asetukset suodattimien alla. Viiva
   erottaa ne toisistaan ilman että tarvitaan kahta paneelia. */
.panel__title--section {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  /* Otsikkoon vieritettäessä (A37) viiva jää paneelin täytteen verran
     yläreunasta eikä osu paneelin reunaan. */
  scroll-margin-top: 16px;
}

/* Siirtopainikkeen tuoma kohdistus (A37). Ilman väliä rengas kulki
   tekstin alareunaa ja ensimmäistä kirjainta pitkin. */
.panel__title--section:focus-visible {
  outline-offset: 4px;
}
