/* ============================================================
   1. TOKENS — suunnittelujärjestelmän arvot (:root)
   Kaikki värit, etäisyydet, typografiat jne. yhdestä paikasta.
   Komponentit käyttävät vain näitä.
   ============================================================ */
:root {
  /* --- Värit: pinta-hierarkia (Stitch: "layered paper") ---
     Käsittele UI:ta paperipinona. Ei 1px-bordereita osiointiin —
     rajat tehdään taustavärin vaihdolla (tonal shift). */
  --color-bg: #ffffff;              /* focal paper (lowest) */
  --color-surface: #f8f9fa;         /* surface */
  --color-surface-low: #f3f4f5;     /* container-low — taustaosiot */
  --color-surface-mid: #edeeef;     /* container — "desk" */
  --color-surface-high: #e8e9eb;    /* container-high — hover */

  /* --- Värit: teksti & reunat --- */
  --color-text: #191c1d;            /* on_surface — EI pure black */
  --color-muted: #5f6368;           /* sekundaariteksti, metatiedot */
  --color-border: rgba(196, 198, 210, 0.15); /* ghost border — vain kun pakko */
  --color-outline: #c4c6d2;         /* näkyvä reuna (focus, a11y) */

  /* --- Värit: brändi --- */
  --color-primary: #001b44;         /* deep navy — luottamus, auktoriteetti */
  --color-primary-light: #002f6c;   /* primary container — gradienttipari */
  --color-accent: #e67e22;          /* amber-gold — CTA, korostus */
  --color-accent-light: #f0a050;    /* accent container — glow */

  /* --- Värit: tilat --- */
  --color-success: #16a34a;
  --color-warning: #ea580c;
  --color-danger: #dc2626;
  --color-text-inverse: #fff;        /* teksti tummalla taustalla (badge, nappi) */

  /* --- Typografia ---
     Newsreader = toimituksellinen painovoima (otsikot, AI:n löydökset).
     Manrope = tekninen selkeys (leipäteksti, UI).
     Inter = pienet metatiedot, tägit. */
  --font-editorial: 'Newsreader', Georgia, serif;
  --font-body: 'Manrope', system-ui, sans-serif;
  --font-label: 'Inter', system-ui, sans-serif;
  --font-heading: var(--font-editorial);
  --line-height-tight: 1.2;
  --line-height-base: 1.6;          /* generous leading — syvälukeminen */
  --letter-spacing-headline: -0.02em; /* "inked" — tiukka otsikoissa */

  /* --- Etäisyydet (1rem = 16px) --- */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2rem;
  --space-6: 3rem;
  --space-7: 4rem;                  /* hero-padding mobiili/desktop */

  /* --- Pyöristykset --- */
  --radius-sm: .25rem;
  --radius-md: .375rem;             /* Stitch: napit */
  --radius-lg: 1rem;
  --radius-full: 9999px;            /* pill-shaped chipit */

  /* --- Varjot (navy-tinted, ei puhdasta mustaa) --- */
  --shadow-sm: 0 1px 3px rgba(0, 27, 68, 0.04);
  --shadow-md: 0 4px 12px rgba(0, 27, 68, 0.06);
  --shadow-float: 0 10px 30px rgba(0, 27, 68, 0.06); /* floating: tooltip, modal, FAB */

  /* --- Lasi (glassmorphism — sticky header, modaalit, dropdown) --- */
  --glass-bg: rgba(248, 249, 250, 0.85);
  --glass-blur: 16px;

  /* --- Gradientit --- */
  --gradient-primary: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));

  /* --- Transitiot --- */
  --transition-fast: 120ms ease;
  --transition-base: 200ms ease;

  /* --- Rajat --- */
  --content-max: 1400px;
  /* Luettava rivinpituus leipatekstille. 560px / ~8.8px per merkki (Manrope
     1.1rem) = ~64 merkkia rivilla, eli keskella 60-75 merkin suositusta.
     Arvo on sama kuin heron sisaisilla lohkoilla (.hero-haku, .hero-askeleet,
     .hero-alert) jo ennestaan — selite paattyy nyt samaan linjaan lomakkeen
     kanssa. Ne kayttavat yha literaalia 560px:aa; tokenisointi seuraavalla
     kosketuksella (ei tassa taskissa: vain lisayksia jaettuun app.css:aan). */
  --content-reading: 560px;
  /* Luettava mitta VAAKAKUVAAJALLE (ei tekstille). Kamman kaltaisessa
     kuvaajassa lukija vertaa rivin vasenta ja oikeaa laitaa toisiinsa; kun
     lohko venyy koko containerin leveydelle (1400px), laidat ovat niin
     kaukana etta vertailu ei enaa synny yhdella silmayksella. 720px on noin
     puolet 1440px:n tyopoytaikkunasta ja jattaa >=480px:n kamma-riville
     ~190px palkkia per puoli (nimi 12rem + luvut 2x3rem + gapit). Leveampi
     kuin --content-reading, koska sisalto ei ole proosaa vaan nelisarakkeinen
     rivi. Palaute #220. */
  --content-kuvaaja: 720px;

  /* --- Aksenttiviiva (korttien vasen raita — kategoriaindikaattori) --- */
  --accent-stripe: 4px;

  /* --- Bias-värit (domain, analyysipisteet) --- */
  --color-bias-high:        hsl(0 72% 51%);
  --color-bias-medium:      hsl(38 92% 50%);
  --color-bias-low:         hsl(142 71% 45%);
  --color-bias-neutral:     hsl(220 9% 46%);
  --color-bias-high-bg:     hsl(0 72% 51% / 0.12);
  --color-bias-high-subtle: hsl(0 72% 51% / 0.04);
  --color-bias-high-border: hsl(0 72% 51% / 0.15);
  --color-bias-high-text:   hsl(0 72% 40%);
  --color-bias-medium-bg:   hsl(38 92% 50% / 0.15);
  --color-bias-medium-text: hsl(38 80% 35%);
  --color-bias-low-bg:      hsl(142 71% 45% / 0.12);
  --color-bias-low-subtle:  hsl(142 71% 45% / 0.04);
  --color-bias-low-border:  hsl(142 71% 45% / 0.15);
  --color-bias-low-text:    hsl(142 71% 30%);
  --color-bias-neutral-bg:  hsl(220 9% 46% / 0.12);
  --color-bias-neutral-text: hsl(220 9% 30%);

  /* --- Primary-sävyn alphan-variantit (luottamus/info-laatikot, pikakutsu) ---
     Brändin navy (#001b44 = hsl(214 100% 13%)) eri alphoilla. */
  --color-primary-subtle: hsl(214 100% 13% / 0.05);   /* pikakutsu bg, info-laatikko */
  --color-primary-border: hsl(214 100% 13% / 0.10);   /* pikakutsu border */
  --color-primary-bg:     hsl(214 100% 13% / 0.08);   /* .merkki.sininen bg */

  /* --- Domain-merkkien uniikit sävyt (kritisoitu/puolustettu/violetti) ---
     Eri kuin bias-värit — nämä ovat sisältösemantiikkaa, eivät vinoumaa. */
  --color-kritisoitu-bg:  hsl(350 80% 95%);
  --color-kritisoitu-text: hsl(350 70% 35%);
  --color-puolustettu-bg: hsl(160 60% 93%);
  --color-puolustettu-text: hsl(160 50% 30%);
  --color-violetti-bg:    hsl(262 83% 90%);
  --color-violetti-text:  hsl(262 60% 40%);

  /* --- Aktiivinen rivi (propagandataulukko, valitut listarivit) ---
     Eri sävy kuin bias-high (puhtaampi punainen, korkeampi saturaatio). */
  --color-rivi-aktiivinen-bg: hsl(0 84% 60% / 0.06);

  /* --- Vakavuus-tasot vihreä→keltainen→punainen (propaganda) ---
     Eri kuin --color-severity-* (jotka ovat orange-pohjaisia, kategoria-merkki).
     Nämä noudattavat liikennevalo-semantiikkaa propagandariville. */
  --color-vakavuus-mild-bg:     hsl(142 71% 45% / 0.15);
  --color-vakavuus-mild-text:   hsl(142 71% 30%);
  --color-vakavuus-moderate-bg: hsl(38 92% 50% / 0.18);
  --color-vakavuus-moderate-text: hsl(38 80% 35%);
  --color-vakavuus-severe-bg:   hsl(0 72% 51% / 0.18);
  --color-vakavuus-severe-text: hsl(0 72% 40%);

  /* --- Admin-moderointi-merkit (odottaa/hyväksytty/hylätty) ---
     Visuaalisesti vahvempia kuin bias-merkit (hyväksytty teal, hylätty pinkki). */
  --color-odottaa-bg:    hsl(45 93% 47% / 0.18);
  --color-odottaa-text:  hsl(45 80% 30%);
  --color-hyvaksytty-bg: hsl(160 59% 52% / 0.18);
  --color-hyvaksytty-text: hsl(160 59% 25%);
  --color-hylatty-bg:    hsl(0 86% 71% / 0.18);
  --color-hylatty-text:  hsl(0 72% 40%);

  /* --- Tag-pillerit (drilldown, etusivun trending) ---
     Vaalea desaturoitu sininen — eri kuin primary tai bias. */
  --color-tag-bg: hsl(222 100% 92%);

  /* --- Sininen-merkin tekstiväri (= primary, mutta semanttinen alias) ---
     Eri merkkityypeille jotka käyttävät navy:tä tekstinä. */
  --color-sininen-text: hsl(214 100% 13%);

  /* --- Narratiivi-paneelin agentti-tagien sävyt ---
     Threat = punainen (vahvempi kuin bias-high), victim = sininen, savior = vihreä.
     Alpha 0.10 — kevyempi kuin bias-bg (.12) jotta narratiivi-paneelissa ei
     domnoi visuaalisesti. */
  --color-victim-bg: hsl(214 100% 50% / 0.10);
  --color-victim-text: hsl(214 80% 30%);

  /* --- Tone-badge-sävyt (narratiivi-paneeli) ---
     Pelottava/toiveikas/neutraali = bias-värit (jo olemassa).
     Moralisoiva/dramatisoiva/kriittinen ovat uniikkeja sävyjä. */
  --color-tone-moralisoiva-bg:  hsl(27 100% 50% / 0.12);
  --color-tone-moralisoiva-text: hsl(27 80% 30%);
  --color-tone-dramatisoiva-bg: hsl(48 96% 53% / 0.15);
  --color-tone-dramatisoiva-text: hsl(48 80% 28%);
  --color-tone-kriittinen-bg:   hsl(214 100% 50% / 0.12);
  --color-tone-kriittinen-text: hsl(214 80% 30%);

  /* --- Rengas (focus-outline) --- */
  --color-ring: rgba(0, 27, 68, 0.15);

  /* --- Overlay (modaalit, dialog::backdrop) ---
     Navy-tinted (rgba(0, 27, 68, 0.5)) — speksin "Navy-tinted varjot"
     -periaate (varjoissa ei puhdasta mustaa) koskee myös overlayta.
     Aiempi rgba(0, 0, 0, 0.5) modaalissa korvattu navy-tintille. */
  --color-overlay: rgba(0, 27, 68, 0.5);

  /* --- Shadow-laajennus (legacy-komponenteista) --- */
  --shadow-lg: 0 10px 30px rgba(0, 27, 68, 0.08);

  /* --- Domain-tokenit (skeleton, hero, highlight, severity, narrative) ---
     Nama EIVAT ole legacy-jaanteita vaan tarkkasti domain-spesifeja:
     tekstin korostus (framing/loaded/omission), vakavuustasot, narratiivipaneelin
     luotettavuus- ja savytoitu-badget. */
  --color-skeleton:             hsl(210 5% 93%);
  --color-hero-gradient:        hsl(27 100% 29% / 0.05);
  --color-highlight-framing:    hsl(38 100% 85%);
  --color-highlight-loaded:     hsl(0 100% 90%);
  --color-highlight-omission:   hsl(262 83% 90%);
  --color-loaded-text:          hsl(0 60% 35%);
  --color-accent-text:          hsl(27 80% 25%);
  --color-accent-text-dark:     hsl(27 80% 30%);
  --color-accent-bg:            hsl(27 100% 29% / 0.08);
  --color-accent-subtle:        hsl(27 100% 97%);
  --color-accent-border:        hsl(27 80% 80%);
  --color-severity-mild-bg:     hsl(27 100% 29% / 0.15);
  --color-severity-moderate-bg: hsl(27 100% 29% / 0.3);
  --color-severity-severe-bg:   hsl(0 72% 51% / 0.15);
  --color-opinion-badge:        hsl(262 60% 35%);
  --color-secondary:            hsl(40 30% 96%);           /* narratiivipaneelin pehmea tausta */

  /* --- Värit: poliittinen kehystys (suomen mediakonventio) ---
     Vasen-punainen, keskusta-harmaa, oikea-sininen.
     Desaturoitu (ei kirkkaita Material-värejä) — sopii design-ohjeen mukaiseen
     "layered paper" -tunnelmaan. Käytetään sekä drilldown-kaavioissa että
     sidebar-jakaumapalkissa (sama data = sama väri). */
  --color-poliittinen-vasen: #dc2626;
  --color-poliittinen-keskusta: #6b7280;
  --color-poliittinen-oikea: #2563eb;
}

/* ============================================================
   2. POHJA — elementit peruustyylitettyinä
   Pico.classless hoitaa ison osan. Täällä vain overridet ja
   puuttuvat. Tavoite: HTML ilman luokkia näyttää jo hyvältä.
   ============================================================ */

/* --- Pico overridet ---
   Pico.classless asettaa responsiivisen font-sizen (100%->106.25%->112.5%)
   ja body > main/header/footer {max-width: 510..1450px} — molemmat halutaan pois.
   Taustat (glassmorphism ylapalkki, footer-border) pitaa kulkea reunasta reunaan,
   sisallon rajauksen hoitaa .container kutsupaikoissa. Form-spacing pienennetty
   (Picon 0.75/1rem -> 0.5/0.75rem) jotta lomake-elementit ovat tiiviimmat
   artikkelilistan suodattimissa. */
:root {
  --pico-font-size: 100%;
  --pico-form-element-spacing-vertical: 0.5rem;
  --pico-form-element-spacing-horizontal: 0.75rem;
}
@media (min-width: 576px), (min-width: 768px), (min-width: 1024px), (min-width: 1280px), (min-width: 1536px) {
  :root { --pico-font-size: 100%; }
}
body > main,
body > header,
body > footer { max-width: none; padding-left: 0; padding-right: 0; }

body { color: var(--color-text); background: var(--color-bg); font-family: var(--font-body); padding-bottom: var(--space-6); }
img { max-width: 100%; height: auto; }
h1, h2, h3 { font-family: var(--font-heading); letter-spacing: var(--letter-spacing-headline); }
h1 { font-size: 2rem; line-height: var(--line-height-tight); }
h2 { font-size: 1.75rem; line-height: var(--line-height-tight); }
h3 { font-size: 1.125rem; line-height: var(--line-height-tight); }
p { margin: 0 0 var(--space-3); }
a { color: var(--color-primary); }
a:hover { color: var(--color-primary-light); }
ul, ol { padding-left: var(--space-4); }
small, .meta { font-family: var(--font-label); font-size: .75rem; }

[x-cloak] { display: none !important; }  /* Alpine FOUC-esto — ainoa sallittu !important */

/* ============================================================
   3. LAYOUT-UTILITYT (vain jos pakko)
   Yksinkertaiset apurit sivun rakenteelle. Nämä ovat harvinaisia
   — mieluummin `main`, `section`, `article` kuin `.container`.
   Jos Pico:n oletus ei riitä.
   ============================================================ */
.container { max-width: var(--content-max); margin: 0 auto; padding: 0 var(--space-3); }

/* ============================================================
   4. INTERAKTIIVISET — napit, lomakkeet, taulukot
   Pico hoitaa perustason. Omat tyylit vain kun tarvitaan
   tuotekohtaisia sävyjä tai .btn-variantteja.
   ============================================================ */
.btn { /* variantti jos <button>-oletus ei riitä, esim. linkkinä */ }
form { /* harvoin tarpeen */ }

/* --- taulukko --- */
table { width: 100%; border-collapse: collapse; }
thead th {
  font-weight: 600;
  text-align: left;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-muted);
  padding: var(--space-2) var(--space-3);
  border-bottom: 2px solid var(--color-outline);
}
tbody td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  font-size: 0.875rem;
}
tbody tr:hover { background: var(--color-surface-mid); }

.taulukko-tiivis { font-size: 0.875rem; margin-bottom: 0; }
.taulukko-tiivis td,
.taulukko-tiivis th { padding: 0.375rem 0.5rem; }

/* ============================================================
   5. SIVURAKENNE — ylhäältä alas kuten HTML
   Järjestys seuraa sivun layoutia: header, hero, main, footer.
   Kaikki yhden rakennepalan tyylit (peruustila + variantit +
   sivu-overridet + media queryt) pidetään yhdessä lohkossa.
   ============================================================ */

/* --- ylapalkki ---
   Sticky header glassmorphism-tyylilla. Container rajoittaa leveyden.
   Flexbox: logo vasemmalla, nav keskella, auth/user oikealla.
   Mobiilivalikko piilotettu >=768px, naytetaan hamburgerilla <=768px. */

.ylapalkki {
  position: sticky;
  top: 0;
  z-index: 100;
  padding: var(--space-2) 0;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border-bottom: 1px solid var(--color-border);
}

/* Mobiilissa tiukka gap: logo + auth-napit + hamburger eivat mahdu 360 px:iin
   space-3:lla (palaute #142, 11.8.2026 — "venyy yli ruudun").

   MITATTU 360x674 (CDP, oikea layout-viewport, kirjautumaton = levein tila):
     logo 144 px (flex-shrink: 0) + gap 16 + oikea 129 + gap 16 + hamburger 44
     = 349 px, kaytettavissa 328 px (container 360 - 2x16 padding)
     -> nav.scrollWidth 349, hamburgerin oikea reuna 365 = 5 px yli viewportin
     -> koko dokumentti vieritti vaakasuunnassa JOKAISELLA sivulla.

   Gap on ainoa jousto: logo ja hamburger ovat `flex-shrink: 0` (brandinimea
   ei katkaista ellipsilla, ja hamburger on 44x44 WCAG 2.5.5 -tap-target).
   space-1 saastaa 24 px -> 325 px mahtuu.

   Kirjautuneena `.oikea` on kapeampi (ikoni + saldo) kuin kirjautumattomana
   ("Kirjaudu" + "Luo tili"), joten mitattu tila on se ahtain. */
.ylapalkki nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.ylapalkki .logo {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  color: var(--color-text);
  font-weight: 600;
  flex-shrink: 0;
}
.ylapalkki .logo img {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
}
@media (min-width: 768px) {
  .ylapalkki .logo img {
    width: 32px;
    height: 32px;
  }
}

/* Desktop navigaatio — piilotettu mobiililla */
.ylapalkki .nav-linkit {
  display: none;
  gap: var(--space-3);
}
.ylapalkki .nav-linkit a {
  color: var(--color-text);
  text-decoration: none;
  font-size: .9375rem;
  transition: color var(--transition-fast);
}
.ylapalkki .nav-linkit a:hover {
  color: var(--color-primary-light);
}

/* Oikea puoli: share + auth/user. Tyonnetaan oikealle margin-left:auto.
   Mobiilissa kapea gap jotta auth-napit + hamburger mahtuvat ahtaaseen tilaan. */
.ylapalkki .oikea {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: auto;
}

/* Auth-napit (kirjautumaton) — mobiilissa tiukka gap */
.ylapalkki .auth-napit {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

/* Napit ylapalkissa — mobiilissa tiukat paddingit ja pienempi fontti
   jotta "Kirjaudu" + "Luo tili" + hamburger mahtuvat 360px-leveydelle. */
.ylapalkki button {
    margin-bottom: 0;
}
.ylapalkki .painike {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-md);
  font-size: .8125rem;
  font-family: var(--font-body);
  cursor: pointer;
  border: none;
  transition: all var(--transition-fast);
  white-space: nowrap;
}
.ylapalkki .painike.ghost {
  background: transparent;
  color: var(--color-text);
}
.ylapalkki .painike.ghost:hover {
  background: var(--color-surface);
}
.ylapalkki .painike.primary {
  background: var(--gradient-primary);
  color: var(--color-text-inverse);
}
.ylapalkki .painike.primary:hover {
  box-shadow: var(--shadow-sm);
}

/* Kayttajavalikko (kirjautunut) */
.ylapalkki .kayttaja {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  position: relative;
}
.ylapalkki .kayttaja-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-bottom: 0;
  border-radius: var(--radius-full);
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--color-text);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.ylapalkki .kayttaja-trigger:hover {
  background: var(--color-surface);
  color: var(--color-primary);
}
.ylapalkki .krediitit {
  font-size: .75rem;
  font-family: var(--font-label);
  color: var(--color-muted);
}

/* Kayttajan pudotusvalikko */
.ylapalkki .kayttaja-pudotus {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  min-width: 200px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-float);
  padding: var(--space-2) 0;
  z-index: 110;
}
.ylapalkki .kayttaja-email {
  padding: var(--space-2) var(--space-3);
  font-size: .75rem;
  color: var(--color-muted);
  font-family: var(--font-label);
}
.ylapalkki .kayttaja-krediitit {
  display: block;
  padding: var(--space-1) var(--space-3);
  font-size: .875rem;
  color: var(--color-text);
  text-decoration: none;
}
.ylapalkki .kayttaja-pudotus hr {
  margin: var(--space-1) 0;
  border: none;
  border-top: 1px solid var(--color-border);
}
.ylapalkki .pudotus-linkki {
  display: block;
  padding: var(--space-2) var(--space-3);
  font-size: .875rem;
  color: var(--color-text);
  text-decoration: none;
  cursor: pointer;
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  font-family: var(--font-body);
  transition: background var(--transition-fast);
}
.ylapalkki .pudotus-linkki:hover {
  background: var(--color-surface);
}
.ylapalkki .pudotus-linkki.vaara {
  color: var(--color-danger);
}
.ylapalkki .pudotus-linkki.vaara:hover {
  background: var(--color-surface);
}

/* Logout-lomake — ei ulkoista asetteluvaikutusta */
.ylapalkki .logout-lomake {
  margin: 0;
  padding: 0;
}

/* Hamburger-nappi — naytetaan vain mobiililla.
   44x44px tap-target (WCAG 2.5.5 minimum target size). */
.ylapalkki .hamburger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--color-text);
  padding: 0;
  flex-shrink: 0;
}

/* Mobiilivalikko — piilotetaan desktopilla. Rajaus tulee .container:ista. */
.ylapalkki .mobiilivalikko {
  padding: var(--space-2) 0;
}
.ylapalkki .mobiilivalikko a {
  display: block;
  padding: var(--space-2) 0;
  color: var(--color-text);
  text-decoration: none;
  font-size: .9375rem;
  border-bottom: 1px solid var(--color-border);
}
.ylapalkki .mobiilivalikko a:last-child {
  border-bottom: none;
}

/* Desktop — palautetaan auth-nappien mukava koko ja gap mobiilin tiukennuksen jalkeen. */
@media (min-width: 768px) {
  /* Nav-rivin valjempi gap: mobiilin space-1 on ahtauspakko (#142), ei tyyli. */
  .ylapalkki nav {
    gap: var(--space-3);
  }
  .ylapalkki .oikea {
    gap: var(--space-2);
  }
  .ylapalkki .auth-napit {
    gap: var(--space-2);
  }
  .ylapalkki .painike {
    padding: var(--space-1) var(--space-3);
    font-size: .875rem;
  }
  .ylapalkki .nav-linkit {
    display: flex;
  }
  .ylapalkki .hamburger {
    display: none;
  }
  .ylapalkki .mobiilivalikko {
    display: none; /* Alpine x-show piilottaa mobiilissa, desktop-mediassa ei nayteta */
  }
}

/* --- sivuotsikko ---
   Kehys-komponentti. Nakyy ylapalkin alla, kertoo milla sivulla olemme
   (otsikko + optionaalinen takaisin-linkki + meta + actions). EI omaa
   taustavaria -- ei kilpaile glassmorphism-ylapalkin kanssa. Erotus alas
   tonal-shiftilla (ohut surface-mid-border, ghost). */

.sivuotsikko {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-4);
}
.sivuotsikko .takaisin {
  display: inline-block;
  font-size: .8125rem;
  font-family: var(--font-label);
  color: var(--color-muted);
  text-decoration: none;
  margin-bottom: var(--space-2);
}
.sivuotsikko .takaisin:hover {
  color: var(--color-primary);
}
.sivuotsikko .rivi {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sivuotsikko .vasen { min-width: 0; }
.sivuotsikko h1 {
  font-family: var(--font-editorial);
  font-size: 1.5rem;
  line-height: var(--line-height-tight);
  margin: 0;
  word-break: break-word;
}
.sivuotsikko .meta {
  margin-top: var(--space-2);
  font-family: var(--font-label);
  font-size: .8125rem;
  color: var(--color-muted);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  align-items: center;
}
.sivuotsikko .meta .erotin {
  color: var(--color-border);
}
.sivuotsikko .oikea {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: center;
}

/* Desktop: otsikko ja actions samalle riville */
@media (min-width: 768px) {
  .sivuotsikko {
    padding: var(--space-5) 0;
    margin-bottom: 0;
  }
  .sivuotsikko .rivi {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-4);
  }
  .sivuotsikko h1 {
    font-size: 2rem;
  }
}

/* Kontekstikaista — rullaava konteksti-otsikko someruutukaappausta varten
   (kontekstikaista.php, palaute #191). Oma komponenttinsa: mika tahansa sivu
   voi kutsua sen, ei vain sivuotsikkoa kayttavat. Renderoidaan otsikkolohkon
   SIBLINGINA .container-kaareen sisalla -> sticky-containing-block ulottuu
   sivun loppuun ja kaista pysyy pinnattuna koko scrollin ajan.
   Ilmestyy ylapalkin (.ylapalkki, z-index 100) ALLE kun iso otsikko on
   rullannut pois nakyvista (.nakyvissa, app.js togglaa). Nakyy kaikilla
   leveyksilla (myos desktop). Glass-tausta matchaa .ylapalkki:n -> nayttaa
   ylapalkin jatkeelta. */
.kontekstikaista {
  position: sticky;
  top: var(--ylapalkki-korkeus, 52px);
  z-index: 99;                       /* ylapalkin 100 alle -> ei peita navia */
  /* Piilotila: max-height 0 + overflow hidden EI jata layout-aukkoa (sticky
     varaa 0px korkeutta flowssa kunnes .nakyvissa avaa). Vaihtoehto transform
     jattaisi tyhjan sticky-slotin. */
  max-height: 0;
  overflow: hidden;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border-bottom: 1px solid var(--color-border);
  transition: max-height var(--transition-base);
}
/* Etuliite + nimi samalla rivilla: flex jotta etuliite ei tyonna nimea omalle
   riville ja ellipsis koskee vain (pitkaa) nimea. Padding kaistan tasolla. */
.kontekstikaista.nakyvissa {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: var(--space-2) 0;
  max-height: 4rem;                  /* riittaa yhdelle padding+teksti-riville */
}
.kontekstikaista .etuliite {
  flex: none;                        /* etuliite ei kutistu, nimi saa ellipsoitua */
  font-family: var(--font-editorial);
  line-height: var(--line-height-tight);
  color: var(--color-muted);         /* hillitty sava -> nimi erottuu painokkaammaksi */
}
.kontekstikaista .nimi {
  font-family: var(--font-editorial);
  font-size: 1.0625rem;
  line-height: var(--line-height-tight);
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;           /* pitkat nimet ("Euroopan unioni") katkeavat siististi */
}

/* --- hero ---
   Kaikki hero-tyyli yhdessä: perustila, erikoiset (404), desktop.
   Kun lisäät uuden hero-variantin, se tulee tähän lohkoon — ei
   tiedoston alaosaan eikä kommentin "responsive" alle. */

/* Pico.classless lisaa main-elementille block-spacingin — kumoa hero siina */
body > main > .hero {
  margin-top: calc(var(--pico-block-spacing-vertical) * -1);
}

/* Hero — mobile-first: kapea padding + pieni h1 + stacked hakulomake */
.hero {
  padding: var(--space-5) var(--space-3);
  text-align: center;
  background: linear-gradient(180deg, var(--color-hero-gradient) 0%, transparent 100%);
  box-sizing: border-box;
  /* Ankkurihyppy heroon (esim. /analysoi "Syötä osoite" -> #analysoi-alku, #652):
     .ylapalkki on sticky top:0 ja peittaisi heron ylareunan. --ylapalkki-korkeus
     asetetaan ajossa ylapalkin todellisesta korkeudesta (app.js), sama muuttuja
     kuin .kontekstikaista:n top-arvossa; fallback 52px matchaa sita.
     Vrt. .tag-list.alphabetical .aakkos-otsikko (r. ~1653). */
  scroll-margin-top: calc(var(--ylapalkki-korkeus, 52px) + var(--space-3));
}

/* Leveysrajaus isolla naytolla (Teemu 12.8.2026: "isolla ruudulla etusivun
   heron teksti on todella levea").

   RAJAUS KOHDISTUU LAPSIIN, EI `.hero`:on ITSEENSA. Taustagradientti on
   full-bleed (reunasta reunaan) — jos `max-width` laitettaisiin `.hero`:on,
   gradientti kutistuisi keskelle ruutua ja ulkoasu hajoaisi isolla naytolla
   pahemmin kuin ennen korjausta.

   Kaksi tasoa, koska ne ratkaisevat eri ongelman:
   1) `.hero > *` = `--content-max` — sama raja kuin `.container`illa, eli
      hero rivittyy muun sivun kanssa samaan linjaan (Teemun pyynto
      "rajoittaa kuten container rajoittaisi"). Kohdistuu myos `content`-
      slottiin (/analysoi: medialista), joka oli ainoa aidosti 1400px:aa
      leveammaksi venynyt lohko otsikon ja selitteen lisaksi.
   2) Leipateksti kapeampaan luettavaan mittaan, ks. `.hero p` alla. 1400px
      ei riita: etusivun selite on 219 merkkia, joka olisi 1400px:lla
      ~2 rivia poikkeuksellisen pitkaa riviä.

   MOBIILI: molemmat ovat `max-width`, eli ne eivat voi sitoa kapealla
   naytolla — 360px:n ruudulla heron sisaltolaatikko on 328px eika 560px:n
   raja aktivoidu lainkaan. Siksi saannot ovat perustyylissa eivatka
   media queryssa: sama mobile-first-kuvio kuin `.hero-haku`illa ja
   `.hero-askeleet`illa, jotka rajaavat leveytensa samalla tavalla.

   Tarkempi rajaus voittaa taman aina: `.hero figure` (480px),
   `.hero-askeleet` ja `.hero-haku` (560px) ovat elementti- tai
   luokkatarkennettuja ja pysyvat omissa mitoissaan. */
.hero > * {
  max-width: var(--content-max);
  margin-left: auto;
  margin-right: auto;
}
.hero figure { margin: 0 auto var(--space-3); max-width: 480px; }
.hero h1 { font-size: 1.75rem; margin-bottom: var(--space-3); }
.hero p {
  font-size: 1.1rem;
  color: var(--color-muted);
  margin-bottom: var(--space-5);
  /* Taso 2 leveysrajauksesta (ks. `.hero > *` ylla): selite kapeampaan
     mittaan kuin muu hero. Etusivun selite on 219 merkkia — pelkalla
     `--content-max`illa (1400px) se olisi kaksi poikkeuksellisen pitkaa
     rivia, mika on teknisesti "rajattu" mutta yha luettavuudeltaan huono.
     Auto-marginaalit toistetaan tassa tarkoituksella vaikka `.hero > *` jo
     antaa ne: ilman niita `p`-elementin perussaanto (osio 2, `margin: 0 0 …`)
     jattaisi lohkon vasempaan reunaan jos `> *` joskus poistetaan, jolloin
     keskitetty teksti hyppaisi hiljaa ruudun laitaan. */
  max-width: var(--content-reading);
  margin-left: auto;
  margin-right: auto;
}
.hero .title { font-size: 2.5rem; margin-bottom: var(--space-2); }
.hero .caption { color: var(--color-muted); margin-bottom: var(--space-4); }
.hero .btn { /* hero-kohtainen nappi */ }

/* Kavelytysaskeleet (hero-komponentin `askeleet`-prop, array-muoto).
   Jokainen askel on oma <li> jotta rivinvaihto tapahtuu VAIN askelten
   valista: yhtena merkkijonona yhdessa <p>:ssa 360px:n naytto katkaisi
   rivin kesken askelen ja jatti nuolen edellisen rivin loppuun irralleen
   omasta askeleestaan (Teemu 12.8.2026 — 59 % avautumispaivan liikenteesta
   oli mobiilia). Rivitysta ei voi rajata tekstisolmun sisalla, joten korjaus
   on rakenteessa eika CSS-kikassa.
   Numero ja nuoli tulevat CSS-counterista: tekstikirjastossa on pelkka
   askelen sisalto, joten jarjestyksen muutos ei vaadi kasin numerointia. */
.hero-askeleet {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1) var(--space-2);
  list-style: none;
  max-width: 560px;
  margin: 0 auto var(--space-5);
  padding: 0;
  color: var(--color-muted);
  counter-reset: hero-askel;
}
.hero-askeleet li {
  counter-increment: hero-askel;
  margin: 0;
  /* Pisin askel on ~25 merkkia (~210px) — mahtuu 320px:n naytolle heron
     paddingin jalkeen, joten nowrap ei voi tuottaa vaakavieritysta. */
  white-space: nowrap;
}
.hero-askeleet li::before { content: counter(hero-askel) ". "; }
/* Nuoli kuuluu askeleen SISAAN (::after) — silloin se ei voi rivityksen
   jalkeen koskaan aloittaa uutta rivia, toisin kuin erillisena merkkina
   tekstivirrassa. */
.hero-askeleet li:not(:last-child)::after { content: " →"; }

/* Hero-hakulomake (palvelu, analysoi) — mobile-first: pystysuuntainen pino */
.hero-haku {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 560px;
  margin: 0 auto var(--space-4);
}
/* TYYPPIAGNOSTINEN selektori — ala sido tata `input[type=...]`:iin (#656/#674,
   spec/komponentit.md § analyysijono). Kentta oli `type="url"` mutta muuttui
   heron eristyksessa muotoon `type="text" inputmode="url"`, jolloin vanha
   `.hero-haku input[type="url"]` lakkasi osumasta hiljaa: kentta putosi Picon
   oletustyyliin (42px) ja nappi jai 16px korkeammaksi. Ei virhetta, ei
   testiosumaa — vain vaara ulkoasu tuotannossa (Teemu 11.8.2026).

   Muoto `> input:not([hidden])` on speksin vakiintunut kuvio
   (spec/frontend-css.md § Picon lomakesaannot): `:not()` nostaa
   spesifisyyden (0,2,1):een joka voittaa Picon `input:not([type=checkbox]…)`
   -korkeussaannon (0,1,1), ja lapsivalitsin rajaa saannon lomakkeen omaan
   kenttaan. `height: auto` kumoaa Picon kiinteän korkeuden, jolloin
   flex-stretch tasaa kentan ja napin samaan korkeuteen. */
.hero-haku > input:not([hidden]) {
  flex: 1 1 0%;
  width: 100%;
  font-size: 1rem;
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  transition: border-color var(--transition-fast);
  height: auto;
  min-width: 0;
}
.hero-haku > input:not([hidden]):focus {
  border-color: var(--color-primary);
  outline: 2px solid var(--color-ring);
}
.hero-haku .painike {
  flex: 0 0 auto;
  white-space: nowrap;
  width: 100%;
}

/* Hero-napin sisainen ikoni/teksti-vaihto:
   Mobiilissa kapea (481-640) nayttaa ikonin, muuten teksti. */
.hero-btn-icon { display: none; }
.hero-btn-check {
  display: inline-flex;
  align-items: center;
  font-size: 1.25rem;
  font-weight: 700;
  animation: check-pop 0.4s ease-out;
}
@keyframes check-pop {
  0%   { transform: scale(0); opacity: 0; }
  50%  { transform: scale(1.3); }
  100% { transform: scale(1); opacity: 1; }
}

/* --- medialista (domain-komponentti) ---
   Lista medialahteista. Sama komponentti kolmessa kayttopaikassa:
   analyze.php hero (sisaiset linkit, grid), alapalkki (ulkoiset linkit,
   kompakti), /mediat-arkistosivu (sisaiset linkit, grid).
   Variantit tilaluokkina: .medialista.grid, .medialista.kompakti.
   Korvasi .hero-tags-luokan 9.5.2026 (#401). */
.medialista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.medialista li { margin: 0; padding: 0; list-style: none; }

/* Grid-variantti: .merkki-tagien rivi (analyze hero, /mediat). Keskitetty hero-kontekstissa. */
.medialista.grid { justify-content: center; }
.medialista.grid a.merkki { text-decoration: none; }

/* Kompakti-variantti: tekstilinkit (footteri). Pieni font, tiheampi gap. */
.medialista.kompakti {
  gap: var(--space-1) var(--space-2);
}
.medialista.kompakti .kompakti-linkki {
  font-family: var(--font-label);
  font-size: 0.8125rem;
  color: var(--color-muted);
  text-decoration: none;
  transition: color var(--transition-fast);
}
.medialista.kompakti .kompakti-linkki:hover {
  color: var(--color-text);
}

/* Footter-konteksti: pinotaan disclaimerin alle, otsikko erottuu nav:sta */
.alapalkki .medialista {
  margin-bottom: var(--space-3);
}
.alapalkki .mediat-otsikko {
  display: block;
  font-family: var(--font-label);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin-bottom: var(--space-1);
}

/* Mediat-arkistosivu (#401, 9.5.2026) — kortit ruudukossa.
   Hyodyntaa olemassa olevaa .kortti.compact-komponenttia. */
.mediat-arkisto {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-3);
}
.media-arkisto-kortti h3 { margin-top: 0; margin-bottom: var(--space-2); }
.media-arkisto-kortti h3 a { text-decoration: none; }
.media-arkisto-kortti h3 a:hover { text-decoration: underline; }
.media-arkisto-kortti .meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-1);
}

/* Kortin kompaktit jakaumapalkit (task #560): poliittinen, vinouma, laatu.
   Leima jokaisen palkin ylle (kaanteinen variskaala vinouma/laatu). */
.media-tilastot { margin-top: var(--space-3); }
.media-tilastot .info-teksti { display: block; }
.media-tilastot .vinouma-palkki,
.media-tilastot .laatu-jakauma,
.media-tilastot .poliittinen-suuntaus-palkki {
  margin: var(--space-1) 0 var(--space-2);
}
.media-tilastot .tilasto-rivi:last-child .vinouma-palkki,
.media-tilastot .tilasto-rivi:last-child .laatu-jakauma,
.media-tilastot .tilasto-rivi:last-child .poliittinen-suuntaus-palkki {
  margin-bottom: 0;
}

/* Hero-aluueen ilmoitus + hidas odotus-vihje (analyze-sivu) */
.hero-alert {
  max-width: 560px;
  margin: 0 auto var(--space-3);
  text-align: left;
}
.hero-wait-hint {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
/* analyze.php hero-wait-hint sisaspan + warning-alertin p: korvaa text-sm */
.hero-wait-hint span { font-size: 0.875rem; }
.hero-alert.warning p { font-size: 0.875rem; }

/* Tabletti (481px+): rivilayout + ikonimuoto napissa, teksti piilossa */
@media (min-width: 481px) {
  .hero-haku { flex-direction: row; }
  .hero-haku > input:not([hidden]) { width: 0; }
  .hero-haku .painike { width: auto; padding: var(--space-3); }
  .hero-haku .painike .hero-btn-text { display: none; }
  .hero-haku .painike .hero-btn-icon { display: inline-block; vertical-align: middle; }
}

/* Leveampi mobile/tabletti (641px+): teksti takaisin nakyviin napissa, ikoni piiloon */
@media (min-width: 641px) {
  .hero-haku .painike { padding: var(--space-2) var(--space-4); }
  .hero-haku .painike .hero-btn-text { display: inline; }
  .hero-haku .painike .hero-btn-icon { display: none; }
}

/* Desktop (768px+): leveampi hero-padding + isompi title/caption */
@media (min-width: 768px) {
  .hero { padding: var(--space-7) var(--space-4); }
  .hero h1 { font-size: 2.5rem; }
  .hero .title { font-size: 3.5rem; }
  .hero .caption { font-size: 1.25rem; }
}

/* Sivukohtainen erikoishero */
body.404 .hero { padding: var(--space-6) 0; }
body.404 .hero .title { color: var(--color-muted); }

/* --- main + sisäinen rakenne --- */
main { padding: var(--space-4) 0; }
main section { margin-bottom: var(--space-5); }
article { margin-bottom: var(--space-4); }

/* --- alapalkki ---
   Ghost-border ylareunaan, muted-tekstit, linkit flexilla. */

.alapalkki {
  border-top: 1px solid var(--color-border);
  padding: var(--space-5) 0 var(--space-4);
  margin-top: var(--space-5);
}

.alapalkki .disclaimer {
  display: block;
  color: var(--color-muted);
  font-family: var(--font-label);
  font-size: .75rem;
  margin-bottom: var(--space-3);
}

.alapalkki nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
}
.alapalkki nav a {
  font-size: .8125rem;
  color: var(--color-muted);
  text-decoration: none;
  transition: color var(--transition-fast);
}
.alapalkki nav a:hover {
  color: var(--color-text);
}
.alapalkki nav a:not(:last-child)::after {
  content: "|";
  color: var(--color-border);
  font-size: .75rem;
  margin-left: var(--space-2);
}

.alapalkki .tekijanoikeus {
  display: block;
  color: var(--color-muted);
  font-family: var(--font-label);
  font-size: .6875rem;
}

/* Desktop */
@media (min-width: 768px) {
  .alapalkki nav {
    gap: var(--space-2);
  }
  .alapalkki nav a:not(:last-child)::after {
    content: none;
  }
}

/* ============================================================
   6. KOMPONENTIT — itsenäiset palikat
   Komponentit toimivat missä tahansa DOM:ssa. Ei oletuksia
   äitiluokasta. Leveys täyttää aina äitinsä. Kaikki komponentin
   perus + variantit + sivu-overridet + media queryt yhdessä lohkossa.
   ============================================================ */

/* --- info-teksti + tyhja-tila (#790) ---
   Selittava alateksti ja tyhjan tilan viesti. Molemmilla oli 7 ja 6
   kontekstisaantoa mutta EI perussaantoa -> 49 + 27 kayttopaikasta iso osa
   renderoitiin Picon leipatekstina (1rem, --color-text). Yksi ryhmitelty
   saanto, kontekstisaannoista poistettu toisto (auditointi H4-css-15).
   Vain font-size + color: marginaalit tulevat .mt- ja .mb-utiliteista
   (osio 7, sama spesifisyys 0,1,0 -> myohempi voittaa) ja komponenttien
   omista saannoista. HUOM: ei "*" + "/" -paria kommentissa, se sulkisi sen. */
.info-teksti,
.tyhja-tila {
  font-size: .875rem;
  color: var(--color-muted);
}

/* <small class="info-teksti"> sailyttaa osion 2 pienemman koon (.75rem).
   Ilman tata luokka KASVATTAISI 11 elementin kokoa: mediat.php:n
   .media-tilastot-leimat, article.php:n mittarilabelit, tilastokytkos-
   laatikon alaviite. Luokan tehtava on muted-selite, ei koon nosto. */
small.info-teksti { font-size: .75rem; }

/* --- hakukentta (kehys-komponentti, #706) ---
   Sivuston haun lomake. Toimii missa tahansa aidissa: tayttaa leveyden,
   ei oleta ylapalkkia. Kontekstikohtaiset kavennukset tassa samassa
   lohkossa (frontend-css.md § 8: kaikki komponentin tyylit yhdessa).

   ⚠️ Selektorit ovat tyyppiagnostisia (.hakukentta input) — EI
   input[type="search"]. Tyyppiin sitominen katkeaa hiljaa jos input-tyyppi
   vaihtuu (#656/#674-oppi analyysijonon kentasta). */
.hakukentta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  margin: 0;

  /* Lisakentat lomakkeen sisalla (#711 koostehaun lahdevalinta). flex-wrap on
     turvallinen lisays: kahden lapsen lomakkeet (ylapalkki, /haku) mahtuvat
     yhdelle riville kuten ennenkin — vain kolmas lapsi kaareytyy omalle. */
  flex-wrap: wrap;

  /* Pico piirtaa type=search-kenttaan oman suurennuslasin taustakuvana
     (`background-image: var(--pico-icon-search)`). Napin ikoni riittaa —
     kahta lasia ei nayteta. Nollaus tehdaan Picon omalla muuttujalla eika
     background-image-saannolla: Picon selektori on
     `input:not([type=checkbox],...)[type=search]` eli spesifisyys (0,2,1)
     (`:not()` perii argumenttinsa spesifisyyden), jota `.hakukentta input`
     (0,1,1) ei voita. Muuttuja periytyy inputiin, jolloin Picon oma saanto
     resolvoituu arvoon `none` — ei spesifisyyskisaa lainkaan. */
  --pico-icon-search: none;

  /* 3) RYHMAKUORI (palaute #144, 11.8.2026). Kolmas kerros samaa #744:n
     juurisyyta: `[role=search]` ei ole Picossa pelkka landmark vaan
     ryhmakomponentti. Kaksi edellista kerrosta neutraloivat ryhmatulkinnan
     LAPSILTA (input, nappi); tama neutraloi lomakkeen OMAN kuoren:

       [role=search] { border-radius: var(--pico-border-radius);   -> 5rem
                       box-shadow: var(--pico-group-box-shadow, 0 0 0 transparent);
                       transition: box-shadow ... }
       [role=search]:has(input:not([type=submit],[type=button]):focus)
                     { --pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-input) }

     Mitattu tuotannon CSS:lla 360x674-viewportissa (CDP,
     Emulation.setFocusEmulationEnabled): kun paivamaarakentta sai fokuksen,
     lomakkeen laskettu tyyli oli `box-shadow: rgb(207,213,226) 0 0 0 1px`
     ja `border-radius: 80px`. Koska `lisakentat`-propsi (#711, #728) tuo
     lahdevalinnat ja aikakentat SAMAN lomakkeen sisalle, kuori piirtyi koko
     hakualueen ymparille 80 px:n pyoristyksella. Teemun sanoin: "kun pvm
     kentan aktivoi, joku pyoristettyreuna ilmestyy hassusti taustalle
     ympyroiden koko ns. hakualueen".

     ⚠️ MITTAUS VAATII TRANSITION ODOTUKSEN. `transition: box-shadow` tekee
     valittomasti focus():n jalkeen luetusta arvosta transition ALKUARVON
     (transparent) — ensimmainen mittaus antoi valheellisen vihrean. Jos
     todennat tata uudelleen, odota ~600 ms ennen getComputedStylea.

     Kumous ei tarvitse spesifisyyskisaa: `.hakukentta` (0,1,0) on tasoissa
     `[role=search]`:n (0,1,0) kanssa, ja app.css ladataan Picon jalkeen
     (kehys/Views/components/layout.php) -> lahdejarjestys ratkaisee.
     Nollataan itse ominaisuus eika `--pico-group-box-shadow` -muuttuja,
     koska muuttujan asettava `:has()`-saanto on (0,3,1) ja voittaisi
     komponenttiluokan. `role="search"` SAILYY (ruudunlukija tarvitsee sen);
     vain visuaalinen ryhmatulkinta neutraloidaan — sama linja kuin
     kerroksissa 1-2. */
  box-shadow: none;
  border-radius: 0;
}
/* Hakusanakentan oma tyyli. LAPSIVALITSIN ON PAKOLLINEN (palaute #136,
   11.8.2026): jalkelaisvalitsimena (`.hakukentta input`) tama vuoti
   `.hakukentta-lisa`:n sisalle ja tyylitti lahdevalinnan checkboxit
   pillereiksi — `border-radius: var(--radius-full)` + vaakapadding + `flex: 1`.

   Pahin seuraus ei ollut muoto vaan VALINTATILAN KATOAMINEN. Picon
   `[type=checkbox]:checked` (0,2,0) asettaa `background-image`n (valkoinen
   check-SVG) ja MUUTTUJAN `--pico-background-color`, mutta itse taustavarin
   maalaa `input,select,textarea` (0,0,1) — jonka tama saanto (0,1,1) voitti
   arvolla `background: transparent`. Lopputulos: valkoinen check valkoista
   taustaa vasten = rastia ei nay lainkaan. Koostehaku veloittaa krediitteja
   valittujen lahteiden maaran mukaan (#787), joten vaara kasitys valinnasta
   on vaara kasitys hinnasta.

   Sama ansa kuin #744:ssa (ks. `.hakukentta-aika`-kommentti alla) ja
   frontend-css.md § "Kun nostat spesifisyytta, tarkista mika muu nojasi
   matalampaan": combinator ei muuta spesifisyytta mutta estaa osuman
   sisakkaisiin lohkoihin. HUOM: kun tama saanto rajattiin, `.hakukentta-aika
   input` (#728) menetti perimansa arvot -> ne on toistettu siella
   eksplisiittisesti. Jos lisaat tanne uuden deklaraation, tarkista tarvitseeko
   se saman kompensaation. */
.hakukentta > input {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-outline);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-body);
}
/* Picon ylikirjoitukset inputille. Kaksi eri juurisyyta, sama (0,3,1)-kuvio.

   ⚠️ MITOITA KORKEINTA OSUVAA HAARAA VASTAAN, ALA ENSIMMAISTA LUKEMAASI.
   Picon saantolohkot ovat pitkia valitsinLISTOJA, ja listan jokaisella
   haaralla on OMA spesifisyytensa — voittaja on korkein osuva, ei se joka
   sattuu olemaan luettavimmin kirjoitettu. Sama lohko sisaltaa esim.
   seka `[role=search]>:not(:last-child)` (0,2,0) etta
   `[role=search] input:not([type=checkbox],[type=radio]):not(:last-child)`
   (0,3,1) — jalkimmainen on se jota vastaan pitaa mitoittaa. Tama maksoi
   #744:ssa yhden deploy-kierroksen: (0,2,1)-tasaus riitti neljaan
   ominaisuuteen mutta havisi kahteen (inputin oikeat kulmat, napin
   margin-left).

   Spesifisyys nostetaan TOISTAMALLA KOMPONENTTILUOKKA (`.hakukentta.hakukentta`)
   eika lisaamalla toista `:not([attribuutti])`-suodinta. Syy: attribuuttiin
   sidottu tayte lakkaa osumasta juuri siina tilassa jonka attribuutti kuvaa.
   `:not([disabled])` olisi pudonnut pois heti kun nappi disabloidaan — ja
   komponentin oma docblock lupaa juuri sellaisen laajennuksen ("live-haku"),
   jolloin layout hajoaisi latauksen ajaksi. Luokan toisto ei riipu mistaan
   tilasta: se osuu aina kun komponentti on olemassa. Tarkeyslippua ei tarvita.

   Lahdejarjestys ratkaisee tasapelit (app.css ladataan Picon jalkeen, ks.
   kehys/Views/components/layout.php). `[hidden]` on tyyppiagnostinen —
   selektoria EI sidota `input[type="search"]`:iin (#656/#674). Lapsivalitsin
   rajaa saannot lomakkeen omaan hakukenttaan: `.hakukentta-lisa`:n sisalto
   (#711 lahdevalinta, #728 aikajanne) jaa koskematta.

   1) RYHMATULKINTA (#744). Pico kohtelee `role="search"`-lomaketta
      RYHMAKOMPONENTTINA — samaa saantoperhetta kuin `[role=group]`, ei pelkkaa
      saavutettavuuslandmarkia. Se liimaa lapset yhdeksi kontrolliksi:
      `[role=search] input:not(...)` antaa `flex: 1 1 auto`, jolloin inputin
      peruskoko on Picon `width: 100%` eli koko kontin leveys → `flex-wrap`
      tyonsi lahetysnapin omalle rivilleen. `flex-basis: 0` lopettaa taman.
      `input:not(...):not(:last-child)` (0,3,1) litistaa inputin oikeat kulmat
      0:ksi ja `>:first-child` (0,2,0) pyoristaa vasemmat 5rem:iin — pilleri
      hajosi group-lookiksi. `role="search"` SAILYY (ruudunlukija tarvitsee
      sen); vain visuaalinen ryhmatulkinta neutraloidaan.

   2) IKONIVARAUS. Sama type=search -saanto joka piirsi suurennuslasin (ks.
      --pico-icon-search -kommentti ylla) varaa sille myos tilaa
      `padding-inline-start: calc(... + 1.75rem)`. Kun ikoni on nollattu,
      varaus on turhaa kaistaa. */
.hakukentta.hakukentta > input:not([hidden]) {
  flex: 1 1 0;
  border-radius: var(--radius-full);
  padding-inline-start: var(--space-3);
}
.hakukentta button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
/* Hover koskee vain ikoninappia. Tekstillinen nappi saa kuorensa kutsujan
   luokasta (`nappilisaluokka`, esim. `painike maksullinen`), ja tama saanto
   (0,2,1) voittaisi sen taustavarin (0,2,0) → sininen nappi valahtaisi
   harmaaksi hoverissa vaalean tekstin kanssa. Rajaus varianttiluokalla eika
   kutsujan luokalla: kehys ei saa tuntea `.painike`-variantteja. */
.hakukentta button:not(.tekstillinen):hover {
  background: var(--color-surface-high);
  color: var(--color-primary);
}
/* Picon ryhmatulkinta napille — sama juurisyy ja sama (0,3,1)-kuvio kuin
   inputilla (ks. kommentti ylla: korkein osuva haara + luokan toisto).
   Kumottavat haarat ja niiden spesifisyydet:
     `[role=search]>*`                            (0,1,0) flex-grow: 1
        → nappi venyi taysleveaksi omalle rivilleen; sen `width: 40px` ei
          kadonnut, grow vain ohitti sen
     `[role=search] [type=submit]`                (0,2,0) width: auto
     `[role=search]>:not(:first-child)`           (0,2,0) vasemmat kulmat 0
     `[role=search] [type=submit]:not(:first-child)` (0,3,0) margin-left: -1px

   Huomaa viimeinen: `type="submit"` -haara on korkeampi kuin saman listan
   `button:not(:first-child)` (0,2,1), ja juuri se jai voittamaan kun tama
   saanto oli viela (0,2,1). Ilman naita ikoninappi renderoityy inputin
   jatkeena, ei erillisena pyoreana painikkeena. */
.hakukentta.hakukentta > button:not([hidden]) {
  flex: 0 0 auto;
  width: 40px;
  border-radius: var(--radius-full);
  margin-left: 0;
}

/* Variantti: lahetysnappi jossa on NAKYVA teksti (`nappiteksti`-props, #747).
   Kiintea 40px on ikoninapin mitta — tekstin kanssa nappi tarvitsee leveytta
   ja vaakapaddingin. Vareja tama ei aseta: ne tulevat kutsujan luokasta
   (`nappilisaluokka`), koska kehys ei tieda onko toiminto maksullinen.

   Spesifisyys: kumottava saanto on `.hakukentta.hakukentta > button:not([hidden])`
   (0,3,1) — ks. Picon ryhmatulkinta-kommentti ylla. Tama toistaa
   komponenttiluokan JA lisaa varianttiluokan → (0,4,1). Sama luokantoisto-
   kuvio kuin muualla lohkossa: se ei riipu mistaan elementin tilasta
   (`:not([disabled])` olisi pudonnut pois juuri disabloidusta napista). */
.hakukentta.hakukentta > button.tekstillinen:not([hidden]) {
  width: auto;
  padding: var(--space-1) var(--space-3);
}

/* Variantti: hakutulossivun oma kentta — leveampi, ei ylapalkin kavennusta. */
.hakukentta.iso {
  max-width: 32rem;
}

/* Lisakentat lomakkeen sisalla (#711 koostehaun lahdevalinta).
   HUOM: .hakukentan oma `flex-wrap: wrap` asuu komponentin perussaannossa
   (frontend-css.md § 8: kaikki komponentin tyylit yhdessa lohkossa). */
.hakukentta-lisa {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-1);
  padding-left: var(--space-3);
}
.lahde-valinta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  font-size: .875rem;
  cursor: pointer;
}
/* Checkbox jaa TARKOITUKSELLA Picon natiiviin ulkoasuun: rastitettuna Pico
   maalaa taustan (`--pico-primary-background`) ja piirtaa valkoisen check-SVG:n.
   Se on yksiselitteisin mahdollinen valintaindikaattori — omaa taustaa TAI
   `background`-shorthandia ei saa asettaa tanne, koska shorthand nollaisi
   Picon `background-image`n ja rasti katoaisi (juuri nain kavi #136:ssa
   baselohkon vuodon kautta, ks. `.hakukentta > input` -kommentti).
   `margin: 0` kumoaa Picon `margin-inline-end: .5em` -varauksen; valistys
   tulee labelin `gap`ista. `flex: 0 0 auto` estaa kutistumisen kapealla
   rivilla — Picon `width: 1.25em` on flex-itemin perusleveys, ei minimi. */
.lahde-valinta input {
  flex: 0 0 auto;
  margin: 0;
}
/* Valinta erottuu myos ILMAN varia: sininen tayttö on ensisijainen signaali,
   mutta se katoaa harmaasavytulostuksessa, korkean kontrastin tiloissa ja
   varisokealta. Rakenne `<input> + <span>` (koostehaku.php) sallii taman
   ilman `:has()`-tukea. */
.lahde-valinta input:checked + span {
  font-weight: 600;
}

/* Aikajanteen kentat hakutulossivulla (#728). Sama rakenne kuin
   .lahde-valinta: label kaaria selitteen ja kentan yhdeksi pariksi.

   MOBIILISSA KUMPIKIN LABEL OTTAA OMAN RIVINSA (palaute #144, 11.8.2026).
   Teemu Androidilla 360x674: "Paivamaararajauslaatikot toimii huonosti
   mobiilissa, paivamaara ei nay. Pitaisi rivittaa kentat, etta tilas
   enempi sivusuunnassa."

   Aiempi `flex: 0 0 auto` piti kentan natiivissa oletusleveydessaan, joka
   on SELAINKOHTAINEN: desktop-Chromen mobiiliemulaatiossa se on 167 px ja
   mahtuu (mitattu 11.8.2026 — sisalto ei leikkautunut), mutta Androidin
   natiivi paivamaaravalitsin on leveampi eika sen sisalto mahtunut.
   Emulaatio ei renderoi natiiveja lomakekontrolleja Androidin tavalla,
   joten tata EI voi todentaa emulaatiolla — siksi korjaus tehdaan
   rakenteellisesti eika mittaamalla kentan leveytta:

     - label `flex: 1 1 100%`  -> oma rivi, koko kaytettavissa oleva leveys
     - input `flex: 1 1 auto`  -> venyy rivin loppuun selitteen jalkeen

   Nain kentan leveys ei enaa riipu selaimen oletuksesta vaan rivin
   leveydesta: mita ikina natiivikontrolli haluaakin nayttaa, tilaa on koko
   rivin verran. `min-width: 0` sallii kutistumisen jos natiivileveys on
   suurempi kuin rivi (flex-itemin oletus `min-width: auto` estaisi sen ja
   palauttaisi ylivuodon).

   Desktopilla palautetaan entinen rinnakkaisasettelu: siella tilaa on ja
   alkaen+asti vierekkain on tiiviimpi. Mobile-first (frontend-css.md § 7):
   perustyyli = mobiili, media query lisaa desktopin.

   Spesifisyys: baselohkon KUMPIKAAN saanto ei osu tanne — molemmat ovat
   lapsivalitsimia lomakkeen suoriin lapsiin (`.hakukentta > input` ja
   `.hakukentta.hakukentta > input:not([hidden])`), ja nama inputit ovat
   `.hakukentta-lisa`:n sisalla.

   ⚠️ SIKSI KENTTA EI PERI MITAAN — kaikki tarvittava on toistettava tassa.
   Rivit `margin`/`border`/`background`/`color`/`font-family` EIVAT ole
   koristetta: ilman niita Pico palauttaa omat oletuksensa (mm.
   `margin-bottom: 1rem` -> kentat irtoavat rivista, valkoinen tayttö ->
   lohko lakkaa olemasta lapinakyva). Ne perittiin baselohkosta 11.8.2026
   asti, jolloin base rajattiin lapsivalitsimeksi #136:n takia.
   `background: transparent` on shorthand kuten baselohkossakin: se ei vie
   Picon kalenteri-ikonia, koska ikonin asettava date-saanto on (0,2,1). */
.hakukentta-aika {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  cursor: pointer;
  flex: 1 1 100%;
}
.hakukentta-aika input {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-outline);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-body);
}
@media (min-width: 768px) {
  .hakukentta-aika {
    flex: 0 0 auto;
  }
  .hakukentta-aika input {
    flex: 0 0 auto;
  }
}

/* Koostehaun tuloskontti (#711). Latausvaiheessa sisalto on yksi info-rivi;
   fragment korvaa sen artikkelilistalla. */
.koostehaku-tulokset[aria-busy="true"] {
  opacity: .7;
}

/* Kategoriakoosteen napit (#947, palaute #210). Rivi maksullisia LINKKEJA:
   klikkaus vie kategoriasivulle (`?kategoria=X&hae=1`), joten elementti on
   <a> eika <button> — navigaatio, ei lomakkeen toiminto.

   Jokainen <li> on pystysuora pari: nappi + sen alla lahdenimet. Lahteet ovat
   napin VIERESSA eivatka sen sisalla, koska nimilista kasvaa kun medioita
   lisataan konfiguraatioon; kaksirivinen nappi rikkoutuisi ensimmaisesta
   lisayksesta. Molemmat luetaan silti ennen klikkausta, mika on koko
   vaatimuksen ydin (task #947: "kaikista medioista" olisi vale).

   Ei omaa font-sizea: lahderivi kaytta <small>-elementtia, jolloin koko tulee
   selaimen perustyylista eika app.css:n font-size-laskuri kasva (kynnys 232,
   nykytila 227). */
.koosta-kategoriat {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  list-style: none;
  padding: 0;
  margin: 0;
}
.koosta-kategoriat li {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
/* Auki oleva kategoria. EI .ensisij-varianttia: `.maksullinen` omistaa jo
   sinisen (#747), joten korostus tehdaan reunuksella jotta hinnan signaali
   ei sekoitu valinnan signaaliin. */
.koosta-kategoriat .valittu {
  outline: 2px solid var(--color-outline);
}

/* Konteksti: ylapalkki. Mobiilissa piilotettu, koska mobiilivalikossa on oma
   kutsu — nakyvia hakukenttia on aina tasan yksi. */
.ylapalkki .hakukentta {
  display: none;
}
.ylapalkki .mobiilivalikko-haku {
  padding: var(--space-2) 0;
}
.ylapalkki .mobiilivalikko-haku .hakukentta {
  display: flex;
}
@media (min-width: 768px) {
  .ylapalkki .hakukentta {
    display: flex;
    width: auto;
    flex: 0 1 14rem;
    margin-left: var(--space-2);
  }
}

/* --- hakutulos-ryhma (domain-komponentti, #706) ---
   Yhden osumatyypin lohko hakutulossivulla (/haku): otsikko + linkkilista.
   Ei korttikehysta — tama on paarungon tulosryhma, ei sivupalkin kortti
   (ks. spec/komponentit.md perustelu vs. sivupalkki-kortti). */
.hakutulos-ryhma {
  margin-top: var(--space-5);
}
.hakutulos-ryhma h2 .maara {
  color: var(--color-muted);
  font-family: var(--font-label);
  font-weight: 400;
}
.hakutulos-ryhma ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.hakutulos-ryhma li {
  padding: var(--space-2) 0;
  /* list-style PITAA asettaa li:lle, ei pelkalle ul:lle (palaute #221 — sama
     juurisyy kuin .hakutulos-ehdotukset li:ssa alla, palaute #120):
     pico.classless `ul li{list-style:square}` kohdistuu suoraan
     listaelementtiin, joten ylla oleva `.hakutulos-ryhma ul{list-style:none}`
     ei estä merkin piirtoa. Sama korjaus kuin .medialista li ja .tag-list li. */
  list-style: none;
}
.hakutulos-ryhma .hakutulos-alias {
  color: var(--color-muted);
  margin-left: var(--space-2);
}

/* Hakusivun ehdotuslohko (tyhja tulos / ei viela hakusanaa). */
.hakutulos-ehdotukset {
  margin-top: var(--space-5);
}
.hakutulos-ehdotukset ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.hakutulos-ehdotukset li {
  padding: var(--space-1) 0;
  /* list-style PITAA asettaa li:lle, ei pelkalle ul:lle (palaute #120):
     pico.classless sisaltaa `ul li{list-style:square}` joka kohdistuu suoraan
     listaelementtiin. Ylla oleva `.hakutulos-ehdotukset ul{list-style:none}`
     voittaa spesifisyydella UL:n tasolla, mutta merkin piirtaa LI — ja sen
     oma saanto voittaa perityn arvon. Siksi DevTools nayttaa UL:lle "none"
     samalla kun kayttaja nakee nelioluodit. Sama korjaus kuin .medialista li
     ja .tag-list li. */
  list-style: none;
}

/* --- tags ---
   Peruslista + inline-variantti. */
ul.tags { display: flex; flex-wrap: wrap; gap: var(--space-1); list-style: none; padding: 0; }
ul.tags li { background: var(--color-surface); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); }
ul.tags.inline li { display: inline; background: none; padding: 0; }

/* --- merkki (badge) ---
   Varipohjaiset variantit. PHP-helper paattaa varin, CSS ei tieda kayttotarkoitusta.
   Perusluokka + varivariantti (esim. .merkki.punainen).
   Aiemmin: .badge, .badge-bias-*, .badge-article-*, .badge-political-* jne. */
.merkki {
  display: inline-flex;
  align-items: center;
  padding: 0.15em 0.6em;
  border-radius: var(--radius-full);
  font-family: var(--font-label);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  background: var(--color-surface);
  color: var(--color-muted);
}
.merkki.punainen   { background: var(--color-bias-high-bg);    color: var(--color-bias-high-text); }
.merkki.keltainen  { background: var(--color-bias-medium-bg);  color: var(--color-bias-medium-text); }
.merkki.vihrea     { background: var(--color-bias-low-bg);     color: var(--color-bias-low-text); }
.merkki.harmaa     { background: var(--color-bias-neutral-bg); color: var(--color-bias-neutral-text); }
.merkki.violetti   { background: var(--color-violetti-bg);     color: var(--color-violetti-text); }
.merkki.sininen    { background: var(--color-primary-bg);      color: var(--color-sininen-text); }
.merkki.tumma      { background: var(--color-primary);    color: var(--color-text-inverse); }
.merkki.tumma:hover { background: var(--color-primary-light); color: var(--color-text-inverse); }
.merkki.kritisoitu { background: var(--color-kritisoitu-bg);   color: var(--color-kritisoitu-text); }
.merkki.puolustettu { background: var(--color-puolustettu-bg); color: var(--color-puolustettu-text); }

/* --- modaali ---
   Geneerinen overlay-dialogi. Glassmorphism sisaltolaatikolle.
   Alpine.js ohjaa x-show:lla, transitiot x-transition:lla. */

.modaali {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 200;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
}
.modaali .tausta {
  position: absolute;
  inset: 0;
  background: var(--color-overlay);
  /* CSS-transitiot: Alpine x-show togglee display, CSS hoitaa faden. */
  transition: opacity 150ms ease;
}
.modaali .sisalto {
  position: relative;
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  overflow-y: auto;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  padding: var(--space-4);
  /* CSS-transitiot: Alpine x-show togglee display, CSS hoitaa faden. */
  transition: opacity 150ms ease, transform 150ms ease;
}
.modaali .sulje {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color var(--transition-fast), background var(--transition-fast);
}
.modaali .sulje:hover {
  color: var(--color-text);
  background: var(--color-surface);
}

/* Size-variantit */
.modaali.sm .sisalto { max-width: 360px; }
.modaali.lg .sisalto { max-width: 640px; }

/* Body scroll lock — Alpine toggle: x-effect="document.body.classList.toggle('modaali-auki', open)" */
body.modaali-auki { overflow: hidden; }

/* Desktop */
@media (min-width: 768px) {
  .modaali .sisalto { padding: var(--space-5); }
}

/* --- kirjautumis-modaali ---
   Domain-komponentti joka wrappaa component('modaali').
   Alpine-ohjattu sahkopostikirjautuminen (magic link).
   Lisatty task #248:ssa deprekoitujen utility-luokkien siirron yhteydessa. */

.kirjautumis-modaali p { font-size: 0.875rem; color: var(--color-muted); }
.kirjautumis-modaali .vaihda-tila { text-align: center; }

/* --- krediitti-modaali ---
   Domain-komponentti kirjautuneen kayttajan krediittien ostoon.
   Lisatty task #248:ssa. Ei omia saantoja: selitetekstit tulevat
   .info-teksti-perussaannosta (osio 6, #790). */

/* --- dialog ---
   Native HTML dialog (kopioi prompti, ilmoita virhe). */

dialog {
    border: none;
    box-shadow: var(--shadow-float);
    padding: var(--space-5);
    max-width: 420px;
    width: 90%;
    background: var(--color-bg);
}
dialog[open] {
    display: block;
}
dialog::backdrop {
    background: var(--color-overlay);
}
.dialog-inner {
    max-width: 420px;
    margin: 0 auto;
}
.dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-5);
}

/* --- dialogi-vaihe ---
   Numeroitu vaihe kaksivaiheisessa dialogissa ("Tarkista itse", task #750).
   Erotin taustavarilla, ei 1px-viivalla (No-Line Rule). */
.dialogi-vaihe {
    background: var(--color-surface-low);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
}
.dialogi-vaihe h4 {
    margin: 0 0 var(--space-1);
    font-family: var(--font-heading);
    font-size: 1rem;
}
.dialogi-vaihe p {
    font-size: 0.875rem;
    color: var(--color-muted);
}
.dialogi-vaihe .prompt-textarea {
    margin-bottom: var(--space-2);
}

/* --- prompt-textarea --- */
.prompt-textarea {
    width: 100%;
    font-size: 0.8rem;
    font-family: monospace;
}

/* --- report-fieldset --- */
.report-fieldset {
    border: none;
    padding: 0;
    margin: 0 0 var(--space-3) 0;
    display: flex;
    flex-direction: column;
}
.report-fieldset label {
    display: block;
    margin-bottom: var(--space-2);
}

/* --- artikkelikortti ---
   Domain-komponentti: yksittaisen artikkelin kortti.
   Navy accent-stripe vasemmassa reunassa, korttipohjainen layout. */

.artikkelikortti {
  display: block;
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-left: var(--accent-stripe) solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  transition: box-shadow var(--transition-base);
}
.artikkelikortti:hover {
  box-shadow: var(--shadow-sm);
}
.artikkelikortti.compact {
  padding: var(--space-2);
}
.artikkelikortti h3 {
  font-size: 1rem;
  margin: 0 0 var(--space-2);
  line-height: var(--line-height-tight);
}
.artikkelikortti h3 a {
  color: var(--color-text);
  text-decoration: none;
}
.artikkelikortti h3 a:hover {
  color: var(--color-primary-light);
}
/* Otsikkoprefix listoissa: "Analyysi artikkelista:" pienempana ja
   muted-varilla omalla rivillaan ennen alkuperaista otsikkoa. Sama
   <a>:n sisalla -> klikki vie analyysiin. */
.artikkelikortti .otsikko-prefix {
  display: block;
  font-size: .75rem;
  font-weight: 400;
  color: var(--color-muted);
  font-family: var(--font-label);
  letter-spacing: 0;
  margin-bottom: var(--space-1);
  text-transform: none;
}
.artikkelikortti .otsikko-paa {
  display: block;
}
.artikkelikortti .meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  font-size: .75rem;
  color: var(--color-muted);
}
.artikkelikortti .meta a { text-decoration: none; } /* meta-linkkien puhdas tyyli (korvaa .link-plain) */
.artikkelikortti .erotin {
  color: var(--color-border);
  font-size: .75rem;
}

/* Koostesivun ingressi otsikon alle (task #394, MediaTitleParser).
   Sub-style: pienempi, muted-vari, line-clamp 2 riviin. */
.artikkelikortti-ingressi {
  font-size: .875rem;
  color: var(--color-muted);
  margin: var(--space-1) 0 var(--space-2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Koostesivun premium-varoitus kortin alaosaan (task #394).
   Pieni teksti, muted-vari. */
.artikkelikortti-varoitus {
  display: block;
  margin-top: var(--space-1);
  font-size: .75rem;
}

/* Bias-badge-varit siirtyivat paavalikoimaan .merkki.punainen/keltainen/vihrea
   (ks. merkki-lohko ~rivi 555). Poistettu 20.4.2026 (task 192). */

/* Desktop */
@media (min-width: 768px) {
  .artikkelikortti { padding: var(--space-4); }
  .artikkelikortti.compact { padding: var(--space-2) var(--space-3); }
}

/* --- artikkelilista ---
   Wrapperi artikkelikorteille. Flex-column + gap. */

.artikkelilista {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* --- ranking-lista (#432 Vaihe 6 — /parhaat-viikko)
   Numeroitu top-N lista artikkelikorteille. <ol> pidetaan saavutettavuuden
   takia (semantiikka: jarjestetty lista). Selain piilotetaan oletusnumerointi
   ja .ranking-numero rendaa numeron visuaalisesti. */

.ranking-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  counter-reset: ranking;
}
.ranking-rivi {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}
.ranking-rivi > .artikkelikortti {
  flex: 1;
  min-width: 0;
}
.ranking-numero {
  flex: 0 0 auto;
  min-width: 2ch;
  font-weight: 700;
  color: var(--color-muted);
  padding-top: var(--space-1);
  text-align: right;
}

/* --- parhaat-viikko-sivu (#432 Vaihe 6) */
.parhaat-viikko-footer {
  margin-top: var(--space-4);
  text-align: center;
}

/* --- discover-sivu ---
   Koostesivun omat tyylit. */

.discover-lahde {
  color: var(--color-muted);
  font-size: .875rem;
}
/* Tuoreusleima yli tuoreusrajan (#792). Vain vari muuttuu — ei uutta
   font-sizea (font_size_max-budjetti, test/css-velka-merkit.json). Vari ei ole
   ainoa signaali: leima saa myos oman lauseensa (tuoreus_vanhentunut). */
.discover-sivu .info-teksti.vanhentunut {
  color: var(--color-warning);
}
.discover-takaisin {
  margin-top: var(--space-4);
}

/* Ankkurihyppy /koosta:n osoitelohkoon (#964, palaute #224). Koostehaun
   tulosten alanosto linkittaa /koosta#koosta-osoitteesta:aan, ja .ylapalkki on
   sticky top:0 → se peittaisi lohkon ylareunan eli juuri sen otsikon joka
   vastaa linkin tekstia. Sama jo kaytossa oleva ratkaisu kuin .hero:ssa
   (r. ~724, #652): --ylapalkki-korkeus asetetaan ajossa ylapalkin todellisesta
   korkeudesta (app.js), fallback 52px matchaa sita. EI uutta tokenia eika
   px-arvoa — vain sama laskukaava kuin naapureilla.

   SAANTO KOHDISTUU LUOKKAAN VAIKKA ANKKURI ON ID. Ne ovat samalla elementilla
   (`<section id="koosta-osoitteesta" class="koosta-osoite-nosto">`), joten
   kohde on sama kummin pain tahansa — mutta ID-valitsin olisi spesifisyydeltaan
   (1,0,0) ja jaisi tiedoston ainoaksi sellaiseksi. Tama tiedosto on kaynyt
   spesifisyyskisansa luokkatasolla (ks. frontend-css.md § Picon lomakesaannot
   ja `.hakukentta.hakukentta` -kaksinnus r. ~1229), ja yksittainen ID-poikkeus
   olisi juuri se rivi jonka seuraava lukija joko kopioi tai joutuu
   selittamaan. `.hero`n ennakkotapaus (r. ~724) on niin ikaan luokka. */
.koosta-osoite-nosto {
  scroll-margin-top: calc(var(--ylapalkki-korkeus, 52px) + var(--space-3));
}

/* --- koosta-historia ---
   Aiempien koostesivu-vierailujen lista (/koosta ilman url-paramia).
   Task #424. */

.koosta-historia-lista {
  list-style: none;
  padding: 0;
  margin: var(--space-3) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.koosta-historia-rivi {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.koosta-historia-rivi:hover {
  border-color: var(--color-primary);
}
.koosta-historia-linkki {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  text-decoration: none;
  flex: 1;
  min-width: 0;
}
.koosta-historia-media {
  font-weight: 600;
}
.koosta-historia-osio {
  font-size: .875rem;
  color: var(--color-muted);
}
.koosta-historia-meta {
  white-space: nowrap;
}
.koosta-historia-tyhja {
  padding: var(--space-4);
  text-align: center;
}
.koosta-historia-esimerkit {
  list-style: none;
  padding: 0;
  margin: var(--space-3) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
}

/* --- koosta-sivutus ---
   Koostesivun sivutuskontrollit (task #474). Edellinen/numerot/Seuraava-nappi-rivi.
   Renderoidaan vain kun extraktori palauttaa pagination-datan. */

.koosta-sivutus {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
  align-items: center;
  margin: var(--space-4) 0 var(--space-3);
}
.koosta-sivutus-nappi {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  text-decoration: none;
  font-size: .9rem;
  min-width: 2rem;
  justify-content: center;
}
.koosta-sivutus-nappi:hover {
  border-color: var(--color-primary);
}
.koosta-sivutus-nappi.aktiivinen {
  background: var(--color-primary);
  color: var(--color-bg);
  border-color: var(--color-primary);
  font-weight: 600;
}
.koosta-sivutus-nappi-disabled {
  opacity: .4;
  cursor: not-allowed;
}
.koosta-sivutus-erotin {
  color: var(--color-muted);
  padding: 0 var(--space-1);
}

/* --- palvelu-osio ---
   Osiovälitys palvelu- ja tilannekatsaussivuilla. */

.palvelu-osio {
  padding: var(--space-5) 0;
}
.palvelu-osio + .palvelu-osio {
  border-top: 1px solid var(--color-border);
}
.palvelu-lead {
  max-width: 640px;
  margin: var(--space-2) auto var(--space-4);
  font-size: 1.05rem;
}
.palvelu-cta {
  text-align: center;
  padding-bottom: var(--space-6);
}

/* --- korttiruudukko ---
   Grid-layout korteille (3 saraketta, mobiilissa 1). */

.korttiruudukko {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: var(--space-3);
}
.korttiruudukko.kolme {
  grid-template-columns: 1fr;
}
/* Kahden kortin ruudukko (#652). Oletus-auto-fill tekisi 1400px:n containerissa
   neljä saraketta, jolloin kaksi korttia jäisi kapeina vasempaan reunaan. */
.korttiruudukko.kaksi {
  grid-template-columns: 1fr;
}
@media (min-width: 769px) {
  .korttiruudukko.kolme { grid-template-columns: repeat(3, 1fr); }
  .korttiruudukko.kaksi { grid-template-columns: repeat(2, 1fr); }
}

/* --- jakaumapalkit ---
   Poliittisen kehystyksen progress-bar -komponentti. */

.jakaumapalkit {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 720px;
  margin: 0 auto;
}
.jakaumapalkki .nimi {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 0.95rem;
  margin-bottom: var(--space-1);
}
.jakaumapalkki .osuus {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--color-muted);
}
.jakaumapalkki .raita {
  position: relative;
  width: 100%;
  height: 10px;
  background: var(--color-surface);
  border-radius: 5px;
  overflow: hidden;
}
.jakaumapalkki .tayte {
  height: 100%;
  background: var(--color-primary);
  border-radius: 5px;
  transition: width 0.3s ease;
}

/* --- aiemmat-paivat ---
   Historiallisten päivien linkkilista. */

.aiemmat-paivat {
  list-style: none;
  padding: 0;
  max-width: 480px;
  margin: 0 auto;
}
.aiemmat-paivat li {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.aiemmat-paivat li:last-child {
  border-bottom: none;
}
.aiemmat-paivat a {
  display: block;
  text-decoration: none;
}

/* --- ilmoitus ---
   Flash-viestit (success, warning, danger). */

.ilmoitus {
  padding: var(--space-3);
  border-radius: var(--radius-md);
  font-size: .875rem;
}
.ilmoitus.success {
  background: var(--color-bias-low-bg);
  color: var(--color-bias-low-text);
}
.ilmoitus.info {
  background: var(--color-primary-subtle);
  border-left: 4px solid var(--color-primary);
  color: var(--color-text);
}

/* Virhe-incident_id (tukikoodi) — pieni harmaa rivi virhealueessa.
   Lähde: spec/backend-virheenkasittely.md (task #329 vaihe 1). */
.virhe-tukikoodi {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-muted);
  font-size: .8125rem;
  font-family: monospace;
}

/* Tuntematon media -ehdotus: typo-suggestion-rivi (#400, 9.5.2026).
   Naytetaan ilmoitus-laatikon (analyze.php) tai .ohjaus-laatikon (alapalkki)
   sisalla "Tarkoititko: hs.fi, yle.fi?" -muodossa. Pieni rivi, hillitty väri,
   linkit alleviivattuna jotta klikattavuus ilmenee. No-BEM: ei __ehdotus,
   suoraan .media-typo-ehdotus joka on yksiselitteinen domain-luokka. */
.media-typo-ehdotus {
  font-size: .8125rem;
  color: var(--color-muted);
  margin-bottom: var(--space-2);
}
.media-typo-ehdotus a { text-decoration: underline; }

/* --- haitari (kehys-komponentti) ---
   Numeroitu kollapsoituva paneeli teaser-rivilla. Pohjapalikka single-uudistuksen
   syvaanalyysiosiolle, yleiskayttoinen. Lahde: spec/komponentit.md, task #439. */
.haitari {
  margin-bottom: var(--space-3);
  background: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
.haitari > summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-body);
  border-radius: var(--radius-md);
  transition: background var(--transition-fast);
}
.haitari > summary::-webkit-details-marker { display: none; }
.haitari > summary::after { display: none; } /* Pico disclosure-kolmio pois — oma .haitari-nuoli hoitaa */
.haitari > summary:hover { background: var(--color-surface-high); }
.haitari > summary:focus-visible {
  outline: 2px solid var(--color-outline);
  outline-offset: 2px;
}
.haitari-numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.75rem;
  height: 1.75rem;
  padding: 0 var(--space-1);
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: var(--color-text-inverse);
  font-family: var(--font-label);
  font-weight: 600;
  flex-shrink: 0;
}
.haitari-otsikko {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  color: var(--color-text);
}
.haitari-teaser {
  /* Mobile-first: teaser omalle taysleveälle rivilleen. Ilman tata
     .haitari-otsikko { flex: 1 } puristaa teaserin pystyyn kapealla naytolla
     (yksi merkki per rivi). order: 1 + flex-basis: 100% wrappaa teaserin
     uudelle riville; numero + otsikko + nuoli jaavat ensimmaiselle riville
     (nuoli oikeaan reunaan otsikon flex: 1 -tyontamana). */
  order: 1;
  flex-basis: 100%;
  color: var(--color-muted);
  font-family: var(--font-label);
}
.haitari-nuoli {
  color: var(--color-muted);
  transition: transform var(--transition-base);
  flex-shrink: 0;
}
.haitari[open] > summary .haitari-nuoli { transform: rotate(180deg); }

/* Desktop: teaser takaisin samalle riville (kuten ennen). */
@media (min-width: 768px) {
  .haitari-teaser {
    order: 0;
    flex-basis: auto;
  }
}
.haitari-sisalto {
  padding: var(--space-3);
  border-top: 1px solid var(--color-border);
  line-height: var(--line-height-base);
}

/* Haitari tiiviin kortin sisalla (task #878, palaute #184: asiantuntijakortin
   lahdelista). Kortti on jo oma pintansa ja padding-tasonsa — haitarin oma
   tausta + varjo + space-3-padding tekisi siita laatikon laatikossa. */
.kortti.compact .haitari {
  background: transparent;
  box-shadow: none;
  margin-bottom: var(--space-2);
}
.kortti.compact .haitari > summary { padding: var(--space-1) 0; }
.kortti.compact .haitari-sisalto { padding: var(--space-2) 0 0; }
/* Teaser kiinni otsikkoon tiiviissa kortissa. Perussaannon `.haitari-otsikko
   { flex: 1 }` on tehty leveille syvaanalyysipaneeleille joissa teaser on oma
   lause ("3 tekniikkaa, 2 vetoamista") ja kuuluu oikeaan reunaan. Tassa teaser
   on pelkka lukumaara ("Lahteet: 12"), joten reunaan tyonnettyna se irtoaa
   otsikostaan — ja mobiilissa perussaannon `flex-basis: 100%` veisi paljaan
   numeron kokonaan omalle rivilleen. Nuoli pysyy oikeassa reunassa
   `margin-right: auto`n ansiosta. Mitattu tuotannosta #878:ssa. */
.kortti.compact .haitari-otsikko { flex: 0 0 auto; }
.kortti.compact .haitari-teaser {
  order: 0;
  flex-basis: auto;
  margin-right: auto;
  padding-left: var(--space-1);
}

/* --- syvaanalyysi-otsikko ---
   Vinouma-tabin Tason 1 (paahavainnot) ja Tason 2 (numeroidut haitarit)
   valinen erotin. Task #440, 23.5.2026 — alustava sijoitus .valilehti-divin
   sisalla. Task #441 siirtaa Tason 2 (haitarit) full-width-laatikkoon
   tabin alapuolelle; silloin .syvaanalyysi-otsikko jaa Taso 2 -laatikon
   alkuun. */
.syvaanalyysi-otsikko {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: 1rem;
  color: var(--color-muted);
  font-family: var(--font-label);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* --- merkki (etusivun trendit) ---
   Tagi-napit trending-osiolle. */

.merkki.aihe {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  font-size: .8125rem;
  font-family: var(--font-label);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
.merkki.aihe:hover,
.merkki.aihe.aktiivinen {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}
.merkki .lkm {
  font-size: .6875rem;
  opacity: 0.7;
}

/* --- tag-list (aiheet-arkistosivu /aiheet, #403) ---
   Aakkosjarjestyksessa ryhmitellyt aihetagit. Reusaa .merkki.aihe -tagimerkit;
   wrapperi hoitaa flex-layoutin ja aakkos-otsikon koko-rivi-katkon. */

.tag-list li { list-style: none; }
.tag-list.alphabetical {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.tag-list.alphabetical li { margin: 0; }
.tag-list.alphabetical .aakkos-otsikko-rivi {
  /* Otsikkorivi kayttaa koko leveyden -- katkaisee flexin uudelle riville */
  flex-basis: 100%;
}
.tag-list.alphabetical .aakkos-otsikko {
  margin: var(--space-4) 0 var(--space-2) 0;
  font-size: 1.25rem;
  scroll-margin-top: var(--space-5); /* sticky-headerin alle scrollatessa */
}
/* plain-variantti (#611): ei-linkittava pillilista (lahteen asiasanat).
   Sama flex-layout kuin alphabetical, ilman aakkos-otsikkorakennetta. */
.tag-list.plain {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.tag-list.plain li { margin: 0; }
/* active-filters-variantti (#676, tiivistetty #791): yhdistelmaaihesivun
   aktiiviset rajaukset. Rivi = YKSI pilli jossa on kaksi klikattavaa
   puoliskoa: nimipuolisko (.merkki.aihe) vie yhden aiheen sivulle, x-puolisko
   pudottaa aiheen joukosta. <li> kantaa yhteisen taustan, reunan ja
   pyoristyksen -> puoliskot luetaan yhdeksi palikaksi. (Teemun selaintesti
   11.8.2026: erilliset pyoristykset + rako tekivat x:sta irrallisen, jolloin
   sen liittyminen tagiin ei hahmottunut.)

   Puoliskot nollataan lapinakyviksi VAIN tassa scopessa -- .merkki.aihe
   -perussaanto on jaettu etusivun trendien, /aiheet-arkiston ja .plain-listan
   kanssa, eika sita saa muuttaa taman nakyman takia.

   Erotin puoliskojen valilla on taustan savyero (--color-surface ->
   --color-surface-low), ei 1px-borderi: tokenilohkon "layered paper"
   -periaate (ks. rivi 8). x:n hover jatkaa samaa portaikkoa
   (--color-surface-mid).

   EI overflow: hidden. Picon fokusrengas on box-shadow
   (:where(a:not([role=button])):focus-visible), jonka overflow: hidden
   leikkaisi kokonaan pois -> puoliskot saavat sen sijaan eksplisiittiset
   kulmapyoristykset :first-child/:last-child -valitsimilla. Kun x puuttuu
   (N=1, ei poisto_href) sama elementti on seka ensimmainen etta viimeinen ->
   yksinainen pilli pyoristyy kaikista kulmista eika jaa puolikkaaksi.

   Kosketuskohde: x sailyy 2.5rem x 2.5rem (=40x40px) vaikka visuaali on
   tiivis; align-items: stretch venyttaa nimipuoliskon samaan korkeuteen. */
.tag-list.active-filters {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.tag-list.active-filters li {
  display: flex;
  align-items: stretch;
  min-height: 2.5rem;
  margin: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
/* Puoliskojen nollaus: tausta, reuna ja pyoristys tulevat <li>:lta. */
.tag-list.active-filters li > * {
  background: transparent;
  border: 0;
  border-radius: 0;
}
.tag-list.active-filters li > :first-child {
  border-top-left-radius: var(--radius-sm);
  border-bottom-left-radius: var(--radius-sm);
}
.tag-list.active-filters li > :last-child {
  border-top-right-radius: var(--radius-sm);
  border-bottom-right-radius: var(--radius-sm);
}
.tag-list.active-filters .poista-rajaus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.5rem;
  min-height: 2.5rem;
  line-height: 1;
  color: var(--color-muted);
  text-decoration: none;
  background: var(--color-surface-low);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.tag-list.active-filters .poista-rajaus:hover,
.tag-list.active-filters .poista-rajaus:focus-visible {
  background: var(--color-surface-mid);
  color: var(--color-text);
}
/* Aliakset-alarivi (#416): canonical-tagin alle "myos: a, b, c". */
.tag-list .aliakset {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-muted);
  font-family: var(--font-label);
  font-size: .6875rem;
  line-height: var(--line-height-tight);
}

/* Harvinaisten tagien piilotus oletuksena (#435).
   .harvinainen-luokka annetaan <li>-elementille jos artikkelimaara < 2
   (PHP: tagit-komponentti, prop $harvinainen_kynnys). .nayta-kaikki -toggle
   listalla paljastaa kaikki. .haun-osuma -luokka voittaa piilotuksen kun
   hakuinput osuu piilotettuun riviin -- nain hauku loytaa harvinaisetkin
   ilman etta toggle pitaa kayttaa. */
.tag-list.alphabetical:not(.nayta-kaikki) .harvinainen:not(.haun-osuma) {
  display: none;
}

/* Harvinaiset-toggle-nappi (#435). */
.harvinaiset-toggle {
  margin-bottom: var(--space-3);
}
.harvinaiset-toggle button {
  background: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3);
  font: inherit;
  cursor: pointer;
  color: var(--color-text);
}
.harvinaiset-toggle button:hover {
  background: var(--color-surface-mid);
}
.harvinaiset-toggle button[aria-pressed="true"] {
  background: var(--color-surface-mid);
}

.aakkos-ankkurit {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-2) 0;
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}
.aakkos-ankkurit a {
  padding: var(--space-1) var(--space-2);
  text-decoration: none;
  font-weight: 600;
}
.aakkos-haku {
  margin-bottom: var(--space-3);
}
.aakkos-haku input {
  width: 100%;
  max-width: 480px;
}

/* Kategoriafasettipalkki (#583) -- /aiheet tag-list view=filter-bar.
   Napit tilaluokalla .aktiivinen (No-BEM). Vaakavieritys mobiililla jos
   napit eivat mahdu; desktopilla kietoutuvat. */
.kategoria-suodatin {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.kategoria-suodatin button {
  background: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  padding: var(--space-1) var(--space-3);
  font: inherit;
  cursor: pointer;
  color: var(--color-text);
}
.kategoria-suodatin button:hover {
  background: var(--color-surface-mid);
}
.kategoria-suodatin button.aktiivinen {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-bg);
}

/* --- klusteri-hero ---
   Ison klusterin korostuskortti etusivulla. */

.klusteri-hero header {
  margin-bottom: var(--space-3);
}
.klusteri-hero h2 {
  margin-top: 0;
  margin-bottom: var(--space-1);
}
.klusteri-hero .meta {
  font-size: .875rem;
  color: var(--color-muted);
}
.klusteri-hero .meta .erotin {
  color: var(--color-border);
  margin: 0 var(--space-1);
}
.klusteri-hero .artikkelit {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-3) 0;
}
.klusteri-hero footer {
  margin-top: var(--space-3);
}

/* --- paivan-pahimmat ---
   Etusivun nostolaatikko: 24h sisalla korkeimmat bias_score -artikkelit.
   Reusaa .kortti + .artikkelit -rakenteet artikkelikortti-listausta varten.
   body.sivu-etusivu lisaa valkoisen taustan + shadow-sm (sama kuin klusterit). */

.paivan-pahimmat header {
  margin-bottom: var(--space-3);
}
.paivan-pahimmat h2 {
  margin-top: 0;
  margin-bottom: var(--space-1);
}
.paivan-pahimmat .meta {
  font-size: .875rem;
  color: var(--color-muted);
  margin: 0;
}
.paivan-pahimmat .meta .erotin {
  color: var(--color-border);
  margin: 0 var(--space-1);
}
.paivan-pahimmat .artikkelit {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
}

/* --- esitetyt-faktat ---
   Artikkelisivun (Vinouma-tab) "Esitetyt faktat" -laatikko (task #236, 7.5.2026).
   Listaa analysis.claimedFacts-rivit + visuaalisen lähde-indikaattorin (.merkki).
   Reusaa .kortti-rakennetta. Värivalinta on informatiivinen, ei tuomitseva —
   keltainen "Ei lähdettä" ei ole varsinainen varoitus. */

.esitetyt-faktat header {
  margin-bottom: var(--space-3);
}
.esitetyt-faktat h3 {
  margin-top: 0;
  margin-bottom: var(--space-1);
}
.esitetyt-faktat .johdanto {
  font-size: .875rem;
  color: var(--color-muted);
  margin: 0;
}
.esitetyt-faktat .lista {
  list-style: none;
  padding: 0;
  margin: var(--space-3) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.esitetyt-faktat .rivi {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.esitetyt-faktat .vaite {
  margin: 0;
  color: var(--color-text);
  line-height: 1.5;
}
.esitetyt-faktat .lahde-tieto {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.esitetyt-faktat .lahde-attribuutio {
  font-style: italic;
  color: var(--color-muted);
  font-size: .875rem;
}
.esitetyt-faktat .cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.esitetyt-faktat .cta .muted {
  font-size: .8rem;
}

/* --- vinouma-palkki ---
   Kolmiosainen progress-bar vinouma-jakaumalle. */

.vinouma-palkki {
  display: flex;
  width: 100%;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--color-surface);
  margin: var(--space-2) 0;
}
.vinouma-palkki .osa {
  height: 100%;
  transition: width 0.3s ease;
}
.vinouma-palkki .osa.matala {
  background: var(--color-bias-low);
}
.vinouma-palkki .osa.kohtalainen {
  background: var(--color-bias-medium);
}
.vinouma-palkki .osa.korkea {
  background: var(--color-bias-high);
}

/* --- laatu-jakauma ---
   Kolmiosainen progress-bar journalistic_score-jakaumalle (composite).
   Sama wrapper-rakenne kuin .vinouma-palkki, KÄÄNTEINEN väriskaala:
   hyva (≥70) = vihreä, keskitaso (40-69) = keltainen, heikko (<40) = punainen.
   D54 Vaihe 1 etusivu (#437, 23.5.2026). */

.laatu-jakauma {
  display: flex;
  width: 100%;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--color-surface);
  margin: var(--space-2) 0;
}
.laatu-jakauma .osa {
  height: 100%;
  transition: width 0.3s ease;
}
.laatu-jakauma .osa.hyva {
  background: var(--color-bias-low);
}
.laatu-jakauma .osa.keskitaso {
  background: var(--color-bias-medium);
}
.laatu-jakauma .osa.heikko {
  background: var(--color-bias-high);
}

/* --- klusteriruudukko ---
   Grid sekundaarisille klusterikorteille. */

.klusteriruudukko {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
@media (min-width: 768px) {
  .klusteriruudukko {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* --- klusterikortti ---
   Pienempi klusterikortti gridissä. */

.klusterikortti header {
  margin-bottom: var(--space-2);
}
.klusterikortti h3 {
  margin-top: 0;
  margin-bottom: var(--space-1);
}
.klusterikortti .meta {
  font-size: .8125rem;
  color: var(--color-muted);
}
.klusterikortti .meta .erotin {
  color: var(--color-border);
  margin: 0 var(--space-1);
}
.klusterikortti .artikkelit {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-2) 0;
}
.klusterikortti footer {
  margin-top: var(--space-2);
}

/* --- sivu-etusivu ---
   Tonal-ryhti: body saa surface-low -taustan (hiukan harmahtava), klusterikortit
   pysyvat valkoisina (--color-bg) -> visuaalinen erotus ilman 1px-borderia
   (no-line-periaate, spec/frontend-css.md periaate 9). Sektioiden valit
   korostuneet hengitystilaa varten. */

body.sivu-etusivu {
  background: var(--color-surface-low);
}
body.sivu-etusivu main {
  padding-top: var(--space-4);
  padding-bottom: var(--space-5);
}
body.sivu-etusivu .klusteri-hero,
body.sivu-etusivu .klusterikortti,
body.sivu-etusivu .paivan-pahimmat {
  background: var(--color-bg);
  border: none;
  box-shadow: var(--shadow-sm);
  padding: var(--space-4);
}
body.sivu-etusivu .vinoutuneet-aiheet a {
  background: var(--color-bg);
  padding: var(--space-3);
  text-decoration: none;
  color: inherit;
}
body.sivu-etusivu .vinoutuneet-aiheet a:hover {
  background: var(--color-surface);
}
body.sivu-etusivu .nain-se-toimii .kortti {
  background: var(--color-bg);
  border: none;
  box-shadow: var(--shadow-sm);
}
/* home.php: klusterikortin "Kaikki artikkelit" -footer-linkki (korvaa .link-plain .text-sm) */
body.sivu-etusivu .klusterikortti footer a,
body.sivu-etusivu .klusterikortti footer .lue-lisaa { font-size: 0.875rem; text-decoration: none; }
/* home.php: osio-intro vinoutuneet-aiheet -sectionissa (tyhja-tila: perussaanto, #790) */
body.sivu-etusivu .osio-intro { font-size: 0.875rem; color: var(--color-muted); }
/* Desktop: isommat vali-spacingit sektioiden valiin. */
@media (min-width: 768px) {
  body.sivu-etusivu .klusteri-hero,
  body.sivu-etusivu .klusterikortti,
  body.sivu-etusivu .paivan-pahimmat {
    padding: var(--space-5);
  }
}

/* --- vinoutuneet-aiheet ---
   Lista aiheista joissa korkea vinouma. */

.vinoutuneet-aiheet {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.vinoutuneet-aiheet a {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  transition: background var(--transition-fast);
}
.vinoutuneet-aiheet a:hover {
  background: var(--color-surface);
}
.vinoutuneet-aiheet .nimi {
  font-weight: 500;
  flex: 1;
}
.vinoutuneet-aiheet .meta {
  font-size: .75rem;
  color: var(--color-muted);
  white-space: nowrap;
}
.vinoutuneet-aiheet .meta .erotin {
  color: var(--color-border);
  margin: 0 var(--space-1);
}

/* --- vinouma-aareva ---
   Kaksi saraketta: vinoutuneimmat vs neutraaleimmat. */

.vinouma-aareva {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
.vinouma-aareva .sarake {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
@media (min-width: 768px) {
  .vinouma-aareva {
    grid-template-columns: 1fr 1fr;
  }
}

/* --- viikon-listat (D54 Vaihe 1 #437, etusivu) ---
   Kaksi saraketta: viikon parhaat (composite) vs vinoutuneimmat (bias).
   Sama mobile-first 1fr → 2× 1fr -kuvio kuin .vinouma-aareva. */

.viikon-listat {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
.viikon-listat .sarake {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
@media (min-width: 768px) {
  .viikon-listat {
    grid-template-columns: 1fr 1fr;
  }
}
/* Viikon laatu-jakauma -alaosio */
.viikon-laatu-jakauma .osio-alaotsikko small {
  font-weight: 400;
  color: var(--color-muted);
  margin-left: var(--space-1);
}

/* --- sivu-analysoi: sivukohtaiset override (analyze.php) ---
   Keskittaa osion otsikon "Viimeksi analysoitu" -rivin. */
body.sivu-analysoi .osion-otsikko { text-align: center; }

/* --- sivu-kirjoittaja + sivu-media: jaetut sivukohtaiset override ---
   Author.php ja media.php jakavat saman sidebar- ja valilehti-rakenteen.
   Sidebarin sisainen text-sm/link-plain -refaktorointi. Tyhja-tilat ja
   .info-teksti tulevat perussaannosta (osio 6, #790). */
body.sivu-kirjoittaja .sidebar-item a,
body.sivu-media .sidebar-item a { text-decoration: none; color: inherit; }
body.sivu-kirjoittaja .sidebar-item span,
body.sivu-media .sidebar-item span { font-size: 0.875rem; color: var(--color-muted); }
/* Drill-downin media-/kirjoittaja-jakauma: ankkuri selitteessa ilman alleviivausta */
body.sivu-kirjoittaja .porautumis-selite a,
body.sivu-media .porautumis-selite a,
body.sivu-aihe .porautumis-selite a,
body.sivu-media .porautumis-lista .rivi a { text-decoration: none; }
body.sivu-media .porautumis-lista .rivi span { font-size: 0.875rem; color: var(--color-muted); }

/* --- sivu-aihe: taulukossa media-linkki (topic.php) ---
   AI-yhteenvedon .info-teksti tuli aiemmin taalta; nyt perussäännöstä (osio 6, #790). */
body.sivu-aihe table a { text-decoration: none; color: inherit; }

/* --- sivu-artikkeli: article.php -erityissaanti (body-luokka jo kaytossa) ---
   Yhteista: meta-rivi, perustelut, asiantuntija-kortit, propagandakuvaukset.
   (Tyhjat tilat: perussääntö osiossa 6, #790.)
   Korvaa 40+ kpl text-sm/muted-text/link-plain/text-caption -yhdistelmia. */
/* Meta-rivin linkit (media, kirjoittaja, versiot, alkuperainen) */
body.sivu-artikkeli .meta a { text-decoration: none; }
body.sivu-artikkeli .meta .alkuperainen-linkki { font-size: 0.875rem; }
body.sivu-artikkeli .versiot .lista a { text-decoration: none; }
/* Artikkeli-esikatselun p */
body.sivu-artikkeli .artikkeli-esikatselu p { font-size: 0.875rem; }
/* Nakokulma-banneri */
body.sivu-artikkeli .nakokulma-kuvaus { font-size: 0.875rem; color: var(--color-muted); }
/* Vinouma-tabin sisaiset tekstit */
body.sivu-artikkeli .pisteet-selite,
body.sivu-artikkeli .kehystys-otsikko,
body.sivu-artikkeli .kehystys-perustelu,
body.sivu-artikkeli .tunne-perustelu,
body.sivu-artikkeli .propaganda-kuvaus,
body.sivu-artikkeli .omaperaisyys-perustelu { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-artikkeli .pisteet-selite,
body.sivu-artikkeli .kehystys-otsikko { font-size: 0.75rem; }
body.sivu-artikkeli .propaganda-rivi .tekniikan-nimi { font-size: 0.875rem; }
/* #878: metatietolohkon maarittelylista on samaa metatekstia kuin tyhjat tilat.
   #790: .tyhja-tila irrotettu talta rivilta — se saa saman koon ja varin
   perussaannosta (osio 6). Deklaraatio jaa dl:lle, joka ei ole .info-teksti
   eika .tyhja-tila (font-size-kynnys, test/css-velka-merkit.json). */
body.sivu-artikkeli .artikkeli-metatiedot dl { font-size: 0.875rem; color: var(--color-muted); }
/* JSN-linkki */
body.sivu-artikkeli .jsn-linkki { font-size: 0.875rem; }
/* Asiantuntijakortit */
body.sivu-artikkeli .asiantuntija-titteli,
body.sivu-artikkeli .asiantuntija-konteksti,
/* #746: tutkimushetki + lahteet ovat metatietoa — sama muted-sävy ja koko kuin
   titteli. Liitetty olemassa olevaan selektorilistaan, koska font-size-kynnys
   (test/css-velka-merkit.json) on lukossa eika uusia deklaraatioita lisata.
   #877: `.asiantuntija-lahteet` -> `.lahteet` — sama lista renderöidään nyt
   myös briiffin alle (`lahteet`-komponentti), joten luokka ei voi olla
   asiantuntijakohtainen. Sääntö pysyy sivukohtaisena overridena eikä siirry
   omaksi lohkokseen: uusi standalone-deklaraatio olisi ollut +1 font-size
   kynnyksen 232 alla (nykytaso 230). */
body.sivu-artikkeli .asiantuntija-tutkittu,
body.sivu-artikkeli .lahteet { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-artikkeli .asiantuntija-osaaminen,
body.sivu-artikkeli .asiantuntija-sidonnaisuudet,
body.sivu-artikkeli .asiantuntija-hakulinkki { font-size: 0.875rem; }
/* Tutkimushetki ja uusintanappi samalla rivilla; nappi kietoutuu alle kapealla. */
body.sivu-artikkeli .asiantuntija-tutkittu { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
/* Lahdelista: ei luettelomerkkeja eika sisennysta — lyhyt metalista, ei sisaltoa. */
body.sivu-artikkeli .lahteet ul { list-style: none; padding-left: 0; margin: var(--space-1) 0 0; }
body.sivu-artikkeli .lahteet li { margin-bottom: var(--space-1); }
/* Aiheesta muualla -haku */
body.sivu-artikkeli .haku-ohje { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-artikkeli .kentta-otsikko { font-size: 0.875rem; font-weight: 600; }
body.sivu-artikkeli .haku-tulos-meta { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-artikkeli .hakutunnisteet .lukumaara { font-size: 0.75rem; color: var(--color-muted); }
/* Pimento-ilmoitus */
body.sivu-artikkeli .pimento-vinkki { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-artikkeli .pimento-kiitos { font-size: 0.875rem; color: var(--color-success); }

/* --- sivu-pimento-tulos: blackout-result.php -erityissaanti ---
   Detaljiteks­tit, tekniset tiedot ja suodatetut hakutulokset (muted + pieni teksti).
   Korvaa 37+ kpl .text-sm.muted-text -yhdistelmia alkuperaisessa naytossa. */
body.sivu-pimento-tulos .alaotsikko,
body.sivu-pimento-tulos .edistyma,
body.sivu-pimento-tulos .virheviesti,
body.sivu-pimento-tulos .lisaselitys,
body.sivu-pimento-tulos .api-haku-rivi,
body.sivu-pimento-tulos .pimento-yhteenveto,
body.sivu-pimento-tulos .haitari > summary .haitari-otsikko,
body.sivu-pimento-tulos .tyokalu-sarake,
body.sivu-pimento-tulos .lahde,
body.sivu-pimento-tulos .haku-arvo { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-pimento-tulos .perustelu { font-size: 0.875rem; }
body.sivu-pimento-tulos .tulos-rivi,
body.sivu-pimento-tulos .haku-otsikko { font-size: 0.875rem; }
body.sivu-pimento-tulos .kattavuusvaje { font-size: 0.875rem; }
body.sivu-pimento-tulos details .kortti .kentan-nimi { color: var(--color-muted); }
/* Edistyma-paketti on block-level, joten sailyy blokkina mutta pienena */
body.sivu-pimento-tulos .edistyma { display: block; }
/* Tehdyt haut -taulukko: pitkat hakutermit rivittyvat, ettei taulukko pakota
   sivua vaakascrolliin kapealla naytolla (#535). overflow-wrap: anywhere
   vaikuttaa myos solun min-content-leveyteen — break-word ei riittaisi
   table-layoutissa. */
body.sivu-pimento-tulos .tehdyt-haut td { overflow-wrap: anywhere; }

/* --- sivu-palvelu: palvelu.php markkinointisivun paketoitu override ---
   5 x .palvelu-osio, kaikki symmetrisia (keskitetty h2, palvelu-lead, kortit).
   Korvaa 14 text-sm + 17 text-center yhdella pakettimaisella override-lohkolla. */
body.sivu-palvelu .palvelu-osio { text-align: center; }
body.sivu-palvelu .palvelu-osio .palvelu-lead { color: var(--color-muted); }
body.sivu-palvelu .palvelu-osio .kortti.compact p { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-palvelu .palvelu-osio .jalkikirjoitus {
  font-size: 0.875rem; color: var(--color-muted); margin-top: var(--space-4);
}

/* --- sivu-tilannekatsaus: palvelu-osioiden keskityksen "paketoitu" override ---
   Sivulla 7 sectionia, kaikki samannakoisia (keskitetty h2, palvelu-lead,
   compact-korttien meta-p). Yksi override paketoi koko sivun esityksen. */
body.sivu-tilannekatsaus .palvelu-osio { text-align: center; }
body.sivu-tilannekatsaus .palvelu-osio .palvelu-lead { color: var(--color-muted); }
body.sivu-tilannekatsaus .palvelu-osio .kortti .palvelu-lead,
body.sivu-tilannekatsaus .palvelu-osio .kortti > p { font-size: 0.875rem; color: var(--color-muted); }
body.sivu-tilannekatsaus .palvelu-osio .kortti .muted-poikkeus { color: var(--color-muted); }

/* author.php + media.php + topic.php analytiikka-teaseri (kirjautumaton tila) */
.analytiikka-teaseri { text-align: center; padding: var(--space-5); }
.analytiikka-teaseri p { color: var(--color-muted); }

/* --- nain-se-toimii ---
   Accordion + step-grid etusivun alaosassa (home.php, analyze.php). */

.nain-se-toimii .ruudukko {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
.nain-se-toimii .numero {
  font-size: 2rem;
  font-weight: 700;
  color: var(--color-primary);
  font-family: var(--font-editorial);
  margin-bottom: var(--space-2);
}
/* Kortti keskitetty + kortin p.muted (korvaa text-center + text-sm muted-text) */
.nain-se-toimii .kortti { text-align: center; }
.nain-se-toimii .kortti p { font-size: 0.875rem; color: var(--color-muted); }
@media (min-width: 768px) {
  .nain-se-toimii .ruudukko {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* --- pimento-kortti ---
   Blackout/pimento-kortin rakenne. */

.pimento-lista {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.pimento-kortti .otsikkorivi {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.pimento-kortti h3 {
  margin: 0;
  font-size: 1.1rem;
}
.pimento-kortti .laskuri {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-muted);
  line-height: 1;
  flex-shrink: 0;
}
.pimento-kortti .laskuri.varoitus {
  color: var(--color-danger);
}
.pimento-kortti .aiheet {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-2);
}
.pimento-kortti .meta {
  margin-bottom: var(--space-2);
}
/* blackouts.php meta/footer/muistiinpanot: korvaa .text-sm.muted-text -utilityt */
.pimento-kortti .meta span,
.pimento-kortti footer span { font-size: 0.875rem; color: var(--color-muted); }
.pimento-kortti footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.pimento-kortti .muistiinpanot {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}
.pimento-kortti .muistiinpanot small { color: var(--color-muted); }

/* blackouts.php tyhja-tila (sivu-pimennot body-luokalla) */
body.sivu-pimennot .tyhja-tila { text-align: center; }

/* blackouts.php .page-header .alaotsikko: korvaa .muted-text */
.page-header .alaotsikko { color: var(--color-muted); }

/* --- ilmiolista (#864 osa B) ---
   Paivan otoksen ilmiorivit /tilannekatsaus-sivulla. Ei korttikehysta: lohko
   on sivun ylaosan LUETTAVA osuus, ja kortti tekisi siita silmailtavan
   ruudukon — juuri sen jota § 12.1 valttaa (numerot eivat kerro, lause
   kertoo). Ei omaa font-sizea: globaalit h3/p riittavat, ja
   css-velka-merkit.json font_size_max on 5 yksikon paassa katosta.
   Variantti tilaluokkana (.ilmiolista.otos), ei BEM. */

.ilmiolista {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.ilmio-nosto h3 {
  margin: 0 0 var(--space-1);
}
.ilmio-nosto p {
  margin: 0 0 var(--space-1);
}
.ilmio-nosto .kuvaus {
  color: var(--color-muted);
}
/* Huomiolause on rivin ydin (§ 12.1): se kertoo MITEN aiheesta uutisoitiin.
   Korostus tehdaan painolla eika koolla — kokoero vaatisi uuden font-sizen. */
.ilmio-nosto .huomiolause {
  font-weight: 600;
}
.ilmio-nosto .maara {
  margin-bottom: 0;
}

/* --- paiva-navigointi (#978 osa C) ---
   Eteen/taakse-siirtyma paivakohtaisten koostesivujen valilla
   (/tilannekatsaus). Ulkoasultaan sukua `.sivutus`elle mutta eri asia: siella
   liikutaan saman tulosjoukon sivujen valilla, tassa vaihdetaan paivaa eli
   koko sivun sisaltoa. Ei omaa font-sizea — peritaan ympariston koko
   (css-velka-merkit.json font_size_max on 5 yksikon paassa katosta). */

.paiva-navigointi {
  display: flex;
  align-items: center;
  /* Nuolet rivin laitoihin: reunapaivalla disabloitu alkio pitaa paikkansa,
     joten jaljella oleva nuoli ei hyppaa paivaa vaihdettaessa. Pico-CSS:n
     `nav { justify-content: space-between }` antaisi saman, mutta se
     kirjoitetaan tahan nakyviin — ilman rivia sailion tyyli olisi Picon
     varassa (vrt. .valintalinkit joka joutuu kumoamaan saman saannon). */
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: var(--space-4) 0;
}
.paiva-navigointi .paiva {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-muted);
  text-decoration: none;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}
.paiva-navigointi a.paiva:hover {
  background: var(--color-surface);
  color: var(--color-text);
}
/* Sama tilaluokka kuin `.sivutus`illa: reunapaiva ei ole klikattava eika
   nayta silta. `pointer-events: none` ei ole tarpeen — alkio on <span>. */
.paiva-navigointi .paiva.disabloitu {
  opacity: 0.4;
}

/* --- vihje ---
   Info-ikoni popoverilla. Kehys-komponentti (domain-riippumaton).
   Kaytto: pieni i-nappi otsikon vieressa, @click avaa selitteen. */

.vihje {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: var(--space-1);
}
.vihje-trigger {
  display: inline-flex;
  align-items: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  opacity: 0.4;
  transition: opacity var(--transition-base);
  color: inherit;
  margin: 0;
}
.vihje-trigger:hover { opacity: 1; }
.vihje-sisalto {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 10;
  background: var(--color-surface-low);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  font-size: .75rem;
  font-weight: 400;
  line-height: 1.5;
  min-width: 200px;
  /* Viewport-clamp (UX-bugifix 11.6.2026): takaa etta popover mahtuu
     nayttoon kapeillakin ruuduilla (<336px). vihje.php:n asemoi() siirtaa
     popoverin translateX:lla viewportin sisaan kun ankkuri (left:0 /
     right:0) tyontaisi sen reunan yli — clamp varmistaa etta siirron
     jalkeen koko sisalto mahtuu 8px marginaalein. Sama clamp koskee kaikkia
     vihje-popovereita, myos raati-varianttia (#647). */
  max-width: min(320px, calc(100vw - 16px));
  box-shadow: var(--shadow-float);
  white-space: normal;
}

/* Kortti/mittarikortti/porautumis-kortti suoran lapsena -> oikea ylakulma
   (ei inline-flow). .kortti saa position: relative alempana kortti-lohkossa,
   .mittarikortti on jo position: relative, .porautumis-kortti saa position
   alempana porautumis-kortti-lohkossa. Inline-kaytto (esim.
   <h3>teksti <vihje/></h3>) ei osu tahan selektoriin, joten virtaus otsikon
   vieressa sailyy. */
.kortti > .vihje,
.mittarikortti > .vihje,
.porautumis-kortti > .vihje {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  margin-left: 0;
  z-index: 2;
}

/* Tooltip asemoituu oikean reunan mukaan kun trigger on oikeassa ylakulmassa,
   muuten popover leikkaantuisi kortin oikeasta reunasta. */
.kortti > .vihje .vihje-sisalto,
.mittarikortti > .vihje .vihje-sisalto,
.porautumis-kortti > .vihje .vihje-sisalto {
  right: 0;
  left: auto;
}

/* Auki-tila (#664, palaute #92): yo. wrapper on position:absolute + z-index:2 =
   OMA PINOUTUMISKONTEKSTI, joten popoverin z-index:10 vaikuttaa vain sen sisalla
   — ulospain koko wrapper on tasolla 2. Kortit itse ovat position:relative ilman
   z-indexia (eivat luo kontekstia), joten kaikkien korttien wrapperit kilpailevat
   samalla tasolla ja DOM-jarjestys ratkaisee: mobiilissa .mittarit on
   2-sarakkeinen ruudukko, jolloin seuraavan kortin i-nappi piirtyi avatun
   popoverin paalle. Nosto tehdaan VAIN auki-tilassa (Alpine sitoo .auki:n samaan
   open-muuttujaan), jolloin suljettuna pinoutuminen sailyy ennallaan.
   Arvo 30: voittaa naapuriwrapperit (2) ja kortin sisaiset kerrokset (.versiot,
   .premium-ilmoitus = 10), mutta jaa selvasti alle kelluvien (analyysijono 50,
   .kontekstikaista 99, .ylapalkki 100, dropdown/turnstile 110, modaali 200).
   Sama nosto .raati-vihje-variantille alempana — molemmat on paivitettava. */
.kortti > .vihje.auki,
.mittarikortti > .vihje.auki,
.porautumis-kortti > .vihje.auki {
  z-index: 30;
}

/* --- raati-vihje ---
   Tuomariraadin pisteet i-ikonista (task #508). Uudelleenkayttaa .vihje-popoverin
   mekaniikkaa ja tyylia: trigger = .vihje-trigger, popover-runko = .vihje-sisalto
   (tausta, border, padding, font-size peritaan). EI rinnakkainen mekaniikka.
   Tama lohko lisaa vain raati-spesifin: wrapper-positiointi + listan rakenne +
   popoverin leveysrajaus (sisalto on rikkaampi kuin pelkka string). */
.raati-vihje {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: var(--space-1);
}
/* Leveysrajaus: .vihje-sisalto on min-width 200 / max-width 320. Raati-popover
   kasvaa sisallon mukaan (lista + selitysteksti), siksi width: max-content.
   Leveysrajaus peritaan .vihje-sisalto:sta — EI omaa clampia (#647, 31.7.2026):
   aiempi calc(100vw - 56px) oletti ankkurin olevan 40px viewportin oikeasta
   reunasta (taysleveä kortti). Mobiilissa .mittarit on 2-sarakkeinen ruudukko,
   jolloin ankkuri on ~186px reunasta eika mikaan clamp-arvo riita. Reunan yli
   valuminen hoidetaan JS-asemoinnilla (vihje_popover_xdata() → asemoi()), joka
   mittaa todellisen sijainnin ja siirtaa popoverin translateX:lla nayttoon. */
.raati-vihje-sisalto {
  width: max-content;
}
.raati-vihje-otsikko {
  display: block;
  margin-bottom: var(--space-1);
}
.raati-aanet {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-2);
}
.raati-aanet li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  line-height: 1.6;
}
.raati-malli { color: var(--color-muted); }
.raati-piste { font-weight: 600; }
.raati-vihje-teksti {
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-muted);
  margin: 0;
  white-space: normal;
}

/* Mittarikortin/kortin suorana lapsena → oikea ylakulma (sama kuin .vihje), popover
   asemoituu oikean reunan mukaan ettei leikkaannu kortista. */
.mittarikortti > .raati-vihje,
.kortti > .raati-vihje {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  margin-left: 0;
  z-index: 2;
}
.mittarikortti > .raati-vihje .vihje-sisalto,
.kortti > .raati-vihje .vihje-sisalto {
  right: 0;
  left: auto;
}

/* Auki-tila (#664) — sama pinoutumiskorjaus kuin .vihje:lla, perustelut siella.
   Tama variantti on mobiilissa se joka rikkoi: .mittarit-ruudukon vierekkaiset
   raati-kortit peittivat toistensa popoverit. */
.mittarikortti > .raati-vihje.auki,
.kortti > .raati-vihje.auki {
  z-index: 30;
}

/* --- artikkeli-tyokalupalkki ---
   Toolbar artikkelisivun ylareunassa, normaalissa flowssa. Domain-komponentti.
   EI sticky (task #549): sticky top:0 tormasi .ylapalkki:n (sticky, z-index 100)
   kanssa -> ylapalkki peitti tyokalupalkin skrollatessa (mobiilipalaute #9).
   Sticky-konteksti hoidetaan .kontekstikaista-nimikaistalla (kontekstikaista.php,
   sivuotsikon stickyPrefix/stickyTitle-propsit) — tyokalupalkin toiminnot (kopioi/ilmoita/
   jaa) eivat tarvitse jatkuvaa saatavuutta skrollin aikana.
   Napit ikoneina mobiilissa (.ikoni-mobiilissa piilottaa .painike-tekstin),
   desktopilla ikoni + teksti vierekkain.
   #878 (palaute #189): `.keski`-alue ja sen saannot poistettu AI-merkinnan
   mukana — se oli olemassa vain merkintaa varten. Palkissa on nyt yksi lapsi
   (.oikea), joka pysyy oikeassa reunassa `margin-left: auto`lla molemmissa
   koossa (nav on flex + space-between, joka yhdella lapsella tasaisi vasemmalle). */

.artikkeli-tyokalupalkki {
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-2) 0;
  /* box-shadow poistettu stickyn mukana (#549): varjo viestii kelluvaa
     kerrosta — normaalissa flowssa se olisi harhaanjohtava. */
}
.artikkeli-tyokalupalkki nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.artikkeli-tyokalupalkki .oikea {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: nowrap;
  margin-left: auto; /* tyontaa oikealle — ainoa lapsi #878:n jalkeen */
}
.artikkeli-tyokalupalkki .oikea .painike {
  padding: var(--space-1) var(--space-2);
  font-size: 0.75rem;
  white-space: nowrap;
  margin-bottom: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
/* Mobiilissa nayta vain ikoni (piilota teksti).
   Desktopilla teksti palautuu @media-queryssa. */
.artikkeli-tyokalupalkki .painike.ikoni-mobiilissa .painike-teksti {
  /* sr-only-tyylinen piilotus saavutettavuus sailyttaen — aria-label
     riittaa screen readereille, mutta pidetaan teksti DOM:ssa
     desktop-lavennusta varten */
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* Dialog-sisallon leipateksti (kopioi prompti, ilmoita virhe) — korvaa .text-sm */
.artikkeli-tyokalupalkki dialog p { font-size: 0.875rem; }
/* Media-merkki linkkina — korvaa .link-plain */
.artikkeli-tyokalupalkki .oikea a.merkki { text-decoration: none; }

/* Desktop: napit teksteilla, isommat gapit ja nappipadding.
   `margin-left: auto` EI nollata (#878): palkissa on vain `.oikea`, joten
   nollaus tasaisi napit vasempaan reunaan. */
@media (min-width: 769px) {
  .artikkeli-tyokalupalkki nav { flex-wrap: nowrap; gap: var(--space-3); }
  .artikkeli-tyokalupalkki .oikea {
    gap: var(--space-2);
  }
  .artikkeli-tyokalupalkki .oikea .painike {
    padding: var(--space-2) var(--space-4);
    font-size: .9375rem;
  }
  /* Desktopilla teksti palautuu nakyviin */
  .artikkeli-tyokalupalkki .painike.ikoni-mobiilissa .painike-teksti {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
  }
}

/* --- artikkeli-asettelu ---
   Kaksipalstainen layout: alkuperainen (vasen) + analyysi (oikea).
   Mobiilissa padding = 0, koska kutsuva sivu on `.container`-kaareessa
   joka hoitaa horisontaalisen marginaalin. Tuplapadding aiheutti
   x-overflowin mediasivulla.
   Suorat lapset saavat `min-width: 0` jotta grid-itemit eivat venyta
   asettelua kun sisalto (taulukot, narratiivit) on leveampaa. */

.artikkeli-asettelu {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0;
}
.artikkeli-asettelu > * { min-width: 0; }
.artikkeli-asettelu .alkuperainen {
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  background: var(--color-surface-low);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.artikkeli-asettelu .analyysi {
  min-width: 0;
}

/* Desktop: kaksipalstainen layout */
@media (min-width: 1025px) {
  .artikkeli-asettelu { grid-template-columns: 1fr 1fr; }
  .artikkeli-asettelu .alkuperainen {
    padding: var(--space-4);
    margin-bottom: 0;
  }
}

/* --- syvaanalyysi ---
   Vinouma-tabin Taso 2 -haitarit full-width-laatikkona .artikkeli-asettelu-gridin
   alapuolella. Sama max-width kuin gridilla (centered .container:in sisalla).
   Nakyvissa vain kun activeTab === 'bias' (Alpine x-show, scope nostettu
   .artikkeli-asettelun ylapuolelle article.php:ssa). Mobiilissa block-elementtina
   pötköön luonnostaan, ei erillista mobiilisaatoa. Task #441. */
.syvaanalyysi {
  max-width: var(--content-max);
  margin: var(--space-5) auto 0;
}

   Above-the-fold -osio: metarivi, otsikko, mittarit, oivallus. */

.artikkeli-nosto {
  margin-bottom: var(--space-5);
}

/* Metarivi: "Yle · Uutinen · 12.4.2026" */
.artikkeli-meta {
  font-family: var(--font-label);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-muted);
  margin-bottom: var(--space-2);
}

/* Otsikko — artikkelisivukohtainen */
body.sivu-artikkeli h1 {
  font-family: var(--font-editorial);
  font-size: 2rem;
  font-weight: 700;
  line-height: var(--line-height-tight);
  color: var(--color-primary);
  letter-spacing: -0.01em;
  margin-bottom: var(--space-4);
}
@media (min-width: 768px) {
  body.sivu-artikkeli h1 {
    font-size: 2.5rem;
  }
}

/* Osio-otsikot (h3) tarvitsevat position:relative tooltip-laukaisimien
   absoluuttista positiointia varten. Korvaa 10 inline-tyyliä partial-tiedostoissa
   (task #457, H3-css-1 23.5.). */
body.sivu-artikkeli h3 { position: relative; }

/* --- mittarit ---
   2x2 grid, desktopissa 4x1. */

.mittarit {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-bottom: var(--space-4);
}
@media (min-width: 768px) {
  .mittarit {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* --- mittarikortti ---
   Yksittainen mittarikortti (vinouma, JSN, tunnelma, kehystys). */

.mittarikortti {
  position: relative;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.mittarikortti .otsikko {
  display: block;
  font-family: var(--font-label);
  font-size: .625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin-bottom: var(--space-1);
}
.mittarikortti .arvo {
  font-family: var(--font-editorial);
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  color: var(--color-primary);
}
/* Mittarikortin pisteet-arvo kayttaa merkki-varivarianttia vain varin saamiseksi —
   resetoi basen padding/radius jotta SVG-rengas nayttaa samalta kuin ennen. */
.mittarikortti .arvo.merkki {
  display: block;
  padding: 0;
  border-radius: 0;
  background: transparent;
  white-space: normal;
}
.mittarikortti .arvo.merkki.punainen  { color: var(--color-bias-high); }
.mittarikortti .arvo.merkki.keltainen { color: var(--color-bias-medium); }
.mittarikortti .arvo.merkki.vihrea    { color: var(--color-bias-low); }
.mittarikortti .arvo .pisteet-rengas { color: inherit; }
.mittarikortti.teksti {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.mittarikortti .teksti-arvo {
  font-family: var(--font-body);
  font-size: .8125rem;
  font-weight: 700;
  color: var(--color-primary);
  line-height: 1.3;
}
.mittarikortti.tyhja {
  opacity: 0.4;
}
.mittarikortti.tyhja .teksti-arvo {
  font-style: italic;
}
/* Kahden nimetyn arvon pari saman kortin sisalla (task #878, palaute #182a:
   Tunnelma + Kehystys yhdistettiin, jotta .mittarit tayttaa gridin tasan).
   Selite ENNEN arvoa: ilman sita lukija ei tieda kumpi luku on kumpi. */
.mittarikortti-pari {
  display: block;
  margin-top: var(--space-1);
}
.mittarikortti-pari .info-teksti {
  display: block;
  line-height: 1.2;
}

/* --- oivallus-kortti ---
   Ydinhavainto-kortti (tumma tausta, accent-raita). */

.oivallus-kortti {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  border-left: var(--accent-stripe) solid var(--color-accent);
}
.oivallus-kortti p {
  margin: 0;
  font-family: var(--font-editorial);
  font-style: italic;
  font-size: 1.0625rem;
  line-height: 1.5;
  color: inherit;
}

/* --- artikkeli-konteksti ---
   Metatiedot + versiot + body preview + disclaimer. */

.artikkeli-konteksti {
  margin-bottom: var(--space-5);
  padding: var(--space-4);
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.artikkeli-konteksti .meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: 0 0 var(--space-3);
  font-size: .875rem;
}
.artikkeli-konteksti .meta a {
  font-size: .875rem;
}

/* --- versiot ---
   Artikkeliversioiden dropdown. */

.versiot {
  position: relative;
}
.versiot .versiot-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  background: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.3em 0.7em;
  font-size: .82rem;
  color: var(--color-muted);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
  line-height: 1.4;
}
.versiot .versiot-toggle:hover {
  background: var(--color-surface-low);
  border-color: var(--color-muted);
}
.versiot .versiot-ikoni {
  font-size: 1.05em;
}
.versiot .nuoli {
  font-size: .75em;
  transition: transform var(--transition-fast);
}
.versiot .nuoli.open {
  transform: rotate(180deg);
}
.versiot .lista {
  list-style: none;
  margin: 2px 0 0;
  padding: 0.3em 0;
  background: var(--color-surface-low);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  min-width: 100%;
  position: absolute;
  z-index: 10;
  box-shadow: var(--shadow-md);
}
.versiot li {
  padding: 0.35em 0.75em;
  font-size: .82rem;
  color: var(--color-muted);
}
.versiot li a {
  color: var(--color-muted);
  text-decoration: none;
  display: block;
}
.versiot li a:hover {
  color: var(--color-text);
}
.versiot li.aktiivinen {
  font-weight: 600;
  color: var(--color-text);
}
.versiot .nykyinen {
  font-weight: 400;
  font-size: .78rem;
  color: var(--color-muted);
}

/* --- artikkeli-esikatselu ---
   Blockquote artikkelin alusta. */

.artikkeli-esikatselu {
  margin: 0 0 var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-low);
  border-left: var(--accent-stripe) solid var(--color-accent);
  border-radius: var(--radius-md);
}
.artikkeli-esikatselu p {
  margin: 0;
  line-height: 1.65;
}

/* --- artikkeli-metatiedot ---
   Sivun pohjan tekninen tietolohko (task #878, palaute #182b): artikkelin
   sisallon lyhennelma + paivaykset. Hillitty: tama on hakuteos, ei sisaltoa
   jota sivulle tullaan lukemaan. Ei omaa font-sizea — muted-koko tulee
   body.sivu-artikkeli -selektoriryhmasta (font-size-kynnys lukossa, 232). */

.artikkeli-metatiedot {
  margin: var(--space-5) 0 var(--space-4);
}
.artikkeli-metatiedot h3 {
  margin-bottom: var(--space-2);
  color: var(--color-muted);
}
.artikkeli-metatiedot dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-1) var(--space-3);
  margin: 0;
}
.artikkeli-metatiedot dt {
  color: var(--color-muted);
}
.artikkeli-metatiedot dd {
  margin: 0;
}

/* --- alkuperainen-linkki ---
   Linkki lahdeartikkeliin artikkelisivun meta-rivilla (task #878, palaute #183).
   Oli aiemmin muuta metaa PIENEMPI tekstilinkki, ja ulkopuolinen lukija ei
   loytanyt mista artikkelin paasee lukemaan. Nyt painikemainen pilleri: erottuu
   meta-rivin muista merkeista ilman etta se kilpailee sivun paa-CTA:n kanssa
   (ei gradienttia, ei accent-varia). */

.alkuperainen-linkki {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-primary-border);
  border-radius: var(--radius-full);
  background: var(--color-primary-bg);
  color: var(--color-primary);
  font-family: var(--font-label);
  font-weight: 700;
  white-space: nowrap;
}
.alkuperainen-linkki:hover {
  background: var(--color-primary);
  color: var(--color-text-inverse);
}

/* --- ai-varoitus ---
   Disclaimer teksti. #878: sisaltaa vain tekstia — sama lahdelinkki on
   korostettuna meta-rivilla (.alkuperainen-linkki), joten `a`-saanto poistettu. */

.ai-varoitus {
  font-size: .8rem;
  color: var(--color-muted);
  margin: 0;
  line-height: 1.4;
}

/* --- ai-merkinta ---
   AI-lapinakyvyysmerkinta (EU AI Act 50(4), task #783). Kolme varianttia:
   .leima  = generoidun tekstin yhteydessa, nakyvin
   .rivi   = aggregaattisivut (media/kirjoittaja/aihe), hillitty
   .tiivis = .merkki-tunnus listoissa ja tyokalupalkissa — EI omia saantoja,
             perii .merkki-luokan kokonaan. Perussaanto on siksi rajattu
             .leima+.rivi-variantteihin: .ai-merkinta-tason display/margin
             ylikirjoittaisi .merkki:n inline-flexin (sama spesifisyys, myohempi
             rivi voittaa) ja rikkoisi tunnuksen.
   Tekstikoot tulevat <small>- ja <strong>-elementeilta (Pico) — ei omia
   font-size-saantoja. */

.ai-merkinta.leima,
.ai-merkinta.rivi {
  color: var(--color-muted);
  line-height: 1.45;
}
.ai-merkinta.leima {
  display: block;
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border-left: var(--accent-stripe) solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-surface-low);
}
.ai-merkinta.leima strong { color: var(--color-text); }
.ai-merkinta.leima p { margin: var(--space-1) 0 0; }
.ai-merkinta-meta,
.ai-merkinta-linkki { display: block; margin-top: var(--space-2); }
/* Ylamarginaali (palaute #159, 14.8.2026): rivi-variantti renderoidaan
   sisallon JALKEEN (media/aihe/kirjoittaja: artikkelilistan alle), joten
   pelkka alamarginaali jatti merkinnan kiinni ylapuoliseen listaan.
   HUOM: (0,2,0) voittaa .mt-*-utilityn (0,1,0) — BriefingService antaa
   riville `mt-md`:n joka jaa nyt vaikutuksettomaksi (1rem -> 2rem). Ei
   rikkoudu: merkinta tarvitsee siella vain ilmaa tekstin ja itsensa valiin. */
.ai-merkinta.rivi { margin: var(--space-5) 0 var(--space-4); }
.ai-merkinta.leima a,
.ai-merkinta.rivi a {
  color: var(--color-muted);
  text-decoration: underline;
}
/* EU:n virallinen AI-merkintakuvake (#879). Yksi saanto kattaa kaikki kolme
   varianttia -- kuvake on osa merkintaa, ei varianttikohtainen koriste.
   flex: none   koska tiivis-variantti on .merkki (inline-flex): ilman tata
                kuvake litistyy ahtaassa sivuotsikon actions-slotissa.
   vertical-align koska leima ja rivi ovat tekstivirtaa (ei flexia), jolloin
                baseline jattaisi kuvakkeen liian ylos.
   Mitta em-yksikoissa: kuvake skaalautuu ymparoivan tekstin mukana (tiivis
   .75rem, leima/rivi perustekstin koko) ilman omaa font-size-saantoa. */
.ai-merkinta .ikoni {
  width: 1.15em;
  height: 1.15em;
  margin-right: 0.35em;
  vertical-align: -0.2em;
  flex: none;
}

/* --- nakokulma-banneri ---
   Artikkelin nakokulma -kortti accent-raidalla. */

.nakokulma-banneri {
  padding: var(--space-3) var(--space-4);
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  border-left: var(--accent-stripe) solid var(--color-primary);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--space-5);
}
.nakokulma-banneri h3 {
  font-size: 1rem;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

/* --- valikot + valilehti ---
   Tab-navigaatio ja -paneelit. Kaytetaan legacy-tyylejä. */

.valikot {
  border-bottom: 2px solid var(--color-border);
  display: flex;
  gap: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.valikot::-webkit-scrollbar { display: none; }
.valikkopainike {
  padding: var(--space-2) var(--space-3);
  border: none;
  background: none;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: .875rem;
  color: var(--color-text);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  white-space: nowrap;
  transition: all var(--transition-fast);
}
.valikkopainike:hover {
  color: var(--color-primary);
  background: var(--color-surface-low);
}

/* Variantti: korostettu valilehti (Teemu 11.8.2026).
   Artikkelisivun "Todellisuustarkistus" on palvelun kiinnostavin ja kallein
   osio, mutta se oli yksi valilehti kahden joukossa eika erottunut mitenkaan.

   ILME LAINATAAN `.painike.ensisij`:LTA (ks. § painike alempana): navy
   gradient + inverse-teksti + shadow-sm hoverissa. Tama on se pari jolla
   sivusto merkitsee ensisijaisen toiminnon kaikkialla muualla, joten
   korostettu valilehti nayttaa sivuston omalta napilta eika uudelta
   keksinnolta. Ensimmainen yritys (amber-taustapilleri, sama paiva) oli
   juuri sellainen keksinto — Teemu: "ei nayta samalta kun muut napit
   sivustolla, kayta samaa tyylia kun joku muu nappi."

   Arvot TOISTETAAN tassa eika jaeta selektoriryhmalla `.painike`-lohkon
   kanssa, koska variantin on pysyttava ENNEN `.valikkopainike`-tilasaantoja:
   `.korostettu` ja `[aria-selected="true"]` ovat molemmat (0,2,0), joten
   tasapelin ratkaisee lahdejarjestys. Selektoriryhma pakottaisi variantin
   `.painike`-lohkoon (~600 rivia alempana) jolloin se voittaisi valitun
   valilehden tilan ja kayttaja ei enaa naisi kumpi on auki. Toisto on tassa
   halvempi kuin vaara kerros — jos kolmas kayttopaikka ilmestyy, tehdaan
   tokeni (`--painike-korostus-bg`, frontend-css.md § Varianttien jakaminen).

   HINTA EI KUULU TAHAN: valilehden vaihto ei veloita mitaan, ja hinnan
   nayttaminen napissa joka ei veloita rikkoisi krediittipainike-sopimuksen
   (#747) toiseen suuntaan. Hinta lukee panelin omassa napissa. */
.valikkopainike.korostettu {
  background: var(--gradient-primary);
  color: var(--color-text-inverse);
  font-weight: 600;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.valikkopainike.korostettu:hover {
  background: var(--gradient-primary);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-sm);
}
/* Valittuna: tilasaanto alla asettaisi `color: var(--color-primary)` eli
   navy-on-navy — teksti katoaisi taustaansa. Korkeampi spesifisyys (0,3,0)
   pitaa tekstin luettavana; alleviivaus (border-bottom-color: accent) tulee
   yha tilasaannosta ja erottaa auki olevan valilehden. */
.valikkopainike.korostettu[aria-selected="true"] {
  color: var(--color-text-inverse);
}
/* Lukittuna (kirjautumaton / ei krediitteja): `.lukittu` alla on kirjoitettu
   LAPINAKYVALLE tekstinapille — se himmentaa tekstin harmaaksi. Taytetylla
   napilla sama saanto tuottaa harmaan tekstin navy-taustalla: mitattu
   rgb(95,99,104) gradientin paalla, kontrasti ~1.5:1 eli lukukelvoton.
   Teksti pidetaan siis inverse-varisena ja lukitus ilmaistaan pelkalla
   himmennyksella (0.7 eika 0.5 — taytetty nappi tummuu enemman kuin
   lapinakyva samalla arvolla). Spesifisyys (0,3,0) > `.lukittu` (0,2,0),
   joten jarjestys ei ratkaise tassa. */
.valikkopainike.korostettu.lukittu {
  color: var(--color-text-inverse);
  opacity: 0.7;
}
.valikkopainike.korostettu.lukittu:hover {
  background: var(--gradient-primary);
  opacity: 0.85;
}

.valikkopainike[aria-selected="true"] {
  color: var(--color-primary);
  border-bottom-color: var(--color-accent);
  font-weight: 600;
}
.valikkopainike.lukittu {
  opacity: 0.5;
  cursor: default;
  color: var(--color-muted);
}
.valikkopainike.lukittu:hover {
  opacity: 0.65;
  background: transparent;
}
@media (min-width: 641px) {
  .valikkopainike { padding: var(--space-2) var(--space-4); font-size: .9375rem; }
}

/* --- premium-ilmoitus --- */

.premium-ilmoitus {
  position: relative;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-top: var(--space-2);
  max-width: 360px;
  box-shadow: var(--shadow-md);
  z-index: 10;
}
.premium-ilmoitus p {
  margin: 0 0 var(--space-2) 0;
  font-size: .9rem;
}

/* --- pisteet-lohko ---
   Iso pisteennaytto (vinouma, JSN). */

.pisteet-lohko {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.pisteet-lohko .arvo {
  font-size: 3rem;
  font-weight: 700;
  font-family: var(--font-body);
  line-height: 1;
}
/* Pisteet-lohkon ison numeron varivariantti — sama pattern kuin .mittarikortti.
   Resetoi .merkki-basen padding/radius/background, jotta numero pysyy isona
   ilman pillerimaista taustaa. Vari tulee bias/jsn-luokasta presenter-tasolla. */
.pisteet-lohko .arvo.merkki {
  display: inline-block;
  padding: 0;
  border-radius: 0;
  background: transparent;
  white-space: normal;
}
.pisteet-lohko .arvo.merkki.punainen  { color: var(--color-bias-high); }
.pisteet-lohko .arvo.merkki.keltainen { color: var(--color-bias-medium); }
.pisteet-lohko .arvo.merkki.vihrea    { color: var(--color-bias-low); }
.pisteet-lohko .otsikko {
  font-size: .875rem;
  color: var(--color-muted);
}

/* --- kehystys ---
   Poliittinen kehystys -lohko analyysivaliohjeessa. */

.kehystys .otsikko {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* --- kortti.kritisoitu / kortti.puolustettu ---
   Entiteettikorttien varivariantit. Otsikkovari kantaa tilan
   semantiikan (kritiikki = punainen, puolustus = vihrea). */

.kortti.kritisoitu { background: var(--color-bias-high-subtle); border-color: var(--color-bias-high-border); }
.kortti.kritisoitu h4 { color: var(--color-danger); }
.kortti.puolustettu { background: var(--color-bias-low-subtle); border-color: var(--color-bias-low-border); }
.kortti.puolustettu h4 { color: var(--color-success); }

/* --- propaganda-rivi ---
   Yksittainen propagandatekniikka-rivi. */

.propaganda-rivi {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-1);
}
.propaganda-rivi.aktiivinen {
  background: var(--color-rivi-aktiivinen-bg);
}
.propaganda-kuvaus {
  padding: 0 var(--space-3) var(--space-2);
}

/* --- vakavuus ---
   Severity badge propagandariville. */

.vakavuus {
  font-size: .65rem;
  padding: 0.1em 0.4em;
  border-radius: var(--radius-full);
  font-weight: 600;
  text-transform: uppercase;
  background: var(--color-surface-mid);
  color: var(--color-muted);
}
.vakavuus.severity-none,
.vakavuus.none {
  background: var(--color-surface-mid);
  color: var(--color-muted);
}
.vakavuus.severity-mild,
.vakavuus.mild {
  background: var(--color-vakavuus-mild-bg);
  color: var(--color-vakavuus-mild-text);
}
.vakavuus.severity-moderate,
.vakavuus.moderate {
  background: var(--color-vakavuus-moderate-bg);
  color: var(--color-vakavuus-moderate-text);
}
.vakavuus.severity-severe,
.vakavuus.severe {
  background: var(--color-vakavuus-severe-bg);
  color: var(--color-vakavuus-severe-text);
}

/* --- mockery-osio ---
   Artikkelisivun "Suora pilkka" -osio (task #392, schema v4, 5.5.2026).
   Listaa analysis.mockery[]-instanssit (sanatarkat pilkkasitaatit). */

.mockery-osio {
  margin-bottom: var(--space-4);
}
.mockery-osio .kuvaus {
  color: var(--color-muted);
  margin: 0 0 var(--space-3);
}
.mockery-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.mockery-rivi {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.mockery-kohde {
  color: var(--color-text);
}
.mockery-sitaatti {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--color-border);
  background: var(--color-surface-mid);
  font-style: italic;
  line-height: 1.5;
}

/* --- otsikkoanalyysi ---
   Artikkelisivun bias-tabin "Otsikkoanalyysi"-osio (task #405, schema v5, 9.5.2026).
   Esittaa otsikon kolme nakokulmaa: factualAlignment (otsikko vs. sisalto),
   clickbaitScore (klikkihoukutus 0-100), polarizationTone (sananvalintojen lataus).
   Yhden silmaytyksen UX: jokainen rivi otsikko + score-merkki + kuvaus. */

.otsikkoanalyysi {
  margin-bottom: var(--space-4);
}
.otsikkoanalyysi-rivi {
  margin-bottom: var(--space-3);
}
.otsikkoanalyysi-rivi strong {
  margin-right: var(--space-2);
}
.otsikkoanalyysi-rivi .merkki {
  vertical-align: baseline;
}
.otsikkoanalyysi-kuvaus {
  margin: var(--space-1) 0 0;
  color: var(--color-muted);
  line-height: var(--line-height-base);
}

/* --- edistymispalkki ---
   Progress bar. (Omaperaisyys-osio siirtyi score-badgeen task #547 —
   ei kayttopaikkoja talla hetkella, sailytetty score view=bar -velkaa
   varten, ks. spec/komponentit.md § score.) */

.edistymispalkki {
  width: 100%;
  height: 8px;
  background: var(--color-surface-mid);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.edistymispalkki .taytto {
  height: 100%;
  background: linear-gradient(90deg, var(--color-primary), var(--color-accent));
  border-radius: var(--radius-full);
  transition: width 0.4s ease;
}
/* --- rivien-valista ---
   Kansalaisen nakokulma: rakenteiset faktat. Scoping-namespace (ei oma kortti):
   sisalto istuu haitari-slotin .haitari-sisalto-kontainerissa, joten oma
   surface-low-tausta + margin poistettu (#11). Ala-selektorit maarittavat
   osiokohtaisen esitystavan (rivi-borderit, uppercase-otsikot). */

.rivien-valista h3 {
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: var(--space-3);
}
.rivien-valista .rivi {
  display: block;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.rivien-valista .rivi:last-child { border-bottom: none; }
.rivien-valista .otsikko {
  display: block;
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin-bottom: var(--space-1);
}
.rivien-valista .arvo {
  display: block;
  font-weight: 400;
  font-size: .9rem;
  color: var(--color-text);
  text-align: left;
  line-height: 1.5;
}
.rivien-valista .yhteenveto {
  font-size: .9rem;
  color: var(--color-muted);
  margin-top: var(--space-3);
  margin-bottom: 0;
  line-height: 1.6;
}
/* v12: strukturoidut havainnot (presupposition/agency/voiceDistribution).
   Kukin havainto omalla rivillaan; sitaatti (.muted) description:n alla. */
.rivien-valista .havainto {
  display: block;
  margin-bottom: var(--space-2);
}
.rivien-valista .havainto:last-child { margin-bottom: 0; }
.rivien-valista .havainto .muted {
  display: block;
  margin-top: var(--space-1);
  font-style: italic;
}
/* v12: aiSpeculation — erottuva lohko + leima. Korostaa että kyseessä on
   tekoälyn tulkinta (accent-reuna surface-hierarkian sijaan, koska tämä on
   semanttisesti eri luonteinen kuin mitatut havainnot). */
.rivien-valista .ai-havainto {
  margin-top: var(--space-4);
  padding: var(--space-3);
  background: var(--color-surface-low);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
}
.rivien-valista .ai-havainto .leima {
  display: inline-block;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-accent);
  margin-bottom: var(--space-1);
}
.rivien-valista .ai-havainto .sisalto {
  color: var(--color-text);
  line-height: 1.6;
  margin: 0 0 var(--space-1) 0;
}
.rivien-valista .ai-havainto .muted {
  margin: 0;
  font-style: italic;
}

/* --- hakutunnisteet ---
   Aiheesta muualla -tabin hakukomponentti. */

.hakutunnisteet {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
.hakutunniste {
  display: inline-flex;
  align-items: center;
  gap: 0.25em;
  padding: 0.3em 0.8em;
  border-radius: var(--radius-full);
  font-size: .8rem;
  font-weight: 500;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-text);
  cursor: pointer;
  transition: all var(--transition-fast);
}
.hakutunniste:hover {
  border-color: var(--color-primary);
  background: var(--color-surface-mid);
}
.hakutunniste.aktiivinen {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}
.hakutunniste.aktiivinen:hover {
  background: var(--color-primary-light);
  border-color: var(--color-primary-light);
}
.hakutunniste.liittyva {
  border-style: dashed;
}
.hakutunniste.liittyva.aktiivinen {
  border-style: solid;
}
.hakutunniste .valinta {
  font-size: .7em;
  margin-left: .2em;
}

/* --- hakusyote ---
   Vapaan hakutermin tekstikentta "Aiheesta muualla" -paneelissa
   (Views/partials/article-osio-aiheesta-muualla.php). Sisarpari on
   .hakuvalitsin alla — samaa paneelia, sama nimeamislinja (haku + elementti).

   Nimetty uudelleen #744:ssa: luokka oli `.hakukentta`, joka tormasi kehyksen
   hakulomake-komponenttiin (#706). Sama nimi tarkoitti seka `<form>`ia etta
   `<input>`ia, joten nama input-tyylit valuivat lomakkeen kaareelle: tausta,
   padding ja border nakyivat lomakkeessa ja veivat ylapalkin kaistan niin
   etta lahetysnappi rivittyi. */

.hakusyote {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: .875rem;
  background: var(--color-bg);
  color: var(--color-text);
  margin-bottom: 0;
}
.hakusyote:focus {
  border-color: var(--color-primary);
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
}

/* --- hakuvalitsin --- */

.hakuvalitsin {
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: .8rem;
  background: var(--color-bg);
  color: var(--color-text);
  margin-bottom: 0;
}

/* --- lahde-ruudukko --- */

.lahde-ruudukko {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-1);
}
.lahde {
  display: flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: .75rem;
  cursor: pointer;
  transition: all var(--transition-fast);
  user-select: none;
}
.lahde input[type="checkbox"] { display: none; }
.lahde:hover {
  border-color: var(--color-primary);
  background: var(--color-surface-mid);
}
.lahde.aktiivinen {
  background: var(--color-surface-low);
  border-color: var(--color-primary);
  color: var(--color-primary);
  font-weight: 600;
}

/* Tabletti: 3 saraketta, desktop: 4 saraketta */
@media (min-width: 641px) {
  .lahde-ruudukko { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1025px) {
  .lahde-ruudukko { grid-template-columns: repeat(4, 1fr); }
}

/* --- hakutulokset --- */

.hakutulokset {
  margin-top: var(--space-3);
}
.hakutulos {
  transition: box-shadow var(--transition-fast);
}
.hakutulos .otsikkolinkki { font-weight: 600; }

/* --- kontekstilinkit / kontekstilinkki ---
   CTA-linkit artikkelisivun alaosassa. */

.kontekstilinkit {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-5);
  margin-bottom: var(--space-4);
}
.kontekstilinkki {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-3);
  border-radius: var(--radius-lg);
  font-weight: 600;
  font-size: .85rem;
  text-decoration: none;
  color: var(--color-primary);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
.kontekstilinkki:hover {
  background: var(--color-surface-high);
  border-color: var(--color-primary);
}
.kontekstilinkki.ensisijainen {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}
.kontekstilinkki.ensisijainen:hover {
  background: var(--color-primary-light);
  color: var(--color-text-inverse);
}
.kontekstilinkki .nuoli {
  font-size: 1.2rem;
  flex-shrink: 0;
  margin-left: var(--space-3);
}
@media (min-width: 769px) {
  .kontekstilinkki { font-size: .9rem; padding: var(--space-3) var(--space-4); }
}

/* --- artikkeli-tagit ---
   Topic-tagit artikkelisivun alaosassa. */

.artikkeli-tagit {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

/* --- lahteen-asiasanat ---
   Lahdemedian omat asiasanat/osastot (scrape_metadata, #611) analyysitagien
   alla. Otsikko on <small>-elementti -- perii .75rem pohjasaannosta (osio 2),
   EI uutta font-size-deklaraatiota (H2-css-2-mittari 229/232). Pillit
   renderoi tagit-komponentin view=plain neutraalilla .merkki-perusluokalla
   (erottuu .merkki.tumma-analyysitageista). */
.lahteen-asiasanat { margin-bottom: var(--space-4); }
.lahteen-asiasanat-otsikko {
  display: block;
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: var(--space-2);
}

/* --- pikakutsu ---
   Mini-esittely / CTA-osio artikkelisivun lopussa. */

.pikakutsu {
  background: var(--color-primary-subtle);
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  border: 2px solid var(--color-primary-border);
  margin-bottom: var(--space-5);
  text-align: center;
}
.pikakutsu h3 {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}
.pikakutsu p {
  font-size: .85rem;
  color: var(--color-muted);
  margin-bottom: var(--space-3);
  line-height: 1.6;
}
.pikakutsu .painike {
  display: inline-block;
  width: 100%;
  max-width: none;
}
@media (min-width: 769px) {
  .pikakutsu .painike { max-width: 300px; }
}

/* --- painike (geneerinen) ---
   Kaytto: .painike, .painike.ensisij, .painike.maksullinen, .painike.hero
   Ylapalkki ylikirjoittaa omaan kontekstiinsa. */

.painike {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  font-size: .9375rem;
  font-family: var(--font-body);
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-text);
  text-decoration: none;
  transition: all var(--transition-fast);
  line-height: 1.4;
}
.painike:hover {
  background: var(--color-surface);
  border-color: var(--color-outline);
}
/* .maksullinen jakaa .ensisij:n sinisen tarkoituksella (Teemun paatos, #747):
   krediittia kuluttava toiminto nayttaa paatoiminnolta, jotta hinta ja
   painoarvo lukevat samassa napissa. Deklaraatiot jaetaan selektoriryhmalla
   eika kopioida — nain varit eivat paase eroon toisistaan.

   Miksi oma semanttinen luokka eika pelkka .ensisij:
     1) grep loytaa yhdella haulla kaikki napit joiden takana on veloitus,
     2) kun maksullisuudelle joskus lisataan oma tunniste (krediitti-ikoni,
        reunus), se lisataan tahan yhteen sarjaan eika 4+ nappiin erikseen.

   Luokka toimii yksinaan: class="painike maksullinen" riittaa, .ensisij ei
   ole ehto. Kokoluokat (.pieni, .xpieni, .w-full, .hero) komposoituvat
   normaalisti — ne saatavat vain mittoja, eivat vareja. */
.painike.ensisij,
.painike.maksullinen {
  background: var(--gradient-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}
.painike.ensisij:hover,
.painike.maksullinen:hover {
  box-shadow: var(--shadow-sm);
}
.painike.hero {
  padding: var(--space-3) var(--space-5);
  font-size: 1.0625rem;
  background: var(--gradient-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
  border-radius: var(--radius-lg);
}
.painike.hero:hover {
  box-shadow: var(--shadow-md);
}

/* --- painike-variantit (ghost, accent, vaara, pieni, xpieni) --- */

.painike.ghost {
  background: transparent;
  color: var(--color-text);
  border-color: var(--color-border);
}
.painike.ghost:hover {
  background: var(--color-surface);
}
.painike.accent {
  background: var(--color-accent);
  color: var(--color-text-inverse);
  border-color: var(--color-accent);
}
.painike.accent:hover {
  background: var(--color-accent-light);
}
.painike.vaara {
  background: var(--color-danger);
  color: var(--color-text-inverse);
  border-color: var(--color-danger);
}
.painike.vaara:hover {
  opacity: 0.9;
}
.painike.pieni {
  font-size: .8rem;
  padding: var(--space-1) var(--space-2);
}
.painike.xpieni {
  font-size: .75rem;
  padding: 0.15rem 0.5rem;
  line-height: 1.4;
}

/* --- ilmoitus-variantit (warning, danger) --- */

.ilmoitus.warning {
  background: var(--color-bias-medium-bg);
  color: var(--color-bias-medium-text);
}
.ilmoitus.danger {
  background: var(--color-bias-high-bg);
  color: var(--color-bias-high-text);
}

/* --- askel-numero ---
   Iso numeroindikaattori (01, 02...) palvelusivun vaihelistassa. */

.askel-numero {
  font-size: 2rem;
  font-weight: 700;
  color: var(--color-primary);
  font-family: var(--font-editorial);
  margin-bottom: var(--space-2);
}

/* --- kortti (geneerinen) ---
   Uudelleennimetty card -> kortti. */

.kortti {
  position: relative;
  display: block;
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
/* Accent-stripe varianttina (kun pitää korostaa kortti). Spec/frontend-css.md
   periaate 3 — variantti pisteen kautta. Aiempi oletus rikkoi etusivun. */
.kortti.korostettu {
  border-left: var(--accent-stripe) solid var(--color-primary);
}
.kortti h2 { margin-top: 0; font-size: 1.125rem; }
.kortti h3 { font-size: 1rem; }
.kortti img { border-radius: var(--radius-sm); }
.kortti .meta { font-size: .875rem; color: var(--color-muted); }
.kortti.fancy { border-color: var(--color-accent); box-shadow: var(--shadow-md); }
.kortti.fancy h2 { color: var(--color-accent); }
.kortti.compact { padding: var(--space-2); }
@media (min-width: 768px) {
  .kortti { padding: var(--space-4); }
}

/* --- profiili ---
   Profiilisivun layout + Osta-kortit grid + iso krediittisaldo. */

.profiili.kapea { max-width: 760px; margin: 0 auto; }

.profiili-saldo {
  font-size: 3rem;
  font-weight: 700;
  font-family: var(--font-editorial);
  color: var(--color-primary);
  line-height: 1;
  margin: var(--space-2) 0;
}
.credit-positive { color: var(--color-success); font-weight: 600; }
.credit-negative { color: var(--color-danger); font-weight: 600; }

/* Osta-kortit: responsiivinen grid — auto-fit, 240px minimum leveys.
   1 col mobiili, 2-3 col desktop riippuen viewport-leveydesta. */
.osta-kortit {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
}

/* Variantti: tiivis — kapeat kontit (krediitti-modaali, sm-modaalin sisältö ~300px).
   Pienempi minimisarake → paketit rinnakkain jo modaalissa, kapeammalla pinoutuvat.
   Korttipinta (focal paper) erottaa paketit modaalin glass-taustasta ilman bordereita. */
.osta-kortit.tiivis {
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--space-3);
}
.osta-kortit.tiivis > div {
  background: var(--color-bg);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
}

/* Profiilisivun lomakepainikkeet: Pico tekee submit-napeista 100% widthin
   form:n ainoana lapsena — overridataan inline-auto. */
.profiili form button.painike,
.profiili form input[type="submit"].painike { width: auto; }

/* --- krediittiloki-taulukko ---
   Profiilisivun kulutushistoria. Mobile-first: rivit taittuvat allekkain
   (label + arvo per kenttä) jotta kapealla ruudulla ei tule vaakascrollia.
   Desktopilla (≥768px) palautuu tabulaariseksi. Sama partial renderöidään
   myös fragment-endpointissa (ProfileController::creditsFragment) — sama
   markup molemmissa, sopimus säilyy. */

/* Mobiili (perustyyli) — lista-näkymä.

   ⚠️ PICO OMISTAA `td,th`:N TAUSTAN JA VIIVAN (palaute #139, 11.8.2026).
   `td,th { background-color: var(--pico-background-color); border-bottom:
   1px solid var(--pico-table-border-color) }` on (0,0,1) mutta se osuu
   jokaiseen soluun, ja lista-näkymässä solu on oma lohkonsa. Lopputulos
   mobiilissa: jokainen SOLU piirtyi valkoisena laatikkona haitarin
   `--color-surface`-taustaa vasten, ja koska oma sääntö kavensi paddingin
   arvoon `var(--space-1) 0`, laatikot näyttivät vaakasuunnassa
   paddingittomilta. Teemun sanoin: "valkoiset laatikot joka entryssä,
   ilman paddingiä".

   Korjaus on Teemun vaihtoehto: pinta siirtyy SOLUSTA RIVILLE, jolloin yksi
   tapahtuma = yksi kortti jolla on oikea padding. Solut ovat läpinäkyviä ja
   viivattomia — rivien erottelu tehdään pintaerolla, ei 1px-viivalla
   (No-Line Rule, frontend-css.md § 9). Desktop-lohko palauttaa tabulaarisen
   ilmeen sellaisenaan. */
.krediittiloki-taulukko { display: block; width: 100%; }
.krediittiloki-taulukko thead { display: none; }
.krediittiloki-taulukko tbody { display: block; }
.krediittiloki-taulukko tr {
  display: block;
  padding: var(--space-3);
  margin-bottom: var(--space-2);
  background: var(--color-bg);
  border-radius: var(--radius-md);
  border-bottom: none;
}
.krediittiloki-taulukko tr:last-child { margin-bottom: 0; }
.krediittiloki-taulukko td {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  background: none;    /* Picon solutausta pois — rivi kantaa pinnan */
  border-bottom: none; /* Picon solukohtainen viiva pois (No-Line) */
  text-align: left;  /* override inline style="text-align:right" mobiilissa */
  /* Pitkät yhtenäiset stringit (esim. Stripe cs_live_… session-ID) rikotaan
     mihin kohtaan tahansa jotta ne eivät työnnä solua yli ruudun mobiilissa.
     `anywhere` sallii rikkomisen myös sanan sisällä — `break-word` ei riitä
     koska Stripe-ID:ssä ei ole sanavälejä. */
  overflow-wrap: anywhere;
}
.krediittiloki-taulukko td::before {
  content: attr(data-otsikko);
  font-weight: 600;
  color: var(--color-muted);
  flex-shrink: 0;
}

/* Desktop — palautuu tabulaariseksi.
   Pinta ja rivinerotin palaavat SOLUUN (kuten Picolla), koska taulukossa
   rivi ei ole kortti vaan rivi. Mobiilin korttitila puretaan eksplisiittisesti
   (tausta, margin, pyöristys) — muuten `display: table-row` perisi ne. */
@media (min-width: 768px) {
  .krediittiloki-taulukko { display: table; }
  .krediittiloki-taulukko thead { display: table-header-group; }
  .krediittiloki-taulukko tbody { display: table-row-group; }
  .krediittiloki-taulukko tr {
    display: table-row;
    padding: 0;
    margin-bottom: 0;
    background: none;
    border-radius: 0;
    border-bottom: none;
  }
  .krediittiloki-taulukko td {
    display: table-cell;
    padding: var(--space-2);
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    text-align: inherit;  /* palauta inline-tyyli (right-align Krediitit) */
  }
  .krediittiloki-taulukko td::before { content: none; }
}

/* --- admin: suppilo-palkki ---
   Konversiosuppilon bar analytics-sivulla. */

.suppilo-palkki-wrap {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-1) 0;
}
.suppilo-palkki {
  height: 1.4rem;
  background: var(--color-primary);
  border-radius: var(--radius-md);
  min-width: 2px;
  transition: width 0.3s ease;
}
.suppilo-palkki-label {
  white-space: nowrap;
  font-size: .85rem;
}
.suppilo-pudotus {
  color: var(--color-muted);
  font-size: .8rem;
  margin-left: var(--space-1);
}

/* --- admin: mod-kortti ---
   Moderointikortit moderation-sivulla. */

.mod-kortti {
  margin-bottom: var(--space-3);
}
.mod-kortti header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.mod-meta {
  font-size: .85rem;
  color: var(--color-muted);
}
.mod-syy {
  margin: var(--space-2) 0;
  padding: var(--space-2);
  background: var(--color-surface);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-md);
}
.mod-toiminnot {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.mod-toiminnot button {
  margin-bottom: 0;
  padding: var(--space-1) var(--space-3);
  font-size: .85rem;
}

/* --- admin: artikkeli-linkit ---
   Käytetään moderation-näkymässä (admin/moderation.php) ja muissa admin-tauluissa
   joissa pitää linkata sisäiseen analyysiin (otsikko/ID) tai alkuperäiseen
   ulkoiseen artikkeliin (URL ↗). #402, 9.5.2026.

   Otsikko leikataan CSS:llä (text-overflow: ellipsis) — ei kovakoodausta PHP:ssä. */

.artikkeli-id {
  color: var(--color-muted);
  font-family: monospace;
  font-size: .85rem;
  margin-right: var(--space-1);
}
a.artikkeli-id {
  text-decoration: none;
}
a.artikkeli-id:hover {
  color: var(--color-primary);
  text-decoration: underline;
}
.artikkeli-otsikko {
  display: inline-block;
  max-width: 40ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
.artikkeli-ulkoinen {
  display: inline-block;
  margin-left: var(--space-1);
  text-decoration: none;
}
.artikkeli-ulkoinen:hover {
  text-decoration: underline;
}

/* --- admin: badge-variantit (moderointi) --- */

.merkki.odottaa {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-odottaa-bg);
  color: var(--color-odottaa-text);
  font-size: .75rem;
  font-weight: 600;
}
.merkki.hyvaksytty {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-hyvaksytty-bg);
  color: var(--color-hyvaksytty-text);
  font-size: .75rem;
  font-weight: 600;
}
.merkki.hylatty {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-hylatty-bg);
  color: var(--color-hylatty-text);
  font-size: .75rem;
  font-weight: 600;
}

/* --- toast ---
   Toast-ilmoitus fixed-positiossa (moderointi, admin). */

.toast {
  position: fixed;
  bottom: var(--space-3);
  right: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  /* Oletustausta: variantiton toast (showToast ilman success/error-luokkaa)
     oli lapinakyva → lukukelvoton paallekkaisteksti kelluvan analyysijonon
     paalla (bugi 10.6.2026). Tumma neutraali + floating-varjo. */
  background: var(--color-text);
  box-shadow: var(--shadow-float);
  color: var(--color-text-inverse);
  font-weight: 600;
  z-index: 1000;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.toast.show { opacity: 1; }
.toast.success { background: var(--color-success); }
.toast.error { background: var(--color-danger); }

/* --- sisaltosivu ---
   Staattisen sisaltosivun kapea tekstipalsta (tietosuoja, kayttoehdot,
   miten-tama-on-tehty, docs/api). Komponentti:
   kehys/Views/components/sisaltosivu.php.

   Korvasi .sisalto-kapea-luokan (#828, palaute #167): se nollasi Picon
   <article>-pystypaddingin (`padding: 0 var(--space-3)`), jolloin h1 liimautui
   kortin ylareunaan ja viimeinen kappale alareunaan — molemmat mitattu 0 px
   tuotannosta. Valkoinen kortti + radius tulevat Picon <article>-saannosta;
   tama lohko antaa ilman ja rajaa rivinpituuden.

   Sijainti osiossa 6 (komponentit) eika 5 (sivurakenne): palikka ei oleta
   aidiltaan muuta kuin leveyden, samoin kuin .kortti. Sama paikka jossa
   .sisalto-kapea oli. */

.sisaltosivu {
  max-width: 720px;
  margin: 0 auto var(--space-5);
  padding: var(--space-4) var(--space-3);
}
/* Otsikkolohko: h1 + paivityspvm, erotus alas ghost-linella (sama kuvio kuin
   .sivuotsikko:lla). Kumoaa Picon `article>header`-saannon (0,0,2), jonka
   sectioning-tausta ja negatiiviset sivumarginaalit on mitoitettu Picon omaan
   paddingiin — ne eivat osu taman lohkon paddingiin vaan tyontaisivat
   otsikkopalkin reunojen yli. Oma selektori (0,1,1) voittaa lahdejarjestyksen
   lisaksi myos spesifisyydella, joten !importantia ei tarvita. */
.sisaltosivu > header {
  margin: 0 0 var(--space-4);
  padding: 0 0 var(--space-3);
  background: none;
  border-radius: 0;
  border-bottom: 1px solid var(--color-border);
}
/* Picon `article>header>*` nollaa lastensa margin-bottomin — palautetaan vali
   otsikon ja pvm-rivin valiin. */
.sisaltosivu > header h1 { margin-bottom: var(--space-2); }
/* Paivityspvm-rivi otsikon alla */
.sisaltosivu .paivamaara { font-size: 0.875rem; color: var(--color-muted); margin-bottom: 0; }

/* Desktop */
@media (min-width: 768px) {
  .sisaltosivu { padding: var(--space-5) var(--space-4); }
}

/* --- artikkeli-suodattimet --- */
.artikkeli-suodattimet { margin-bottom: var(--space-3); }
.artikkeli-suodattimet .suodatinpalkki {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: var(--space-3);
    flex-wrap: wrap;
}
.artikkeli-suodattimet .lajittelu { display: flex; flex-direction: column; gap: var(--space-1); min-width: 160px; }
.artikkeli-suodattimet .lajittelu select { margin-bottom: 0; }
.artikkeli-suodattimet .toiminnot { display: flex; gap: var(--space-1); align-items: flex-end; }
.artikkeli-suodattimet .otsikko {
    font-family: var(--font-label);
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.artikkeli-suodattimet .paneeli {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--space-2);
    padding: var(--space-3);
    background: var(--color-surface-low);
    border-radius: var(--radius-md);
}
.artikkeli-suodattimet .ryhma { display: flex; flex-direction: column; gap: var(--space-1); min-width: 140px; }
.artikkeli-suodattimet .ryhma select { margin-bottom: 0; }
.artikkeli-suodattimet .maara { margin-top: var(--space-1); }
.artikkeli-suodattimet .maara span { font-size: 0.875rem; color: var(--color-muted); } /* tulosten lukumaara */
.artikkeli-suodattimet .aktiivi-piste {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: var(--color-accent);
    margin-left: var(--space-1);
    vertical-align: middle;
}
/* Poliittinen nakokulma -suodatin (task #304 → #313).
   Sijaitsee paneelin sisalla "Suodattimet"-napin takana — sama paikka kuin
   vinouma + aihe (siirretty 30.4. UX-palautteen 29.4. kohta 4 perusteella).
   Kayttaa flex-basis: 100% jotta painikerivi saa oman rivinsa paneelin
   flex-row-layoutissa eika ahtaudu vinouma- ja aihe-ryhmien viereen. */
/* Suhde aiheeseen -suodatin (task #628) jakaa saman rakenteen ja tyylin —
   ryhmitelty selektori, ei rinnakkaista saantoblokkia. */
.artikkeli-suodattimet .paneeli .suodatin-poliittinen,
.artikkeli-suodattimet .paneeli .suodatin-aihesuhde {
    flex-basis: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.artikkeli-suodattimet .paneeli .suodatin-poliittinen .painikerivi,
.artikkeli-suodattimet .paneeli .suodatin-aihesuhde .painikerivi {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}
.poliittinen-painike,
.aihesuhde-painike {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-md);
    border: 1px solid var(--color-outline);
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-body);
    font-size: 0.8rem;
    font-weight: 500;
    cursor: pointer;
    transition: all var(--transition-fast);
}
.poliittinen-painike:hover,
.aihesuhde-painike:hover {
    background: var(--color-surface);
}
.poliittinen-painike.aktiivinen,
.aihesuhde-painike.aktiivinen {
    background: var(--color-surface-low);
    border-color: var(--color-text);
    font-weight: 600;
}
/* Aktiivitilan visuaalinen tunniste — politiikan vari vasemmassa reunassa.
   Kayttaa --color-poliittinen-* tokeneita (sama data = sama vari kuin
   drilldown-kaavioissa ja sidebar-jakaumassa). */
.poliittinen-painike.vasen.aktiivinen {
    border-color: var(--color-poliittinen-vasen);
    border-left: 4px solid var(--color-poliittinen-vasen);
}
.poliittinen-painike.keskus.aktiivinen {
    border-color: var(--color-poliittinen-keskusta);
    border-left: 4px solid var(--color-poliittinen-keskusta);
}
.poliittinen-painike.oikea.aktiivinen {
    border-color: var(--color-poliittinen-oikea);
    border-left: 4px solid var(--color-poliittinen-oikea);
}
.poliittinen-painike:focus-visible,
.aihesuhde-painike:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* --- artikkeli-lista --- */
.artikkeli-lista .lataus,
.artikkeli-lista .virhe {
    text-align: center;
    padding: var(--space-4) 0;
}
.artikkeli-lista .lataus p,
.artikkeli-lista .virhe p { color: var(--color-muted); } /* muted-text korvaus; .tyhja-tila: perussaanto #790 */

/* --- sivutus --- */
/* `:not([hidden])` on speksin vakiintunut kuvio (ks. `.hero-haku > input:not([hidden])`
   ja `frontend-css.md § Picon lomakesaannot`): ilman sita oma `display: flex` kumoaisi
   selaimen `[hidden] { display: none }` -oletuksen, koska tekijan saanto voittaa
   selaimen oletuksen spesifisyydesta riippumatta. `sivutus`-komponentin `jsOnly`-tila
   piilottaa navin `hidden`-attribuutilla ja `sivutus-enhance.js` poistaa attribuutin
   hydratoidessaan — piilotus ei saa vuotaa auki tasta saannosta. Ei `!important`. */
.sivutus:not([hidden]) { display: flex; justify-content: center; align-items: center; gap: var(--space-2); margin: var(--space-5) 0; flex-wrap: wrap; }
.sivutus .sivu {
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    color: var(--color-muted);
    text-decoration: none;
    min-width: 1.75rem;
    font-size: .875rem;
    text-align: center;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.sivutus .sivu:hover { background: var(--color-surface); color: var(--color-text); }
.sivutus .sivu.aktiivi { background: var(--color-primary); color: var(--color-text-inverse); font-weight: 600; }
.sivutus .sivu.disabloitu { opacity: 0.4; pointer-events: none; }
.sivutus .sivu.ellipsis { pointer-events: none; min-width: auto; padding: var(--space-2) var(--space-1); }
.sivutus-info { padding: var(--space-2) var(--space-3); color: var(--color-muted); }
.sivutus button:disabled { opacity: 0.5; cursor: not-allowed; }

/* --- valintalinkit (komponentti `valintalinkit`, kehys) ---
   Nakymatilan valinta linkkeina (esim. hakutulosanalyysin kate #822).
   Sukua `.sivutus`elle ulkoasultaan mutta ei sama asia: sivutus liikkuu
   joukon SISALLA, tama vaihtaa joukon MAARITELMAA. Ei omaa font-sizea —
   peritaan ympariston koko, jolloin komponentti sopii sekä leipatekstin
   etta osio-otsikon viereen ilman variantteja. */
.valintalinkit {
    display: flex;
    align-items: center;
    /* ALA POISTA: kumoaa Pico-CSS:n `nav { justify-content: space-between }`.
       Rivi nayttaa flexin oletusarvolta ja siksi turhalta, mutta ilman sita
       Pico levittaa vaihtoehdot rivin molempiin laitoihin (mitattu tuotannossa
       #822: 1368 px leveassa sailiossa vali oli 1040 px) — kaksi saman
       valinnan vaihtoehtoa lukeutuu silloin erillisiksi kontrolleiksi ja
       `gap` ei paase vaikuttamaan. */
    justify-content: flex-start;
    gap: var(--space-1);
    flex-wrap: wrap;
    margin: var(--space-2) 0 var(--space-4);
}
.valintalinkit .valinta {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    color: var(--color-muted);
    text-decoration: none;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.valintalinkit a.valinta:hover { background: var(--color-surface); color: var(--color-text); }
.valintalinkit .valinta.aktiivinen {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-text-inverse);
    font-weight: 600;
}

/* --- jako-painikkeet (komponentti `jako`, kehys) ---
   Yksi lohko koko sivustolle. `.jako-painikkeet` = view='painikkeet'
   (ikonirivi), pudotus-variantti kayttaa jaettuja .dropdown-tyyleja.
   Ylapalkin `.ylapalkki .jako` -saannot poistettu K1-templatet-6:ssa kun
   ylapalkin kuollut jakopalkki poistui. */
.jako-painikkeet {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.jako-painikkeet .otsikko {
    font-size: 0.8rem;
    color: var(--color-muted);
    font-weight: 500;
}
.jako-painike {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-muted);
    cursor: pointer;
    transition: all var(--transition-fast);
    text-decoration: none;
    padding: 0;
}
.jako-painike:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
    background: var(--color-surface-low);
}
.jako-painike.facebook:hover { color: #1877f2; border-color: #1877f2; }
.jako-painike.x-some:hover { color: var(--color-text); border-color: var(--color-text); }
.jako-painike.linkedin:hover { color: #0a66c2; border-color: #0a66c2; }
.jako-painike.whatsapp:hover { color: #25d366; border-color: #25d366; }
.jako-painike.kopioi:hover { color: var(--color-primary); border-color: var(--color-primary); }

/* jako-painikkeet header-kontekstissa */
.otsikko-jako .jako-painikkeet {
    gap: var(--space-1);
    flex-wrap: nowrap;
}
.otsikko-jako .jako-painikkeet .otsikko { display: none; }
.otsikko-jako .jako-painike {
    width: 28px;
    height: 28px;
}
.otsikko-jako .jako-painike svg {
    width: 14px;
    height: 14px;
}

/* --- erotin (card-meta separator) --- */
.erotin { color: var(--color-border); }

/* --- porautumis-analytiikka ---
   Drilldown-sivujen grid + palkit + kaaviot.
   Käytetään: author, media, topic analytiikkaosiossa. */

.porautumis-ruudukko {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin-top: var(--space-3);
}
.porautumis-kortti {
  position: relative;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
.porautumis-kortti.levea {
  grid-column: 1 / -1;
}
.porautumis-kortti h3 {
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: var(--space-2);
  color: var(--color-text);
}
.porautumis-palkit {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.porautumis-palkkirivi {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.porautumis-selite {
  width: 100px;
  min-width: 100px;
  font-size: 0.85rem;
  text-align: right;
  flex-shrink: 0;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.porautumis-rata {
  flex: 1;
  height: 1.2rem;
  background: var(--color-surface-mid);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.porautumis-taytto {
  height: 100%;
  border-radius: var(--radius-md);
  transition: width .3s ease;
  min-width: 2px;
}
.porautumis-arvo {
  font-size: 0.8rem;
  min-width: 50px;
  text-align: right;
  color: var(--color-muted);
  flex-shrink: 0;
}
/* Etusivun juttutyyppilohko (#1034): selite tarvitsee leveyden, arvo tilan.
   Perussaannon 100px + ellipsis katkaisee "Mielipidekirjoitus"-selitteen, ja
   TAMAN lohkon ainoa tehtava on tyyppien vertailu — katkaistu nimi veisi
   lohkolta sen tehtavan. Arvokentta kantaa kaksi lukua ("69,4 · n 12 001")
   jotka eivat mahdu perussaannon 50px:aan.
   ⚠️ Nama ovat tarkennuksia olemassa olevaan komponenttiin, eivat sen kopioita:
   komponentin omiin saantoihin ei kosketa (11 kayttopaikkaa). Sivuvaikutus:
   selektorin painoarvo (0,2,0) ylittaa 769px-media queryn saannon (0,1,0),
   joten myos desktopilla selite on 130px eika 160px. Ei katkaise mitaan
   selitetta — pidin sen yhtena saantona ettei lohko kasvata CSS-velkaa. */
.juttutyyppien-laatu .porautumis-selite { width: 130px; min-width: 130px; }
.juttutyyppien-laatu .porautumis-arvo { min-width: 92px; }

.porautumis-rata.pinottu {
  display: flex;
  background: none;
}
/* Palkin PAIKALLA nakyva otosvaraus (#971): kun n <= 2, palkkia ei piirreta
   lainkaan (yhden jutun pinottu palkki on aina 100 % yhta varia). Teksti ottaa
   palkin paikan samalla flex-suhteella, jotta oikean reunan lukusarake pysyy
   linjassa muiden rivien kanssa. Vari tulee .otos-varaus-luokalta. */
.porautumis-varaus {
  flex: 1;
}
.porautumis-segmentti {
  height: 100%;
  transition: width .3s ease;
  min-width: 1px;
}
.porautumis-selitteet {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: 0.8rem;
  color: var(--color-muted);
}
.porautumis-selitteet .rivi {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}
.porautumis-piste {
  display: inline-block;
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 2px;
  flex-shrink: 0;
}
/* Värivariantit (#383, 6.5.2026) — korvaa topic.php inline-tyylit:
   style="background:var(--color-poliittinen-vasen)" → .porautumis-piste.vasen */
.porautumis-piste.vasen    { background: var(--color-poliittinen-vasen); }
.porautumis-piste.keskusta { background: var(--color-poliittinen-keskusta); }
.porautumis-piste.oikea    { background: var(--color-poliittinen-oikea); }
.porautumis-lista {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.porautumis-lista .rivi {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
}
.porautumis-lista .rivi:last-child {
  border-bottom: none;
}
.porautumis-monopoli {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.porautumis-monopoli .luku {
  font-size: 2rem;
  font-weight: 700;
  color: var(--color-text);
}
.porautumis-monopoli .trendi {
  font-size: 0.9rem;
  font-weight: 500;
}

/* Aikasarjan vihje (#831, palaute #152) — porautumis-aikasarja-komponentin
   hiiri-/kosketusvihje: mika ajankohta ja arvo piikin tai kuopan takana on.
   Sijainti lasketaan JS:ssa (aikasarja_tooltip_xdata, domain/helpers.php), CSS
   pitaa ulkoasun — Alpinen :style-sidonta osuisi inline-tyylimittariin.
   Fonttikoko peritaan <small>-elementilta (.75rem, osio 2): css-velka-merkit.json
   font_size_max on tasmalleen taynna eika sita saa nostaa. */
.aikasarja-kuvaaja {
  position: relative;
}
.aikasarja-kuvaaja svg:focus-visible {
  outline: 2px solid var(--color-outline);
  outline-offset: 2px;
}
.aikasarja-osuma {
  fill: transparent;
  pointer-events: all;
  cursor: pointer;
}
.aikasarja-korostus {
  fill: var(--color-bg);
  stroke: var(--color-text);
  pointer-events: none;
}
.aikasarja-vihje {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  pointer-events: none;
  white-space: nowrap;
  background: var(--color-surface-low);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-float);
  padding: var(--space-1) var(--space-2);
}
.aikasarja-vihje-otsikko {
  color: var(--color-muted);
}
.aikasarja-vihje-arvo {
  color: var(--color-text);
  font-weight: 600;
}

/* Desktop: 2-sarakkeinen ruudukko + leveammat selitteet */
@media (min-width: 769px) {
  .porautumis-ruudukko { grid-template-columns: repeat(2, 1fr); }
  .porautumis-selite { width: 160px; min-width: 160px; }
}

/* --- poliittinen-suuntaus ---
   Sidebar-kortti: horisontaalinen jakaumapalkki + legenda.
   Värit suomen mediakonvention mukaan: vasen-punainen, keskusta-harmaa, oikea-sininen
   (--color-poliittinen-*). Sama paletti kuin drilldown-kaavioissa — sama data, sama väri. */

.poliittinen-suuntaus {
  display: block;
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-left: var(--accent-stripe) solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}
@media (min-width: 768px) {
  .poliittinen-suuntaus { padding: var(--space-4); }
}
.poliittinen-suuntaus .palkki {
  display: flex;
  width: 100%;
  height: 12px;
  border-radius: var(--radius-full);
  overflow: hidden;
  margin: var(--space-2) 0;
  background: var(--color-muted);
}
.poliittinen-suuntaus .segmentti {
  height: 100%;
  transition: width var(--transition-base);
}
.poliittinen-suuntaus .segmentti.vasen   { background: var(--color-poliittinen-vasen); }
.poliittinen-suuntaus .segmentti.keskusta { background: var(--color-poliittinen-keskusta); }
.poliittinen-suuntaus .segmentti.oikea   { background: var(--color-poliittinen-oikea); }
.poliittinen-suuntaus .segmentti + .segmentti {
  box-shadow: inset 2px 0 0 var(--color-bg);
}
.poliittinen-suuntaus .selitteet {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: var(--space-2);
}
.poliittinen-suuntaus .selitteet .rivi {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.8rem;
}
.poliittinen-suuntaus .piste {
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}
.poliittinen-suuntaus .piste.vasen   { background: var(--color-poliittinen-vasen); }
.poliittinen-suuntaus .piste.keskusta { background: var(--color-poliittinen-keskusta); }
.poliittinen-suuntaus .piste.oikea   { background: var(--color-poliittinen-oikea); }
.poliittinen-suuntaus .nimi {
  flex: 1;
  color: var(--color-text);
}
.poliittinen-suuntaus .arvo {
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}
.poliittinen-suuntaus .yhteensa {
  font-size: 0.75rem;
  margin-top: var(--space-1);
  margin-bottom: 0;
}

/* Poliittinen kehystys — kompakti palkki (nakyma='palkki', task #560).
   Itsenainen ohut palkki kuten .vinouma-palkki; samat --color-poliittinen-*
   tokenit kuin .poliittinen-suuntaus .segmentti (ei uusia vareja). */
.poliittinen-suuntaus-palkki {
  display: flex;
  width: 100%;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--color-surface);
  margin: var(--space-2) 0;
}
.poliittinen-suuntaus-palkki .segmentti {
  height: 100%;
  transition: width 0.3s ease;
}
.poliittinen-suuntaus-palkki .segmentti.vasen    { background: var(--color-poliittinen-vasen); }
.poliittinen-suuntaus-palkki .segmentti.keskusta { background: var(--color-poliittinen-keskusta); }
.poliittinen-suuntaus-palkki .segmentti.oikea    { background: var(--color-poliittinen-oikea); }

/* --- entiteetti-tilasto ---
   Tilastolukukortti entity-sivuilla (author, media, topic). */

.entiteetti-tilasto { text-align: center; }
.entiteetti-tilasto .arvo { font-size: 1.5rem; font-weight: 700; }
.entiteetti-tilasto .nimi { font-size: .75rem; color: var(--color-muted); }
/* Otoskoko (#952): labelin jatke SAMALLA rivilla, ei omaa rivia — kortissa on
   jo vertailurivi ja kolmas pieni rivi ison luvun alla olisi tehnyt kortista
   listan. Erotin " · " tulee markupista (sama syy kuin .vertailulla alla).
   Ei omaa font-sizea: .nimi on jo .75rem ja app.css:n font-size-kynnys on
   232 (test/css-velka-merkit.json) — opacity riittaa erottamaan otoksen.
   Himmennys ja kursiivi ovat LISA, eivat signaali: varaus ("suuntaa antava")
   on itse tekstissa, koska ruudunlukija ei nae kursiivia. */
.entiteetti-tilasto .nimi .otos { opacity: .8; }
.entiteetti-tilasto .nimi .otos.himmea { font-style: italic; }
/* Vertailuarvo (#884, palaute #203): "vrt. 33,7  9 mediaa" labelin alla.
   Mykistetympi kuin .nimi jotta paaluku sailyy kortin painopisteena.
   Ei omaa font-sizea .joukko-osalle (perii .vertailun) — app.css:n
   font-size-kynnys on 232 (test/css-velka-merkit.json) ja opacity riittaa. */
.entiteetti-tilasto .vertailu {
  margin-top: var(--space-1);
  font-size: .7rem;
  color: var(--color-muted);
}
/* Ei margin-leftia: vali tulee markupin " · "-erottimesta (#884-korjaus).
   Marginaali yksin jatti textContentin muotoon "vrt. 33,79 mediaa" — kaksi
   lukua liimautui yhteen ruudunlukijalle ja kopioidussa tekstissa. */
.entiteetti-tilasto .vertailu .joukko { opacity: .75; }

/* --- otos-varaus ---
   Otoskoon varaus taulukkorivilla (#952). Aihesivun mediakohtaisessa
   taulukossa vinoumaluvun perassa: "66,0 · yksi juttu". Erillinen luokka eika
   .entiteetti-tilaston .otos, koska konteksti on eri (taulukkosolu ilman
   .nimi-emoa) — jaettu perussaanto olisi vaatinut kumoamisen molemmissa.
   Erotin on markupissa, ei marginaalissa. */
.otos-varaus { color: var(--color-muted); opacity: .85; }
.otos-varaus.himmea { font-style: italic; }

/* --- tunnistautuminen ---
   Auth-sivujen layout ja kortti (login, verify, consent). */

.tunnistautuminen { background: var(--color-bg); min-height: 100vh; }
.tunnistautuminen main {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--space-4);
}
.tunnistautuminen-kortti {
  max-width: 420px;
  width: 100%;
  text-align: center;
  padding: var(--space-5) var(--space-4);
}
.tunnistautuminen-kortti .logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  margin-bottom: 2rem;
}
.tunnistautuminen-kortti .logo img {
  width: 48px;
  height: 48px;
  border-radius: 8px;
}
.tunnistautuminen-kortti .logo strong {
  font-family: var(--font-body);
  font-size: 1.25rem;
  color: var(--color-text);
}
.tunnistautuminen-kortti .kuvaus {
  color: var(--color-muted);
  margin-bottom: var(--space-4);
}
.tunnistautuminen-kortti h2 {
  margin-bottom: var(--space-2);
}
.tunnistautuminen-kortti fieldset {
  text-align: left;
}
.tunnistautuminen-kortti fieldset label {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.tunnistautuminen-kortti fieldset label input[type="checkbox"] {
  flex-shrink: 0;
  margin-top: 0.2em;
}
.tunnistautuminen-kortti .ominaisuudet {
  list-style: none;
  padding: 0;
  margin: 0 0 2rem 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.tunnistautuminen-kortti .ominaisuudet li {
  font-size: .875rem;
  color: var(--color-muted);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.tunnistautuminen-kortti .ominaisuudet li::before {
  content: "\2713";
  color: var(--color-success);
  font-weight: 700;
  flex-shrink: 0;
}
.tunnistautuminen-kortti .painike {
  width: 100%;
}
.tunnistautuminen-kortti .alapalkki {
  margin-top: var(--space-5);
  font-size: .75rem;
  color: var(--color-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  border-top: none;
  padding: 0;
}
.tunnistautuminen-kortti .alapalkki a {
  color: var(--color-muted);
  text-decoration: underline;
}
.tunnistautuminen-kortti .alapalkki a:hover {
  color: var(--color-text);
}

/* --- ehtomuutokset (domain-komponentti, #748) ---
   Lyhyt tiivistelma siita mika kayttoehdoissa / tietosuojaselosteessa muuttui.
   Naytetaan consent-ruudussa kun kayttajalta pyydetaan uusi hyvaksynta.
   Vasen tasaus koska tunnistautuminen-kortti on keskitetty — luettava kappale ei ole. */

.ehtomuutokset {
  text-align: left;
  background: var(--color-surface-low);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.ehtomuutokset .muutos h3 {
  margin: 0 0 var(--space-1) 0;
}
.ehtomuutokset .muutos p {
  margin: 0 0 var(--space-1) 0;
  color: var(--color-muted);
}
.ehtomuutokset .muutos a {
  color: var(--color-muted);
  text-decoration: underline;
}
.ehtomuutokset .muutos a:hover {
  color: var(--color-text);
}

/* --- pisteet-teksti ---
   SVG text-elementti score-ring -komponentissa. */

.pisteet-teksti {
  font-family: var(--font-editorial);
  font-size: .625rem;
  font-weight: 700;
  fill: currentColor;
}

/* --- analyysijono ---
   Kelluva bottom-right UI: URL-syotto + jonon tilanne.
   Spec-mukainen: ei BEM, sisaiset elementit selektoreilla, variantit pisteella. */

.analyysijono {
  position: fixed;
  /* Mobiilissa selainten dynaamiset alapalkit peittavat komponentin alaosaa.
     Android Chrome palauttaa env(safe-area-inset-bottom) = 0 vaikka alapalkki
     peittaa nakymaa (palaute #47) — pelkka env()-summa ei siis riita, minimi
     on var(--space-5) = 2rem. iOS Safari: env ~34px → bottom ~58px, OK. */
  bottom: max(env(safe-area-inset-bottom, 0px) + var(--space-4), var(--space-5));
  right: var(--space-2);
  z-index: 50;
  width: calc(100vw - 1rem);
  max-width: calc(100vw - 1rem);
  font-size: 0.875rem;
  font-family: var(--font-label);
}
@media (min-width: 481px) {
  .analyysijono {
    bottom: var(--space-4);
    right: var(--space-4);
    width: 340px;
    max-width: calc(100vw - 2rem);
  }
}
.analyysijono.vilkkuu { animation: jono-vilkku 0.6s ease-out; }
@keyframes jono-vilkku {
  0%, 100% { box-shadow: none; }
  50% { box-shadow: 0 0 0 4px var(--color-accent); border-radius: var(--radius-md); }
}

.analyysijono .otsikko {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  margin-bottom: 0;
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 600;
  transition: background var(--transition-fast);
}
.analyysijono .otsikko:hover { background: var(--color-primary-light); }
.analyysijono .otsikko .tila {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.analyysijono .otsikko .nuoli {
  font-size: 0.75rem;
  opacity: 0.7;
}

.analyysijono .runko {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-top: none;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  padding: var(--space-2);
  max-height: 320px;
  overflow-y: auto;
  box-shadow: var(--shadow-float);
}
.analyysijono form {
  display: flex;
  gap: var(--space-1);
  margin-bottom: var(--space-2);
}
/* Syottokentta: tyyppiagnostinen selektori tarkoituksella (#656).
   Kentta oli type="text" + inputmode="url" jotta skeematon syote ("yle.fi")
   paasee palvelimen normalisoitavaksi; input[type="url"]-sidonta putoaisi
   pois heti kun tyyppi vaihtuu. Lohkossa on vain yksi input.
   #674 palautti kentan type="url":ksi (mobiilin liitaehdotus) ja siirsi
   skeemattoman syotteen sallimisen lomakkeen novalidate-attribuutille.
   Juuri tyyppiagnostisuus teki tyypin vaihdosta turvallisen — tyylit
   osuvat edelleen. Ala sido selektoria tyyppiin. */
.analyysijono form input {
  flex: 1;
  padding: var(--space-2) var(--space-2);
  font-size: 0.875rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  margin: 0;
}
.analyysijono form input:focus {
  outline: 2px solid var(--color-outline);
  outline-offset: 1px;
}
.analyysijono .lisaa {
  padding: 0.25rem 0.625rem;
  margin-bottom: 0;
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1;
  background: var(--color-accent);
  color: var(--color-text-inverse);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  width: auto;
  min-width: 0;
  flex: 0 0 auto;
}
.analyysijono .lisaa:hover { background: var(--color-accent-light); }
.analyysijono .virhe {
  color: var(--color-danger);
  font-size: 0.75rem;
  margin-bottom: var(--space-2);
  padding: 0 var(--space-1);
}
/* Positiivinen ohjausviesti (.virhe:n rinnakkainen, neutraali ei-punainen).
   Esim. media-URL → koostesivulle (discover-redirect). Sama kompakti koko kuin
   .virhe — mahtuu analyysijonon ahtaaseen UI:hin. Sävy = primary-subtle
   (sama kuin .ilmoitus.info, koherenssi info-laatikoiden kanssa). */
.analyysijono .ohjaus {
  color: var(--color-text);
  background: var(--color-primary-subtle);
  border-left: 3px solid var(--color-primary);
  font-size: 0.75rem;
  margin-bottom: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

.analyysijono .tyo {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-muted);
}
.analyysijono .tyo:last-child { border-bottom: none; }
.analyysijono .tyo .ikoni {
  flex-shrink: 0;
  width: 1.25rem;
  text-align: center;
}
.analyysijono .tyo .ikoni.valmis { color: var(--color-success); }
.analyysijono .tyo .ikoni.epaonnistunut { color: var(--color-danger); }
.analyysijono .tyo .info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.analyysijono .tyo .nimi {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.8125rem;
}
.analyysijono .tyo .tila {
  font-size: 0.75rem;
  color: var(--color-muted);
}
.analyysijono .tyo .aika-arvio {
  display: block;
  font-size: 0.75rem;
  color: var(--color-muted);
  margin-top: 2px;
}
.analyysijono .tyo .toiminnot {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}
.analyysijono .tyo .sulje {
  opacity: 0.5;
  font-size: 1rem;
  padding: 0 var(--space-1);
}
.analyysijono .tyo .sulje:hover { opacity: 1; }

/* #958: kertoo montako jonon tyota jaa naytetyn listan ulkopuolelle.
   Ei omaa font-sizea tarkoituksella — font_size_max-kynnys on 232 ja taso 231
   (css-velka-merkit.json: "EI NOSTETA enaa"), ja lohkon oma 0.875rem riittaa.
   Vaimennus tehdaan varilla, ei koolla. */
.analyysijono .jono-otos {
  margin: var(--space-1) 0 0;
  color: var(--color-muted);
}

.analyysijono .tyhjenna {
  display: block;
  width: 100%;
  text-align: center;
  margin-top: var(--space-1);
  font-size: 0.75rem;
  color: var(--color-muted);
}

/* --- analyysijono progress-bar ---
   Task #312 (paketti B 2c): processing/pending-jobien arvio.
   Inline-tyyli sallittu vain --edistyma-CSS-custom-propertyna (Type-C, frontend-css.md). */
.analyysijono .tyo .edistyma-palkki {
  height: 3px;
  background: var(--color-surface-mid);
  border-radius: var(--radius-sm);
  margin-top: 2px;
  overflow: hidden;
}
.analyysijono .tyo .edistyma-palkki .osa {
  background: var(--color-accent);
  height: 100%;
  width: var(--edistyma, 0%);
  transition: width 0.3s ease;
}

.spinner.pieni {
  width: 0.875rem;
  height: 0.875rem;
  border-width: 1.5px;
}
.spinner.spinner-keskitetty {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

/* --- skeleton ---
   Latausanimaatio. Perustila = gradient-shimmer. Text-muunnos = rivipalikka.
   Block/line = artikkelisivun isommat placeholderit. */
.skeleton {
  background: linear-gradient(90deg, var(--color-surface-mid) 25%, var(--color-skeleton) 50%, var(--color-surface-mid) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.5s infinite;
  border-radius: var(--radius-md);
}
@keyframes skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.skeleton-text { height: 1em; margin-bottom: var(--space-2); }
/* Width-variantit (#383, 6.5.2026) — sama nimimaailma kuin .skeleton-line.* */
.skeleton-text.lyhyt { width: 40%; }
.skeleton-text.keski { width: 60%; }
.skeleton-text.pitka { width: 90%; }
.skeleton-text.tayte { width: 100%; }
.skeleton-block { padding: var(--space-3) 0; }
.skeleton-line {
  background: var(--color-surface-mid);
  border-radius: var(--radius-sm);
  height: 0.9rem;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
}
/* Width-variantit (#383, 6.5.2026) — staattiset placeholder-leveydet, korvaavat
   inline width:X% -tyylit. Vapaat leveydet (esim. 45%, 65%, 85%) tehdään
   yhdistelmillä lyhyt+keski+pitka kun pixel-tarkkuus ei ole tärkeää. */
.skeleton-line.lyhyt  { width: 40%; }
.skeleton-line.keski  { width: 60%; }
.skeleton-line.pitka  { width: 90%; }
.skeleton-line.tayte  { width: 100%; }
/* Otsikkorivin korkeusvariantti — käytetään ennen leipäteksti-skeletoneita. */
.skeleton-line.otsikko    { height: 1.5rem; }
.skeleton-line.alaotsikko { height: 1.2rem; }
@keyframes skeleton-pulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.8; }
}

/* --- spinner ---
   Pyoriva latausindikaattori. */
.spinner {
  display: inline-block;
  width: 1.5rem;
  height: 1.5rem;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* --- latausindikaattori ---
   Spinneri ja selite samalla rivilla (#810, palaute #135). Komponentti:
   kehys/Views/components/latausindikaattori.php. Selite on muted koska se on
   valiaikainen tilateksti, ei sisaltoa. Ei omaa font-sizea: koko peritaan
   kontekstista, jolloin sama komponentti toimii sivun leipatekstissa ja
   napissa ilman variantteja. */
.latausindikaattori {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-muted);
}

/* --- tekstikorostukset (propagandatekniikat) ---
   mark-elementti data-type-attribuutilla — visuaalinen osoitus
   analyysin loytamista framing/loaded/omission-osioista. */
mark[data-type="framing"] {
  background: var(--color-highlight-framing);
  padding: 0.05em 0.2em;
  border-radius: 2px;
}
mark[data-type="loaded-language"] {
  background: var(--color-highlight-loaded);
  padding: 0.05em 0.2em;
  border-radius: 2px;
}
mark[data-type="omission"] {
  background: var(--color-highlight-omission);
  padding: 0.05em 0.2em;
  border-radius: 2px;
}

/* --- toolbar + dropdown ---
   Geneerinen toolbar-rivi + pudotusvalikko (artikkeli-tyokalupalkki kaavio).
   Englanninkielinen nimeamien sailytetty taman taskin scopen ulkopuolella. */
.toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.dropdown { position: relative; display: inline-block; }
.dropdown-menu {
  position: absolute;
  right: 0;
  top: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-float);
  padding: var(--space-1) 0;
  min-width: 180px;
  z-index: 50;
}
.dropdown-item {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  text-align: left;
  border: none;
  background: none;
  font-size: 0.875rem;
  cursor: pointer;
  color: var(--color-text);
  text-decoration: none;
}
.dropdown-item:hover { background: var(--color-surface-mid); }

/* --- neutral-content + briiffi-sisalto (LLM:n tuottama sanitoitu proosa) ---
   Kaksi nimea, yhdet deklaraatiot. Neutraali versio ja kansalaisbriiffi (#779)
   renderoivat samanlaista markdown-proosaa, joten tyylit jaetaan
   SELEKTORIRYHMALLA eika kopiona (sama malli kuin `.painike.ensisij,
   .painike.maksullinen`, #747) — kopio ajautuisi erilleen ensimmaisessa
   muutoksessa. Oma luokka briiffille silti siksi, etta `grep .briiffi-sisalto`
   loytaa sen kayttopaikat ja mahdollinen briiffikohtainen saanto on
   lisattavissa yhteen paikkaan.
   Briiffin otsikkotaso on <h2> (Parsedown "## Mita jutussa ei kerrota"), joten
   se liitetaan h1:n deklaraatioon — uutta font-size-arvoa EI lisata (kynnys
   232, taso 230, konfigissa "EI NOSTETA enaa"). */
.neutral-content h1,
.neutral-content h2,
.briiffi-sisalto h1,
.briiffi-sisalto h2 { font-size: 1.5rem; margin-bottom: var(--space-3); }
.neutral-content p,
.briiffi-sisalto p { margin-bottom: var(--space-3); line-height: 1.6; }
.neutral-content strong,
.briiffi-sisalto strong { font-weight: 600; }
.neutral-content ul,
.neutral-content ol,
.briiffi-sisalto ul,
.briiffi-sisalto ol { margin-bottom: var(--space-3); padding-left: var(--space-4); }
.neutral-content li,
.briiffi-sisalto li { margin-bottom: var(--space-1); }
/* Briiffissa on kaksi osiota perakkain — toinen otsikko tarvitsee ilmavuutta
   edellisesta kappaleesta, ensimmainen ei paneelin karjessa. */
.briiffi-sisalto h2 { margin-top: var(--space-4); }
.briiffi-sisalto h2:first-child { margin-top: 0; }

/* --- content-body (pienempi leipateksti, esim. drilldown-tiivistelmat) --- */
.content-body p { font-size: 0.875rem; line-height: 1.6; }
.content-body ul {
  font-size: 0.875rem;
  padding-left: var(--space-4);
  margin: 0;
}

/* --- osio-lista (artikkelin osiot, luetelmat) --- */
.osio-lista {
  list-style: disc;
  padding: 0;
  margin: 0;
  padding-left: var(--space-4);
}
.osio-lista li {
  padding: 0;
  border-bottom: 1px solid var(--color-border);
}
.osio-lista li:last-child { border-bottom: none; }
ol.osio-lista { list-style: decimal; }

/* --- tag (yksittainen tunniste-pill) + tag-cloud ---
   Pyoristetty pilleri. Kaytetaan tag-cloud-containerissa (esim. etusivun suodattimet,
   drilldown-narratiivit). Primary-fixed = desaturoitu navy. */
.tag {
  display: inline-flex;
  align-items: center;
  padding: 0.15em 0.6em;
  border-radius: var(--radius-full);
  font-family: var(--font-label);
  font-weight: 500;
  font-size: 0.75rem;
  background: var(--color-tag-bg);
  color: var(--color-primary);
  transition: all var(--transition-fast);
}
.tag:hover { box-shadow: var(--shadow-sm); }
.tag-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* --- bias-gauge (vinoumamittari — visuaalinen kaari tai palkki) --- */
.bias-gauge { text-align: center; }

/* --- blackout-prompt (pimento-kehotus, artikkelisivun ennen pimento-tuloksia) --- */
.blackout-prompt {
  margin-top: var(--space-3);
  padding: var(--space-4);
  background: var(--color-accent-subtle);
  border: 1px solid var(--color-accent-border);
  border-radius: var(--radius-md);
}

/* --- entiteetti-ruudukko (2-sarakkeinen grid artikkelin toimijoille/entiteeteille) --- */
.entiteetti-ruudukko {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
@media (min-width: 481px) {
  .entiteetti-ruudukko { grid-template-columns: 1fr 1fr; }
}

/* --- taho-linkki (kritisoidut/puolustetut tahot → /aihe/{slug}) ---
   Sama linkkityyli kuin pohjan a-elementti (color: --color-primary, hover:
   --color-primary-light). Plain-text (resolvoimaton) perii osio-lista li
   -tyylin sellaisenaan — ei eroa staattisten ja "klikkaamattomien" valilla.
   Hover-tila paljastaa klikattavuuden. (#404, sama UX-paatos kuin kamma-kortti #308.) */
.taho-linkki {
  color: var(--color-primary);
  text-decoration: underline;
}
.taho-linkki:hover {
  color: var(--color-primary-light);
}

/* --- trending-tags (etusivu — toistuvat aiheet) --- */
.trending-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
}
.trending-tags .merkki {
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
  border: none;
  font-family: inherit;
  transition: background 0.15s, color 0.15s;

  margin-bottom: 0;
}
.trending-tags .merkki:hover { opacity: 0.85; }

/* --- osio-alaotsikko (etusivun pienotsikko) --- */
.osio-alaotsikko {
  font-family: var(--font-label);
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  margin-bottom: var(--space-2);
}

/* --- summary-narratives-title (drilldown-narratiivi otsikko) --- */
.summary-narratives-title {
  font-size: 0.8rem;
  color: var(--color-muted);
  margin-top: var(--space-3);
  margin-bottom: var(--space-1);
}

/* --- narratiivipaneeli (topic, media) ---
   Koko paneelin tyylit yhdessa lohkossa: ohjaimet, ajanjakso-napit,
   skeleton, meta-rivi, luotettavuus-badge, kortti + sen badget,
   savy-badge ja sen 6 variantia, agenttitagit 3 varianttia, sokeat
   kohdat ja media-profiili. */
.narrative-controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.period-buttons {
  display: flex;
  gap: var(--space-1);
}
.period-buttons .merkki {
  cursor: pointer;
  border: none;
  transition: all var(--transition-fast);
}
.period-buttons .merkki.active { font-weight: 600; }

.narrative-skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.narrative-skeleton .skeleton-block {
  height: 5rem;
  background: linear-gradient(90deg, var(--color-surface-low) 25%, var(--color-surface) 50%, var(--color-surface-low) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.5s infinite;
  border-radius: var(--radius-md);
}

.narrative-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.reliability-badge {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.reliability-low {
  background: var(--color-bias-high-bg);
  color: var(--color-bias-high-text);
}
.reliability-moderate {
  background: var(--color-bias-medium-bg);
  color: var(--color-bias-medium-text);
}
.reliability-high {
  background: var(--color-bias-low-bg);
  color: var(--color-bias-low-text);
}

.narratiivi-kortti {
  background: var(--color-secondary);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
}
.narratiivi-kortti .otsikkorivi {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  flex-wrap: wrap;
}
.narratiivi-kortti .merkit {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
  align-items: center;
}
.narrative-label {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-text);
  font-family: var(--font-body);
}
.narrative-examples {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: var(--space-1);
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-2);
}
.narrative-examples span,
.narrative-examples a,
.narrative-examples strong { font-size: 0.75rem; color: var(--color-muted); font-style: italic; }
/* Esimerkkiartikkelien linkit (#648) — otsikko klikattavana /artikkeli/{url_hash}:iin. */
.narrative-examples a:hover { color: var(--color-primary); text-decoration: underline; }
.narrative-examples strong { font-style: normal; color: var(--color-text); }

.tone-badge {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.tone-pelottava    { background: var(--color-bias-high-bg);   color: var(--color-bias-high-text); }
.tone-toiveikas    { background: var(--color-bias-low-bg);    color: var(--color-bias-low-text); }
.tone-neutraali    { background: var(--color-bias-neutral-bg); color: var(--color-bias-neutral-text); }
.tone-moralisoiva  { background: var(--color-tone-moralisoiva-bg);  color: var(--color-tone-moralisoiva-text); }
.tone-dramatisoiva { background: var(--color-tone-dramatisoiva-bg); color: var(--color-tone-dramatisoiva-text); }
.tone-kriittinen   { background: var(--color-tone-kriittinen-bg);   color: var(--color-tone-kriittinen-text); }

.agent-tags {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
}
.agent-tag {
  display: inline-block;
  padding: 2px var(--space-2);
  border-radius: var(--radius-full);
  font-size: 0.7rem;
  font-weight: 500;
}
.agent-tag.threat { background: var(--color-bias-high-bg);  color: var(--color-bias-high-text); }
.agent-tag.victim { background: var(--color-victim-bg);     color: var(--color-victim-text); }
.agent-tag.savior { background: var(--color-bias-low-bg);   color: var(--color-bias-low-text); }

.blind-spots {
  background: var(--color-secondary);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
}
/* Katvealueet-pillit ovat LLM:n tuottamia pitkia fraaseja (32-62 merkkia) —
   .merkki-peruksen nowrap aiheuttaisi sivuttaisvierityksen mobiilissa
   (palaute #44). Sallitaan rivitys vain tassa kontekstissa. */
.blind-spots .merkki { white-space: normal; overflow-wrap: anywhere; }
.media-profile {
  background: var(--color-secondary);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
}

/* Wrapper-luokka narratiivipaneelille — sitoo task #248:ssa siirretyt tyylit
   (korvaa aiemmin inline-kaytetyt text-sm/text-xs/muted-text -utilityt). */
.narratiivi-paneeli .tyhja-vinkki { font-size: 0.875rem; color: var(--color-muted); }
.narratiivi-paneeli .narrative-meta span { font-size: 0.875rem; color: var(--color-muted); }
.narratiivi-paneeli .narratiivi-kortti p { font-size: 0.875rem; color: var(--color-muted); }
.narratiivi-paneeli .blind-spots strong { font-size: 0.875rem; }
.narratiivi-paneeli .media-profile strong { font-size: 0.875rem; }
.narratiivi-paneeli .media-profile p { font-size: 0.875rem; }

/* --- debug-dumppi ---
   Diagnostiikkalohko artikkelisivulla (?debug=1). Tietoinen inline-toteutus
   yhden viewin tasolla — EI komponentti, koska käyttöpaikka on yksi
   eikä tarvitse abstrahointia. Ks. spec/komponentit.md § Inline-duplikaatit
   "Debug-dumpit". CSS täällä jotta inline-styles minimissä viewissä. */
.debug-dumppi { background: var(--color-surface); }
.debug-dumppi .debug-dumppi-otsikko { margin-top: 0; font-size: 0.95rem; color: var(--color-muted); }
.debug-dumppi .debug-dumppi-osio { margin-top: var(--space-2); }
.debug-dumppi .debug-dumppi-osio summary {
    cursor: pointer;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--color-muted);
    padding: var(--space-1) 0;
}
.debug-dumppi .debug-dumppi-osio[open] summary { color: var(--color-text); }
.debug-dumppi h4 {
    font-family: var(--font-body);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--color-muted);
    margin: var(--space-3) 0 var(--space-1);
}
.debug-dumppi .debug-dumppi-lista {
    display: grid;
    grid-template-columns: minmax(160px, max-content) 1fr;
    gap: var(--space-1) var(--space-3);
    margin: var(--space-2) 0;
    font-size: 0.8125rem;
}
.debug-dumppi .debug-dumppi-lista dt {
    font-family: var(--font-label);
    font-weight: 600;
    color: var(--color-muted);
}
.debug-dumppi .debug-dumppi-lista dd {
    margin: 0;
    word-break: break-word;
    font-family: var(--font-body);
}
.debug-dumppi .debug-dumppi-lista dd em { color: var(--color-muted); font-style: italic; }
.debug-dumppi .debug-dumppi-pre {
    background: var(--color-bg);
    padding: var(--space-2);
    font-size: 0.75rem;
    line-height: 1.4;
    overflow-x: auto;
    max-height: 400px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    margin: 0 0 var(--space-2);
    white-space: pre-wrap;
    word-break: break-word;
}
.debug-dumppi .debug-dumppi-pre.debug-dumppi-pre-pitka { max-height: 600px; }
.debug-dumppi .debug-dumppi-tagit {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    list-style: none;
    padding: 0;
    margin: var(--space-1) 0 var(--space-3);
    font-size: 0.75rem;
}
.debug-dumppi .debug-dumppi-tagit li {
    background: var(--color-bg);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    font-family: var(--font-label);
}

/* ============================================================
   7. APU-UTILITIES — rajattu whitelist (D38, 21.4.2026)
   Sallitut: tila, layout-spacing, flex-rakenne.
   Tekstityyli, värit ja linkit kuuluvat komponenttiin — ei tänne.
   Ks. spec/frontend-css.md § Sallitut utility-luokat.
   ============================================================ */

/* --- tila --- */
.hidden  { display: none; }
.muted   { color: var(--color-muted); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.lataa   { opacity: .5; pointer-events: none; transition: opacity var(--transition-fast); }

/* --- marginaali (ylä) --- */
.mt-0   { margin-top: 0; }              /* #383, 6.5.2026 — modaalien h3-otsikot */
.mt-xs  { margin-top: var(--space-1); }
.mt-sm  { margin-top: var(--space-2); }
.mt-md  { margin-top: var(--space-3); }
.mt-lg  { margin-top: var(--space-4); }
.mt-xl  { margin-top: var(--space-5); }
.mt-2xl { margin-top: var(--space-6); }

/* --- marginaali (ala) --- */
.mb-0   { margin-bottom: 0; }           /* #383, 6.5.2026 — viimeisen elementin tyhjä alapuoli */
.mb-xs  { margin-bottom: var(--space-1); }
.mb-sm  { margin-bottom: var(--space-2); }
.mb-md  { margin-bottom: var(--space-3); }
.mb-lg  { margin-bottom: var(--space-4); }
.mb-xl  { margin-bottom: var(--space-5); }

/* --- flex-layout --- */
.flex-wrap    { flex-wrap: wrap; }
.items-center { align-items: center; }
.gap-sm       { gap: var(--space-2); }
.gap-md       { gap: var(--space-3); }
.gap-lg       { gap: var(--space-4); }   /* #383, 6.5.2026 — profile.php hintataulukoiden välistys */
.flex-1       { flex: 1; }
.w-full       { width: 100%; }

/* --- text-tasaus (rakenteellinen, ei typografinen) ---
   D38-jatko (#383, 6.5.2026): poikkeus säännöstä "tekstityyli kuuluu komponenttiin".
   Käyttötapa on data/rakenne (taulukon hinta-sarake, kortin keskittäminen) — ei
   typografinen ominaisuus. 12+ osumaa systemaattinen tarve. */
.text-center  { text-align: center; }
.text-right   { text-align: right; }

/* --- sivurakenne-apuri (otsikot, sivupalkin rivit) --- */
.sidebar-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
}
.osio-otsikko     { font-size: 1.25rem; margin-bottom: var(--space-3); }
.valiotsikko { font-size: 0.9rem;  color: var(--color-muted); margin-bottom: var(--space-2); }
.sidebar-title    { font-size: 1rem;    margin-bottom: var(--space-3); }
.osio-mb          { margin-bottom: var(--space-5); }
.page-header      { margin-bottom: var(--space-5); }
.page-header h1   { margin-bottom: var(--space-1); }

/* --- grid-apurit --- */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--space-3);
}

/* Aihe-arkiston tunnusluvut: kolme numeroa (artikkelit, mediat, vinouma) AINA
   rinnakkain — myös mobiilissa. Kortit ovat lukuvaltaisia (numero + lyhyt
   label), joten 3 saraketta toimii ~360px:n leveydellä kun gap ja paddingit
   ovat tiukkoja. Kortin ja numerofontin kutistaminen ehkäisee overflowin. */
.korttiruudukko.stats-grid-3 {
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}
.korttiruudukko.stats-grid-3 .kortti { padding: var(--space-2); }
.korttiruudukko.stats-grid-3 .entiteetti-tilasto .arvo { font-size: 1.25rem; }
@media (min-width: 768px) {
  .korttiruudukko.stats-grid-3 { gap: var(--space-3); }
  .korttiruudukko.stats-grid-3 .kortti { padding: var(--space-3); }
  .korttiruudukko.stats-grid-3 .entiteetti-tilasto .arvo { font-size: 1.5rem; }
}

/* ============================================================
   === Kamma-kortti ===
   Task #308 (29.4.2026), yleistetty #881 (16.8.2026). Domain-komponentti:
   .kamma-kortti — "Kritiikki ja puolustus" -kampakuvaaja neljalla sivulla
   (/media, /aihe, /haku, /kirjoittaja). #881 poisti rinnakkaisen
   .kuka-kritisoi-puolustaa-lista -toteutuksen (aihe- ja hakusivun listat).
   Käyttää olemassa olevia tokeneita --color-kritisoitu-* ja --color-puolustettu-*.
   (Ei viistoa tahtimerkin perassa: merkkipari sulkisi taman kommentin kesken,
   jolloin lopputeksti luettaisiin selektoriksi ja seuraava saanto katoaisi.)
   Mobile-first.
   ============================================================ */

/* --- Kamma-kortti --- */
/* Leveysrajaus (palaute #220). Poikkeaa tietoisesti spec/frontend-css.md:n
   saannosta "komponentin leveys tayttaa aina aitinsa": kamma on kuvaaja, ei
   tekstilohko, ja sen luettavuus rikkoutuu leveana — lukija vertaa rivin
   vasenta (kritiikki) ja oikeaa (puolustus) laitaa toisiinsa, ja 1400px:n
   levealla lohkolla laidat ovat liian kaukana toisistaan jotta vertailu
   syntyisi. Rajaus on TASSA eika neljassa kutsujassa (/media, /aihe, /haku,
   /kirjoittaja), koska muuten sama saanto pitaisi toistaa nelja kertaa ja
   viides kayttopaikka unohtaisi sen.
   MOBIILI: tama on max-width, joten se ei voi sitoa kapealla naytolla — 360px:n
   ruudulla kortti on taysleveä eika 720px:n raja aktivoidu lainkaan. Siksi
   saanto on perustyylissa eika media queryssa (sama mobile-first-kuvio kuin
   `.hero > *`, ks. kommentti sen yhteydessa). Ei `margin: 0 auto`: kortin vasen
   reuna pysyy linjassa ylapuolisen osio-otsikon kanssa. */
.kamma-kortti {
  max-width: var(--content-kuvaaja);
}
.kamma-kortti .kamma-mittari {
  font-size: 0.875rem;
  color: var(--color-muted);
  margin-bottom: var(--space-3);
}
.kamma-kortti .kamma-mittari-krit { color: var(--color-kritisoitu-text); }
.kamma-kortti .kamma-mittari-puol { color: var(--color-puolustettu-text); }

.kamma-kortti .kamma-rivit {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Rivirakenne (4 saraketta vasemmalta oikealle):
   [nimi] [crit-luku] [palkki: vasen | oikea] [def-luku]
   Nimi on rivin alussa, palkki keskellä rivin oikealla puolella ja kasvaa
   keskeltä haarautuen — vasen ulottuu vasemmalle, oikea oikealle.

   `:not([hidden])` on speksin vakiintunut kuvio (ks. `.sivutus` ja
   `.hakukentta > input:not([hidden])`, frontend-css.md § Picon lomakesaannot):
   ilman sita tama `display: grid` kumoaisi selaimen `[hidden] { display: none }`
   -oletuksen, koska tekijan saanto voittaa selaimen oletuksen spesifisyydesta
   riippumatta. #945: app.js piilottaa `$naytaAluksi`-rajan ylittavat rivit
   `hidden`-attribuutilla ja paljastaa ne napista — piilotus ei saa vuotaa auki
   tasta saannosta. Ei `!important`. */
.kamma-kortti .kamma-rivi:not([hidden]) {
  display: grid;
  grid-template-columns: minmax(5rem, 35%) 2rem 1fr 2rem;
  align-items: center;
  gap: var(--space-1);
  font-size: 0.85rem;
}

.kamma-kortti .kamma-arvo-vasen,
.kamma-kortti .kamma-arvo-oikea {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.kamma-kortti .kamma-arvo-vasen {
  text-align: right;
  color: var(--color-kritisoitu-text);
}
.kamma-kortti .kamma-arvo-oikea {
  text-align: left;
  color: var(--color-puolustettu-text);
}
/* #881: luku voi olla drilldown-linkki (aihesivun entity_stance-suodatus).
   Vari tulee ylla olevista saannoista — linkille vain affordanssi hoverissa. */
.kamma-kortti a.kamma-arvo-vasen,
.kamma-kortti a.kamma-arvo-oikea {
  text-decoration: none;
}
.kamma-kortti a.kamma-arvo-vasen:hover,
.kamma-kortti a.kamma-arvo-oikea:hover {
  text-decoration: underline;
}

/* Vaakapalkki, kaksi puolikasta keskeltä haarautuen.
   Vasen: justify-self end → palkki päättyy keskelle ja kasvaa vasemmalle.
   Oikea: justify-self start → palkki alkaa keskeltä ja kasvaa oikealle. */
.kamma-kortti .kamma-palkki {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  min-height: 1.25rem;
}

.kamma-kortti .kamma-palkki-vasen,
.kamma-kortti .kamma-palkki-oikea {
  display: block;
  height: 0.5rem;
  border-radius: 0;
  max-width: 100%;
}
.kamma-kortti .kamma-palkki-vasen {
  background: var(--color-kritisoitu-bg);
  border: 1px solid var(--color-kritisoitu-text);
  justify-self: end; /* päättyy keskelle, kasvaa vasemmalle */
}
.kamma-kortti .kamma-palkki-oikea {
  background: var(--color-puolustettu-bg);
  border: 1px solid var(--color-puolustettu-text);
  justify-self: start; /* alkaa keskeltä, kasvaa oikealle */
}

.kamma-kortti .kamma-nimi {
  text-align: left;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.kamma-kortti a.kamma-nimi {
  color: var(--color-text);
  text-decoration: none;
  border-bottom: 1px dotted var(--color-border);
}
.kamma-kortti a.kamma-nimi:hover {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

/* #881 (palaute #179): tasapainoinen rivi merkitaan, EI suodateta pois.
   Merkinta on tekstia omalla rivillaan (grid-column 1 / -1) — vari yksin ei
   vality ruudunlukijalle eika nelisarakkeiseen riviin mahdu viidetta saraketta
   ilman etta nimi tai palkki kutistuu. Ei omaa font-sizea: perii rivin koon
   (mobiili 0.85rem / >=480px 0.95rem), joten mitaan ei tarvitse pitaa synkassa. */
.kamma-kortti .kamma-tasapaino {
  grid-column: 1 / -1;
  color: var(--color-muted);
  font-family: var(--font-label);
}

/* #945 (palaute #214): "Lataa lisaa" -nappi. Ulkoasu tulee `.painike`-luokasta,
   tassa vain sijoittelu — ei omaa font-sizea (kynnys 232, css-velka-merkit.json).

   ⚠️ `[hidden]`-rajaus on PAKOLLINEN eika kosmetiikkaa: `.painike` asettaa
   `display: inline-flex` (app.css osio "painike (geneerinen)"), joka kumoaa
   selaimen `[hidden] { display: none }` -oletuksen samalla mekanismilla kuin
   `.kamma-rivi`-grid yllä. Ilman tata nappi nakyisi ilman JS:aa eika tekisi
   mitaan — juuri se kontrolli jonka #912 kielsi. Kirjoitettu `[hidden]`-
   nollauksena eika `:not([hidden])`-rajauksena koska `display` tulee toisesta
   luokasta jota tama saanto ei omista; (0,2,1) voittaa `.painike`n (0,1,0). */
.kamma-kortti .kamma-lisaa[hidden] { display: none; }
.kamma-kortti .kamma-lisaa {
  margin-top: var(--space-3);
}

.kamma-kortti .info-teksti {
  margin-top: var(--space-3);
  margin-bottom: 0;
}

@media (min-width: 480px) {
  .kamma-kortti .kamma-rivi {
    grid-template-columns: minmax(8rem, 12rem) 3rem 1fr 3rem;
    gap: var(--space-2);
    font-size: 0.95rem;
  }
}

/* ============================================================
   === Kehystysroolit (task #504) ===
   Mediasivun "Kehystysroolit"-palikka narratiivit-tabissa: kolme
   rinnakkaista top-listaa (uhka/uhri/pelastaja). Domain-komponentti.
   Käyttää olemassa olevia .agent-tag.threat/victim/savior -värejä
   rooliotsikoissa. Ei uusia font-size-arvoja — periytyy (#504-rajoite).
   Mobile-first: 1 sarake → 600px:stä 3 saraketta.
   ============================================================ */
.kehystysroolit h3 {
  margin-top: 0;
  margin-bottom: var(--space-3);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.kehystysroolit-ruudukko {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
.kehystysroolit-sarake h4 {
  margin: 0 0 var(--space-2);
}
.kehystysroolit-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.kehystysroolit-rivi {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
}
.kehystysroolit-rivi:last-child {
  border-bottom: none;
}
.kehystysroolit-nimi {
  text-decoration: none;
  color: var(--color-text);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
a.kehystysroolit-nimi:hover {
  color: var(--color-accent);
  text-decoration: underline;
}
.kehystysroolit-arvo {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--color-muted);
  white-space: nowrap;
}
@media (min-width: 600px) {
  .kehystysroolit-ruudukko {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

/* ============================================================
   === Tunneprofiili (task #509) ===
   Entiteettisivun (media/kirjoittaja/aihe) tunneprofiili-palikka:
   yksi top-lista (emotional.appeals) label + lukumäärä + osuus.
   Data-ohjattu domain-komponentti (ketju #511 uusiokäyttää).
   Ei uusia font-size-arvoja — periytyy kortti/listarakenteesta.
   ============================================================ */
.tunneprofiili h3 {
  margin-top: 0;
  margin-bottom: var(--space-3);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.tunneprofiili-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.tunneprofiili-rivi {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
}
.tunneprofiili-rivi:last-child {
  border-bottom: none;
}
.tunneprofiili-nimi {
  color: var(--color-text);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tunneprofiili-arvo {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  white-space: nowrap;
}
.tunneprofiili-luku {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--color-text);
}
.tunneprofiili-osuus {
  font-variant-numeric: tabular-nums;
  color: var(--color-muted);
}

/* ============================================================
   === Otsikko vs. sisältö (task #512) ===
   Media-sivun "Otsikko vs. sisältö" -palikka: factualAlignment
   misleading/contradicted -tapausten lukumäärä + tuoreimmat
   esimerkkilinkit. Domain-komponentti. Ei prosenttirankingia,
   ei palkkeja (pieni N → vertailu harhaanjohtava). Ei uusia
   font-size-arvoja — periytyy kortti/listarakenteesta.
   ============================================================ */
/* Lohkon OMA otsikko: <h3> + vihje-nappi samalle riville pystykeskitettyna.
   RAJATTU `> header`iin (#882, palaute #178). Rajaamattomana `.otsikko-sisalto h3`
   on jalkelaisselektori, ja #621:n jalkeen lohko sisaltaa artikkelilistan samassa
   <section>issa -> saanto osui MYOS jokaisen artikkelikortin <h3>:een. Silloin
   kortin otsikosta tuli flex-rivi, ja `.otsikko-prefix` ("Analyysi artikkelista:",
   display: block) asettui otsikon VIEREEN omaksi palstakseen sen sijaan etta olisi
   omalla rivillaan kuten kaikissa muissa artikkelilistoissa. <header> on lohkon
   suora lapsi (otsikko-sisalto-kortti.php), artikkelikortit eivat ole sen sisalla. */
.otsikko-sisalto > header h3 {
  margin-top: 0;
  margin-bottom: var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.otsikko-sisalto-luku {
  color: var(--color-muted);
  margin-bottom: var(--space-3);
}
.otsikko-sisalto-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.otsikko-sisalto-rivi {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
}
.otsikko-sisalto-rivi:last-child {
  border-bottom: none;
}
.otsikko-sisalto-meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--color-muted);
}
.otsikko-sisalto-tila {
  color: var(--color-bias-high-text);
  font-weight: 600;
}
.otsikko-sisalto-pvm {
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   8. KOLMANNEN OSAPUOLEN YLIKIRJOITUKSET — aivan lopuksi
   Lisäosien (payment-widget, captcha, embed, yms.) tyylit
   ylikirjoitetaan täällä. Niputetaan per lisäosa omaan
   lohkoon. Ei sekoiteta omiin komponentteihin.
   ============================================================ */

/* --- bottisuoja (Cloudflare Turnstile, #724) --- */
/* Widget on kiintealeveyksinen iframe (~300x65). Komponentti ei paata
   asemointiaan (spec/frontend-templatointi.md § 2) — .keskita on kutsujan
   antama variantti heroon, oletus seuraa lomakkeen tekstivirtaa. */
.bottisuoja {
  margin-top: var(--space-3);
}
.bottisuoja.keskita {
  display: flex;
  justify-content: center;
}
/* Kelluva variantti (#751): yksi widget koko sivulle, renderoity layoutin
   loppuun. Widget on `interaction-only` eli nakymaton kunnes Turnstile vaatii
   vuorovaikutusta — staattisena layoutin lopussa haaste jaisi sivun alalaitaan
   nakymattomiin ja lahetys nayttaisi jumittuvan syytta. Siksi se kiinnitetaan
   nakymaan. Vasen alalaita: .analyysijono kelluu oikeassa (z-index 50),
   eivat peita toisiaan. z-index 110 = sama kerros kuin ylapalkin dropdownilla
   (ylapalkin 100 ylle, modaalin 200 alle) — haaste on klikattava mutta ei
   ilmesty avatun modaalin paalle.
   Bottom-lauseke on sama kuin .analyysijonolla: Android Chrome palauttaa
   env(safe-area-inset-bottom)=0 vaikka selaimen alapalkki peittaa nakymaa
   (palaute #47), joten minimi on var(--space-5).
   pointer-events: none wrapperissa — nakymaton widget ei saa siepata
   klikkauksia sivun alalaidasta. Haaste-iframe palauttaa auto:n. */
.bottisuoja.kelluva {
  position: fixed;
  left: var(--space-2);
  bottom: max(env(safe-area-inset-bottom, 0px) + var(--space-4), var(--space-5));
  z-index: 110;
  margin-top: 0;
  pointer-events: none;
}
.bottisuoja.kelluva iframe {
  pointer-events: auto;
}
@media (min-width: 481px) {
  .bottisuoja.kelluva {
    left: var(--space-4);
    bottom: var(--space-4);
  }
}

/* --- stripe-payment-element --- */
/* .StripeElement { ... } */

/* --- trustpilot-widget --- */
/* .trustpilot-widget { ... } */
