/* ===== play.html:n omat tyylit =============================================
   Irrotettu `play.html`:sta 30.8.2026. 4 494 rivia inline-CSS:aa oli
   suurin yksittainen mitattu asia koko koodikannassa — enemman kuin
   `norsu.css`:ssa (3 876).

   ⚠️ JARJESTYS ON SAILYTETTY, JA SE ON KOKO SIIRRON EHTO.
   `play.html` sanoo itse rivilla 240: *"pelisivun omat saannot voittavat
   tasapelin"*. 27 valitsinta on maaritelty seka taalla etta
   `norsu.css`:ssa, ja niista 6 EROAA sisallolta — esim. `.login-title`
   on taalla 36px ja siella 48px. Jos tama tiedosto ladattaisiin ENNEN
   `norsu.css`:aa, kuusi saantoa kaantyisi ilman etta mikaan kertoisi.

   Siksi latausjarjestys on:
       1. css/norsu.css      brandi ja jaetut
       2. <style> AVAUSRUUTU  jaa inlineksi: ensimaalaus
       3. css/play.css        TAMA
   ja `<link>` on avausruudun JALKEEN.

   ⚠️ AVAUSRUUDUN TYYLIT EIVAT SIIRTYNEET. Ne ovat inline tarkoituksella:
   ulkoinen tiedosto latautuu myohemmin, ja ensimmainen maalaus olisi
   valkoinen valahdys ennen violettia. Ensivaikutelma on koko idean
   pointti. Sama koskee SVG:n sisaista `.cls-1`:ta.
   ===================================================================== */

* {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    html {
      height: -webkit-fill-available;
    }

    body {
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      /* B1b (2026-08-18): valkoinen kangas. Väri ei ole enää pinta vaan
         merkitsee jotain — toimintoa (brändiväri) tai kappaleen identiteettiä
         (tuoteväri). Harmaa on "ei vielä". Ks. NOTES B-vaiheen pohja. */
      background: #fff;
      min-height: 100vh;
      min-height: -webkit-fill-available;
      color: var(--teksti);
      overflow-x: hidden;
      padding: var(--norsu-turva-yla) env(safe-area-inset-right) var(--norsu-turva-ala) env(safe-area-inset-left);
    }

    .container {
      max-width: 100%;
      /* B1b-korjaus (2026-08-18): sisältö YLÄREUNASTA, ei pystykeskitettynä.
         Kaksi ongelmaa ratkeaa kerralla:
         1) Kun iOS:n näppäimistö aukeaa, visuaalinen näkymä kutistuu mutta
            100vh ei muutu → keskitetty sisältö jäi näppäimistön alle eikä
            kirjoituskenttää nähnyt lainkaan (Markon testi 18.8.).
         2) Pystykeskitys jätti ison tyhjän ylälaidan ja ahtaan keskiön —
            "ahdasta on kun kaikki on keskellä". Duolingossa sisältö alkaa
            ylhäältä ja hengittää alaspäin.
         dvh (dynamic viewport height) ottaa näppäimistön huomioon siellä
         missä se on tuettu; min-height ei enää pakota täyttä korkeutta. */
      padding: 12px 12px 32px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
    }

    /* ===== SCREEN MANAGEMENT ===== */
    .screen {
      display: none;
      width: 100%;
      max-width: 900px;
      animation: fadeIn 0.4s ease-in;
    }

    /* Näkyvyys ei saa riippua animaatiosta — ks. sama perustelu
       css/norsu.css:n `.screen.active`issa (24.8.2026). */
    .screen.active { display: block; opacity: 1; }

    /* EI TRANSFORMIA (22.8.2026). Mitattu: `#biteMapScreen` jäi pysyvästi
       tilaan `matrix(1,0,0,1,0,10)` — animaatio ei päättynyt vaan jäi
       alkuarvoonsa. Näkyvä haitta oli pieni (koko näkymä 10 px alaviistossa),
       mutta transform tekee jälkeläisten `position: fixed` -asemoinnista
       suhteellisen SIIHEN eikä ruutuun. Se esti yläpalkin naulaamisen ja
       sai bannerin laahaamaan vieritettäessä.

       Liuku ei tuo mitään mitä läpinäkyvyys ei jo tee. */
    /* `fadeIn` asuu nyt `css/norsu.css`:ssa — yksi lahde kaikille
       sivuille. Tama kaksoiskappale poistettiin 4.9.2026: korjattu
       versio oli VAIN taalla, ja `play.html` oli ainoa sivu joka sai
       sen. Ks. norsu.css:n perustelu ja tools/css-kaksoiset.py */

    /* ===== LOGIN SCREEN ===== */
    #loginScreen {
      background: white;
      border-radius: 20px;
      padding: 40px 30px;
      box-shadow: none;   /* B1b: sisältö istuu suoraan valkoisella, ei kelluvaa laatikkoa */
      text-align: center;
      max-height: 95vh;
      max-height: 95dvh;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }

    .login-title {
      font-size: 48px;
      font-weight: 700;
      margin-bottom: 10px;
      background: var(--brand-gradient);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }

    .login-subtitle {
      font-size: 16px;
      color: var(--teksti-kevyt);
      margin-bottom: 12px;
    }

    /* Removed: login, emoji-pin, player-card, settings-overlay CSS (moved to launcher.html) */

    /* ===== RANKING OVERLAY ===== */
    .ranking-overlay {
      position: fixed;
      top: 0; left: 0; right: 0; bottom: 0;
      background: rgba(0,0,0,0.6);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 1000;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.25s;
    }

    .ranking-overlay.active {
      opacity: 1;
      pointer-events: auto;
    }

    .ranking-card {
      background: #fff;
      border-radius: 20px;
      padding: 24px 20px;
      width: 92%;
      max-width: 400px;
      max-height: 85vh;
      text-align: center;
      box-shadow: none;   /* B1b: sisältö istuu suoraan valkoisella, ei kelluvaa laatikkoa */
      display: flex;
      flex-direction: column;
    }

    .ranking-title {
      font-size: 22px;
      font-weight: 700;
      color: var(--teksti);
      margin-bottom: 8px;
    }

    .ranking-tabs {
      display: flex;
      gap: 4px;
      margin-bottom: 14px;
      background: #f0f0f0;
      border-radius: 10px;
      padding: 3px;
    }
    .ranking-tab {
      flex: 1;
      padding: 7px 0;
      border: none;
      border-radius: 8px;
      background: transparent;
      font-size: 13px;
      font-weight: 600;
      color: var(--teksti-vaimea);
      cursor: pointer;
      transition: all 0.2s;
    }
    .ranking-tab.active {
      background: #fff;
      color: var(--teksti);
      box-shadow: 0 1px 4px rgba(0,0,0,0.1);
    }

    .ranking-list {
      flex: 1;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      margin-bottom: 12px;
    }

    .ranking-row {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px 12px;
      border-radius: 12px;
      margin-bottom: 6px;
      background: #f8f8ff;
      transition: background 0.2s;
    }

    .ranking-row:first-child {
      background: linear-gradient(135deg, var(--tint-yellow), #fff3cd);
      border: 2px solid #ffd54f;
    }

    .ranking-pos {
      font-size: 16px;
      font-weight: 700;
      color: var(--teksti-kevyt);
      min-width: 28px;
      text-align: center;
    }

    .ranking-row:first-child .ranking-pos {
      color: #f9a825;
    }

    .ranking-avatar {
      font-size: 28px;
    }

    .ranking-info {
      flex: 1;
      min-width: 0;
      text-align: left;
    }

    .ranking-name {
      font-size: 15px;
      font-weight: 700;
      color: var(--teksti);
    }



    .ranking-stats {
      display: inline;
      font-size: 11px;
      color: var(--teksti-kevyt);
      margin-left: 8px;
    }

    .ranking-score {
      position: relative;
      min-width: 52px;
      min-height: 52px;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
    }
    .ranking-score-rosette {
      position: absolute;
      inset: 0;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .ranking-score-rosette svg {
      width: 52px;
      height: 52px;
    }
    .ranking-score-number {
      position: relative;
      z-index: 1;
      font-size: 15px;
      font-weight: 900;
      color: #fff;
      text-shadow: 0 1px 2px rgba(0,0,0,0.2);
      line-height: 1;
    }
    .ranking-score-label {
      position: absolute;
      bottom: -2px;
      left: 50%;
      transform: translateX(-50%);
      font-size: 7px;
      font-weight: 700;
      color: rgba(255,255,255,0.85);
      text-transform: uppercase;
      letter-spacing: 0.5px;
      z-index: 1;
    }

    .ranking-empty {
      padding: 30px 10px;
      color: var(--teksti-kevyt);
      font-size: 14px;
    }

    .ranking-loading {
      padding: 30px 10px;
      color: var(--teksti-kevyt);
      font-size: 14px;
    }

    .ranking-close-btn {
      display: block;
      width: 100%;
      padding: 12px;
      border: none;
      border-radius: 12px;
      background: transparent;
      font-size: 14px;
      color: var(--teksti-kevyt);
      cursor: pointer;
      transition: all 0.2s;
    }

    .ranking-close-btn:hover {
      background: #f5f5f5;
      color: var(--teksti-himmea);
    }

    .ranking-btn {
      font-size: 0.95em;
      padding: 10px 20px;
      background: linear-gradient(135deg, #f9a825, #ff8f00);
      color: #fff;
      border: none;
      border-radius: 12px;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.2s;
      margin-top: 8px;
    }

    .ranking-btn:hover {
      transform: scale(1.03);
      box-shadow: 0 4px 15px rgba(249,168,37,0.4);
    }

    /* ===== BITE MAP SCREEN ===== */
    #biteMapScreen {
      background: transparent;
    }

    .bite-map-header {
      text-align: center;
      margin-bottom: 40px;
      color: var(--ink);
    }

    /* Yläpalkki pysyy paikallaan polkua vierittäessä (Markon havainto 20.8.):
       asetuksiin piti scrollata takaisin ylös. Palkin sisältö on navigointia,
       ei sisältöä — navigointi ei saa kadota sisällön mukana.
       Tausta on läpinäkymätön, jotta kappalepalkit kulkevat sen ALTA eivätkä
       läpi. ::before venyttää saman valkoisen ylös turva-alueen (kellonaika,
       akku) taakse — muuten sisältö näkyisi statusbarin raosta kun palkki on
       kiinnittyneenä. Alaviiva ilmestyy vasta kun on vieritetty, jotta
       lepotilassa mikään ei viivoita valkoista kangasta turhaan. */
    .bm-topbar {
      display: flex;
      align-items: center;
      gap: 10px;
      /* NAULATTU (22.8.2026, Markon pyyntö). `position: fixed` on kiinni
         RUUDUSSA — se ei liiku vieritettäessä eikä ylivenytyksessä, toisin
         kuin `sticky`, joka on kiinni dokumentissa.
         Tämä oli mahdollista vasta kun `fadeIn`-animaation transform
         poistettiin: transform teki jälkeläisen kiinteästä asemoinnista
         suhteellisen siihen, ja palkki valui −386 px 400 px vierityksellä. */
      position: fixed;
      top: 0; left: 0; right: 0;
      z-index: 60;
      background: #fff;
      /* Sivupehmuste jaetun `.norsu-ylapalkki`-luokan muuttujan kautta: kaksi
         samantarkkuuksista sääntöä samasta ominaisuudesta olisi jättänyt
         voittajan järjestyksen varaan.

         18px suunnilleen alanavigaation reunimmaisen kuvakkeen linjassa
         (Marko 22.8.2026: "ne ovat olleet liian reunassa"). KIINTEÄ eikä
         mitattu: kokeilin laskea sen alanavin napista ajossa, ja mittaus osui
         hetkeen jolloin navi ei ollut vielä asemoitunut → 154 px pehmustetta
         ja lippu keskellä ruutua. Likimääräinen oikea voittaa tarkan väärän. */
      --yp-sivupehmuste: 18px;
      /* Kiinteä palkki on bodyn pehmusteen ULKOPUOLELLA, joten
         turva-alue tulee tähän. Muuttujan kautta, ettei synny kahta
         samantarkkuuksista sääntöä samasta ominaisuudesta. */
      --yp-ylapehmuste: calc(8px + var(--norsu-turva-yla));
      transition: box-shadow 0.18s ease;
    }
    .bm-topbar::before {
      content: '';
      position: absolute;
      left: 0; right: 0; bottom: 100%;
      height: 120px;
      background: #fff;
      pointer-events: none;
    }
    .bm-topbar.bm-topbar-kiinni {
      box-shadow: 0 1px 0 var(--border, #E8E4EE), 0 8px 12px -10px rgba(0, 0, 0, 0.25);
    }
    .bm-icon {
      flex: 0 0 auto;
      width: 40px; height: 40px;
      display: flex; align-items: center; justify-content: center;
      border-radius: 50%;
      background: var(--bg);
      border: 1px solid var(--border);
      color: var(--ink);
      font-size: 20px; line-height: 1;
      text-decoration: none;
      cursor: pointer;
    }
    .bm-icon:hover { background: #eceef2; }

    /* Tila-alue: omat luvut, ei kohteita. Streak ja pisteet ovat sitä mitä
       yläpalkki saa kertoa — muiden luvut ovat alanavigaation 🏅:n takana. */
    .bm-tila {
      flex: 1 1 auto;
      min-width: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 12px;
      font-size: 14px;
      font-weight: 700;
      color: var(--ink-soft, #574F66);
    }
    .bm-tila .bm-luku { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
    .bm-tila .bm-streak { color: #E8590C; }
    .bm-tila .bm-pisteet { color: var(--brand-primary, var(--brand-primary)); }
    .bm-avatar { font-size: 22px; }


    .bite-map-title {
      flex: 1 1 auto;
      min-width: 0;
      text-align: center;
      font-size: 26px;
      font-weight: 700;
      margin-bottom: 0;
    }

    .bite-map-subtitle {
      font-size: 18px;
      opacity: 0.9;
    }

    .player-info {
      background: var(--bg);
      backdrop-filter: blur(10px);
      padding: 12px 20px;
      border-radius: 16px;
      color: white;
      font-weight: 600;
      margin-top: 15px;
      font-size: 14px;
      text-align: center;
    }

    .player-name-row {
      font-size: 18px;
      font-weight: 700;
      margin-bottom: 6px;
    }

    .medal-counts {
      display: flex;
      justify-content: center;
      gap: 12px;
      font-size: 15px;
      font-weight: 700;
    }

    .my-score-wrap {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      margin-top: 8px;
    }
    .my-score-rosette {
      position: relative;
      width: 56px;
      height: 56px;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
    }
    .my-score-rosette svg {
      position: absolute;
      inset: 0;
      width: 56px;
      height: 56px;
    }
    .my-score-num {
      position: relative;
      z-index: 1;
      font-size: 17px;
      font-weight: 900;
      color: #fff;
      text-shadow: 0 1px 3px rgba(0,0,0,0.25);
      line-height: 1;
    }
    .my-score-bonuses {
      display: flex;
      flex-direction: column;
      gap: 2px;
      font-size: 13px;
      font-weight: 700;
      color: var(--teksti-vaimea);
    }

    .medal-count-item {
      display: flex;
      align-items: center;
      gap: 3px;
      opacity: 0.6;
    }

    .medal-count-item.has-medals {
      opacity: 1;
    }

    .medal-count-num {
      font-size: 14px;
      min-width: 14px;
      text-align: center;
    }

    .play-time-display {
      font-size: 13px;
      font-weight: 600;
      color: #6b7280;
      margin-top: 4px;
    }

    /* ===== ASETUSPANEELI (2026-08-17, alhaalta nouseva) ===== */
    #gsOverlay { position: fixed; inset: 0; z-index: 4000; }
    #gsOverlay .gs-backdrop {
      position: absolute; inset: 0; background: rgba(0,0,0,0.45);
      opacity: 0; transition: opacity 0.22s ease;
    }
    #gsOverlay.open .gs-backdrop { opacity: 1; }
    #gsOverlay .gs-sheet {
      position: absolute; left: 0; right: 0; bottom: 0;
      background: #fff; border-radius: 22px 22px 0 0;
      padding: 10px 20px calc(22px + env(safe-area-inset-bottom));
      transform: translateY(100%); transition: transform 0.22s ease;
      max-height: 88vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
    }
    #gsOverlay.open .gs-sheet { transform: translateY(0); }
    .gs-handle {
      width: 40px; height: 4px; border-radius: 2px; background: var(--raja-tumma);
      margin: 0 auto 14px;
    }
    .gs-title {
      font-size: 1.25rem; font-weight: 700; text-align: center;
      color: var(--ink); margin-bottom: 18px;
    }
    .gs-row {
      display: flex; align-items: center; justify-content: space-between;
      padding: 12px 14px; background: #f7f8fc; border-radius: var(--radius-card);
      margin-bottom: 16px;
    }
    .gs-row-label { font-weight: 600; color: var(--ink); }
    .gs-toggle {
      width: 52px; height: 30px; border-radius: 15px; border: none;
      background: #d5d8e0; cursor: pointer; position: relative;
      transition: background 0.2s; flex-shrink: 0;
    }
    .gs-toggle::after {
      content: ''; position: absolute; top: 3px; left: 3px;
      width: 24px; height: 24px; border-radius: 50%; background: #fff;
      transition: transform 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.2);
    }
    .gs-toggle.on { background: var(--brand-primary); }
    .gs-toggle.on::after { transform: translateX(22px); }
    .gs-section { transition: opacity 0.2s; }
    .gs-section.gs-disabled { opacity: 0.4; pointer-events: none; }
    .gs-section-label {
      font-size: 0.8rem; font-weight: 600; color: var(--teksti-vaimea);
      text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px;
    }
    .gs-cards { display: flex; gap: 10px; }
    .gs-card {
      flex: 1; min-width: 0; padding: 14px 6px;
      background: #fff; border: 2px solid var(--border);
      border-radius: var(--radius-card); cursor: pointer;
      font-family: 'Poppins', -apple-system, sans-serif;
      display: flex; flex-direction: column; align-items: center; gap: 4px;
      transition: all 0.15s;
    }
    .gs-card:active { transform: scale(0.97); }
    .gs-card.selected {
      border-color: var(--brand-primary); background: var(--tint-purple);
    }
    .gs-card-icon { font-size: 26px; line-height: 1.1; }
    .gs-card-name { font-size: 0.9rem; font-weight: 700; color: var(--ink); }
    .gs-card-play { font-size: 0.7rem; color: var(--teksti-kevyt); }
    .gs-card.selected .gs-card-play { color: var(--brand-primary); font-weight: 600; }
    .gs-hint { font-size: 0.78rem; color: var(--teksti-hento); text-align: center; margin-top: 10px; }
    .gs-switch {
      width: 100%; margin-top: 10px; padding: 13px;
      background: none; border: none;
      color: #6b7280; font-size: 0.92rem; font-weight: 600; cursor: pointer;
      font-family: 'Poppins', -apple-system, sans-serif;
    }
    .gs-switch:hover { color: var(--ink); }

    .gs-done {
      width: 100%; margin-top: 20px; padding: 15px;
      background: var(--brand-gradient); color: #fff; border: none;
      border-radius: 12px; font-size: 1rem; font-weight: 700; cursor: pointer;
      font-family: 'Poppins', -apple-system, sans-serif;
    }

    .switch-player-btn {
      position: absolute;
      top: 12px;
      right: 12px;
      background: var(--bg);
      border: 1px solid var(--border);
      color: var(--ink);
      width: 40px;
      height: 40px;
      border-radius: 50%;
      font-size: 20px;
      line-height: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
      z-index: 10;
      padding: 0;
    }

    .switch-player-btn:hover {
      background: #eceef2;
      transform: scale(1.1);
    }

    .bite-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      gap: 20px;
      margin-bottom: 40px;
    }

    /* ===== POLKUNÄKYMÄ (B2, 2026-08-19) ==========================
       Solmut kappalejärjestyksessä, siksak-asettelu antaa polulle
       liikettä. Väri = tuoteväri, harmaa = ei vielä aloitettu,
       rengas = hallinta-%, 💎 = täydellinen. */
    .path-track {
      position: relative;
      display: block;
      max-width: 420px;
      margin: 0 auto;
      /* Tilaa alalaitaan kahdesta syystä: viimeisenkin kappaleen on voitava
         vierittyä bannerin alle, ja alanavigaatio on `position: fixed` eli
         se peittäisi muuten viimeisen solmun.

         HUOM. tässä oli `padding-bottom: 28vh` ENNEN lyhennemuotoa
         `padding: 8px 0 40px` — lyhenne nollasi sen, joten 28vh ei koskaan
         ollut voimassa. Nyt kaikki on yhdessä säännössä. */
      /* Molemmat päät mitoitetaan ajossa (mitoitaPolku js/game-bitemap.js):
         ylhäällä tarvitaan tasan bannerin verran, alhaalla tasan sen verran
         että viimeinen kappale yltää bannerin tasalle. Kiinteä `24vh` oli
         liikaa lyhyelle kirjalle: neljän solmun polku vieritti 200 px
         tyhjää, vaikka kaikki mahtui ruudulle. */
      padding: 8px 0 var(--polku-ala, 100px);
    }

    /* ===== KAPPALEBANNERI (B2, 19.8.) =====================
       Duolingon malli: jakso alkaa omalla palkillaan ja koko jakson
       solmut jakavat sen värin. Kertoo MITÄ kappaleessa opitaan. */
    /* Lataamaton kappale: sisällysluettelo tietää sen olevan olemassa, mutta
   sisältöä ei ole. Katkoviiva on universaali paikanvaraaja — se kertoo
   "tähän voisi tulla jotain" ilman ikonia tai selitystekstiä. Ei väriä,
   koska tuoteväri merkitsee ansaittua sisältöä. */
.path-unit-tyhja {
  background: transparent;
  border: 2px dashed var(--line, #d8d2e4);
  box-shadow: none;
  cursor: default;
  opacity: .72;
}
.path-unit-tyhja .path-unit-label,
.path-unit-tyhja .path-unit-title { color: var(--ink-faint, #8A8298); }
.path-unit-tyhja .path-unit-fill { display: none; }

/* Kappaleen painopiste sisällysluettelosta — pieni, hillitty, ei versaalia.
   Tämä on aikuisen metatietoa: se kertoo mitä kirja opettaa kappaleessa, ei
   mitä oppija saavuttaa. Siksi se ei kilpaile otsikon kanssa. */
.path-unit-meta {
  font-size: 11.5px;
  line-height: 1.35;
  margin-top: 3px;
  color: var(--ink-faint, #8A8298);
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
}
/* Ladatulla kappaleella on väripohja, jolloin harmaa katoaa siihen (Markon
   havainto 20.8.). Sävy tulee samasta --node-ink:stä kuin otsikko, vain
   himmeämpänä — se pysyy toissijaisena mutta luettavana kummallakin pohjalla. */
.path-unit:not(.path-unit-tyhja) .path-unit-meta {
  color: var(--node-ink, #fff);
  opacity: 0.85;
}

/* Aikuiselle sama katkoviiva mutta toimintakelpoinen: reunus vahvistuu ja
   oikeaan reunaan tulee +. Lapselle nappia ei renderöidä lainkaan — häntä ei
   pyydetä tekemään jotain mitä hän ei voi tehdä. */
.path-unit-tyhja-admin { border-color: var(--brand, var(--brand-primary)); opacity: 1; }
.path-unit-tyhja-admin .path-unit-label,
.path-unit-tyhja-admin .path-unit-title { color: var(--ink-soft, #574F66); }
/* LAPSEN PYYNTO KATKOVIIVASOLMUSTA (Marko 26.9.2026). Aikuisen ➕ on tayttä
   brandivaria — se on TEKO joka valmistuu heti. Tama on PYYNTO jonka joku muu
   lunastaa, joten se on aariviiva: sama ero kuin `.koe-kohta-aihe`n ja
   ehdotusnapin valilla. ⚠️ Ei pilleri (UI-periaate 4). */
.path-unit-pyyda {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 7px 12px;
  border: 2px solid var(--brand, var(--brand-primary));
  border-radius: 10px;
  background: #fff;
  color: var(--brand, var(--brand-primary));
  font: inherit; font-size: .82rem; font-weight: 700;
  cursor: pointer;
}
.path-unit-pyyda.kuitattu {
  border-color: var(--onnistui);
  color: var(--onnistui);
  cursor: default;
}

.path-unit-lisaa {
  margin-left: auto;
  width: 34px; height: 34px; flex: 0 0 auto;
  border: none; border-radius: 50%;
  background: var(--brand, var(--brand-primary)); color: #fff;
  font-size: 20px; line-height: 1; font-weight: 600;
  cursor: pointer;
}

/* KAPPALELOHKO + TARTTUVA BANNERI (20.8.2026, malli Duolingosta).
   Banneri pysyy ruudulla koko sen kappaleen ajan ja väistyy vasta kun
   seuraava kappale työntää sen pois. Kolme asiaa kerralla: pelaaja tietää
   aina missä kappaleessa on, ruudulla on jatkuvasti kappaleen väri, ja
   pitkä polku rauhoittuu kun jokin pysyy paikallaan.
   Tarttumiskohta on yläpalkin ALAREUNA — korkeus mitataan JS:ssä
   muuttujaan --bm-topbar-h, koska se elää fontin ja turva-alueen mukaan. */
    .path-section { position: relative; }
    /* Kiinteä palkki on poissa normaalivirrasta — sisältö tarvitsee saman
       verran ylätilaa, tai ensimmäinen solmu jää sen alle. */
    /* Turva-alue vähennetään: se on jo bodyn pehmusteessa, ja
       --bm-topbar-h sisältää sen palkin korkeudessa. Ilman tätä
       ensimmäinen solmu valuisi turva-alueen verran liian alas. */
    #biteMapScreen { padding-top: calc(var(--bm-topbar-h, calc(58px + var(--norsu-turva-yla))) + 18px - var(--norsu-turva-yla)); }

    /* Ensimmäisellä kappaleella ei ole erotinta — se lukee bannerissa. Sen
       ensimmäinen solmu tarvitsee siksi oman ylätilansa. */
    .path-section:first-child { padding-top: 6px; }

    /* YKSI BANNERI, KIINTEÄ PAIKKA (21.8.2026).
       Aiempi versio teki jokaisesta kappaleesta oman tarttuvan bannerin, ja
       ruudulla saattoi olla kaksi paksua väripalkkia yhtä aikaa. Marko
       havaitsi sen levottomaksi ja osoitti Duolingon ratkaisun: paksu palkki
       on tasan yksi, aina samassa paikassa, ja seuraavat kappaleet esitellään
       ohuella erottimella. Banneri on nyt polun ULKOPUOLELLA oma elementtinsä,
       ja sen sisältö vaihtuu vieritettäessä. */
    /* .path-unit määritellään alempana ja asettaa position: relative.
       Sama tarkkuus (0,1,0) → myöhempi voittaa, ja banneri jäi vieriväksi.
       Kaksi luokkaa nostaa tarkkuuden 0,2,0:aan. (Mitattu 21.8.) */
    .path-unit.path-banner {
      /* NAULATTU, EI TARTTUVA (22.8.2026, Markon linjaus). `sticky` alkaa
         virrasta ja nousee paikalleen vasta kun sen ohi on vieritetty —
         banneri siis liikkui kahden ääriasennon väliä. Se on OTSIKKO: sen
         paikka ei saa riippua siitä missä kohtaa polkua ollaan.

         Mahdollista vasta kun `.screen`in animaatiosta poistui transform;
         sitä ennen `fixed` olisi asemoitunut ruudun sijaan siihen. */
      position: fixed;
      /* MAALAUS POIS (24.8.2026, Markon havainto). Pitkä painallus valitsi
         bannerin tekstin ja jätti sinisen maalauksen ruudulle — ja koska
         pitkä painallus on nyt bannerin oma ele, maalaus syntyi joka
         kerta. `touch-callout` estää lisäksi iOS:n oman valikon.
         (Sama korjaus tehtiin solmuille 21.8., rivi ~1083.) */
      -webkit-user-select: none;
      user-select: none;
      -webkit-touch-callout: none;
      /* Varafallback sisältää turva-alueen: jos mittaus ei ole vielä
         ehtinyt ajaa, arvaus on silti oikeassa kokoluokassa. */
      top: calc(var(--bm-topbar-h, calc(58px + var(--norsu-turva-yla))) + 2px);
      left: 50%;
      width: min(892px, calc(100% - 8px));
      transform: translateX(-50%);
      z-index: 30;
      margin: 0;
    }

    /* Naulattu banneri on poissa virrasta — polku tarvitsee sen verran
       ylätilaa. Korkeus mitataan (--bm-banneri-h), koska otsikko voi olla
       yhdellä tai kahdella rivillä ja se vaihtuu vieritettäessä. */
    .path-track { padding-top: var(--polku-yla, 123px); }

    /* Ohut erotin: kertoo mitä seuraavaksi tulee viemättä tilaa väriltä.
       Viivat kummallakin puolella tekevät siitä väliotsikon eivätkä palkin —
       ero on se, ettei se yritä olla toinen banneri. */
    .path-divider {
      display: flex;
      align-items: center;
      gap: 12px;
      /* Alaväli isompi kuin yläväli: erotin kuuluu SEURAAVAAN kappaleeseen,
         ja sen ensimmäinen solmu tarvitsee saman väljyyden kuin bannerin alla. */
      margin: 30px 8px 26px;
      color: var(--ink-faint, #8A8298);
      font-size: 13px;
      font-weight: 700;
      text-align: center;
    }
    /* KIRJANVAIHTO (teema 3, 23.8.2026). Kappale-erotin on väliotsikko;
       tämä on jakson vaihto, ja sen pitää lukea vahvemmin — muuten
       toisen kirjan kappale 1 näyttää tämän kirjan kappaleelta.
       Ero tehdään painolla ja välillä, ei värillä: väri polulla on
       varattu kappalenumerolle. */
    .path-kirja {
      display: flex;
      align-items: center;
      gap: 12px;
      margin: 44px 8px 8px;
      color: var(--brand-primary, #4A2E6F);
      font-size: 12px;
      font-weight: 800;
      letter-spacing: .08em;
      text-transform: uppercase;
    }
    .path-kirja::before,
    .path-kirja::after {
      content: '';
      flex: 1;
      height: 2px;
      border-radius: 2px;
      background: var(--brand-primary, #4A2E6F);
      opacity: .22;
    }
    .path-kirja span {
      flex: 0 1 auto;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* SARJAN VAIHTUMINEN on isompi tapahtuma kuin kirjan vaihtuminen sarjan
       sisällä. Ero tehdään painolla ja välillä, ei uudella värillä.
       Modifier heti perusluokan jälkeen — sama lohko, ei toista komponenttia
       kilpailemassa samalla tarkkuudella (ks. CLAUDE.md CSS-järjestys). */
    .path-kirja.on-sarja {
      margin-top: 64px;
      font-size: 13px;
    }
    .path-kirja.on-sarja::before,
    .path-kirja.on-sarja::after {
      height: 3px;
      opacity: .38;
    }
    /* Sarjan nimi omalle rivilleen kirjan nimen yläpuolelle. Näkyy VAIN kun
       kirjan nimi ei jo kerro sarjaa (ks. norsuSarjaNimessa) — muuten tässä
       lukisi "HIGH FIVE!" ja sen alla "High Five! 4". */
    .path-kirja-sarjanimi {
      display: block;
      font-size: 10.5px;
      font-weight: 700;
      letter-spacing: .12em;
      opacity: .62;
      margin-bottom: 2px;
    }
    .path-kirja-nimi { display: block; }

    .path-divider::before,
    .path-divider::after {
      content: '';
      flex: 1;
      height: 1px;
      background: var(--border, #E8E4EE);
    }
    .path-divider span {
      flex: 0 1 auto;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* Mallikirja: brändivioletti pinta, kulta VAIN kirjan nimessä.
       BRAND.md:n oma pari (violetti pinta + keltainen sisältö), kontrasti 7,7:1.

       KULTA RAJATTU YHTEEN PAIKKAAN 20.8.2026 (Markon havainto: "ne näyttävät
       ehkä vähän randomeilta, varsinkin tuo prosentti").

       Sääntö johon päädyttiin: **sama merkitys näyttää samalta joka kirjassa;
       vain identiteetti vaihtelee.** Prosentti tarkoittaa kappaleen hallintaa
       jokaisessa kirjassa ja käyttää siksi musteen väriä kaikkialla — kultaisena
       se olisi väittänyt olevansa eri asia kuin sama luku muualla. Sama koskee
       täyttyvää palkkia ja solmujen kehää: ne mittaavat edistymistä, eivät
       kerro kenen kirja on kyseessä.

       Kirjan nimi on ainoa rivi, jonka tehtävä ON identiteetti — ja siksi ainoa
       paikka jossa kulta sanoo jotain: tämä on Norsun oma kirja. Yksi käyttö
       yhdellä säännöllä ei ole satunnaisuutta; kaksi käyttöä eri merkityksillä
       oli. */
    .path-unit-demo .path-unit-book {
      color: var(--brand-highlight);
      opacity: 1;
    }

    .path-unit-book {
      font-size: 10.5px;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      opacity: 0.72;
      margin-bottom: 1px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .path-unit {
      position: relative;
      z-index: 2;
      display: flex;
      align-items: center;
      gap: 12px;
      background: var(--node-color);
      color: var(--node-ink, #fff);
      border-radius: 16px;
      padding: 14px 18px 18px;
      margin: 26px 4px 22px;
      box-shadow: 0 4px 0 var(--node-rim, rgba(0, 0, 0, 0.18)),
                  inset 0 2px 0 rgba(255, 255, 255, 0.22);
    }
    .path-unit-label {
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      opacity: 0.85;
    }
    .path-unit-title {
      font-size: 17px;
      font-weight: 700;
      line-height: 1.3;
      margin-top: 2px;
    }
    .path-unit-text { flex: 1; min-width: 0; position: relative; z-index: 1; }

    /* Kappaleen mittari (19.8.).
       ENSIMMÄINEN YRITYS oli täyttää koko banneri vasemmalta oikealle —
       Marko kysyi heti oikean kysymyksen: "miten siinä sitten teksti näkyisi?"
       Ei mitenkään: teksti jää osittain täydelle värille ja osittain vaalealle
       pohjalle, eikä yksi tekstiväri toimi molemmilla. Mitattuna se hävisi.

       Mittari siirrettiin bannerin ALAREUNAAN omaksi palkikseen. Teksti ei
       koskaan ole sen päällä, banneri pysyy yhtenä värinä (jolloin
       readableInk takaa kontrastin), ja täyttyminen näkyy silti. */
    .path-unit { overflow: hidden; }
    .path-unit-fill {
      position: absolute;
      left: 0;
      bottom: 0;
      height: 6px;
      width: calc(var(--unit-conf, 0) * 1%);
      background: currentColor;
      opacity: 0.9;
      border-radius: 0 3px 3px 0;
      transition: width 0.5s ease;
    }
    /* Palkin ura: kertoo että mittarissa on matkaa jäljellä */
    .path-unit::after {
      content: '';
      position: absolute;
      left: 0; right: 0; bottom: 0;
      height: 6px;
      background: currentColor;
      opacity: 0.22;
    }
    .path-unit-fill { z-index: 1; }
    .path-unit-pct {
      position: relative;
      z-index: 2;
      font-size: 13px;
      font-weight: 700;
      color: inherit;
      opacity: 0.85;
      flex: 0 0 auto;
    }

    /* T4c (21.8.2026): bannerin oikea reuna on nyt SANAKORTIT, ei play.
       Sanakortit koskevat juuri sitä kappaletta jonka banneri näyttää, joten
       ne kuuluvat banneriin. Play painetaan joka istunnossa ja se siirtyi
       peukalon ulottuville alanavigaation keskelle.

       Ulkoasu peritään suoraan entiseltä play-napilta: käänteinen täyttö
       (`--node-ink` pohjaksi, `--node-color` merkiksi) toimii sekä tummalla
       että vaalealla kappalevärillä, koska molemmat on jo laskettu
       readableInk():llä. */
    .path-unit-kortit {
      flex: 0 0 auto;
      width: 44px; height: 44px;
      display: flex; align-items: center; justify-content: center;
      border-radius: 50%;
      border: none;
      background: var(--node-ink, #fff);
      font-size: 20px;
      text-decoration: none;
      cursor: pointer;
      box-shadow: 0 3px 0 var(--node-rim, rgba(0, 0, 0, 0.25));
      transition: transform 0.12s, box-shadow 0.12s;
    }
    .path-unit-kortit:active {
      transform: translateY(2px) scale(0.96);
      box-shadow: 0 1px 0 var(--node-rim, rgba(0, 0, 0, 0.25));
    }
    .path-unit-zero .path-unit-kortit { background: #ede6f8; }

    /* Vanha bannerin play-nappi (poistettu käytöstä T4c:ssä) */
    .path-unit-play {
      flex: 0 0 auto;
      width: 44px;
      height: 44px;
      border-radius: 50%;
      /* KÄÄNTEINEN TÄYTTÖ, EI LÄPIKUULTAVA (20.8.2026, Markon havainto:
         "play-nappi ei toimi violetilla taustalla, koska molemmat on
         violetteja").

         Tässä oli `rgba(255,255,255,0.16)` eli 16 % valkoista pohjavärin
         päällä. Keskisävyisellä oranssilla se vaalenee näkyvästi, mutta
         tummalla se ei: var(--brand-primary) + 16 % valkoista on yhä violetti. Sama vika
         koski myös kappale 1:n mustaa ja kappale 8:n ruskeaa — mallikirja vain
         paljasti sen.

         Nyt nappi on TÄYTETTY musteen värillä ja nuoli on pohjan värinen.
         Molemmat on jo laskettu (readableInk), joten käännös toimii
         automaattisesti kummallakin: tummalla pohjalla valkoinen nappi ja
         tumma nuoli, vaalealla päinvastoin. */
      border: none;
      background: var(--node-ink, #fff);
      color: var(--node-color);
      font-size: 16px;
      cursor: pointer;
      /* Kohotettu reuna kuten solmuilla. Kaksi tehtävää: nappi puhuu samaa
         kolmiulotteista kieltä kuin muu polku, JA se erottuu myös vaaleilla
         pohjaväreillä — valkoinen oranssilla jää muuten kontrastiin 2,76:1,
         alle 3:1 -ohjearvon. Varjo ei ole koriste vaan se mikä nostaa
         kontrastin riittäväksi. */
      box-shadow: 0 3px 0 var(--node-rim, rgba(0, 0, 0, 0.25));
      transition: transform 0.12s, box-shadow 0.12s;
    }
    .path-unit-play:active {
      transform: translateY(2px) scale(0.96);
      box-shadow: 0 1px 0 var(--node-rim, rgba(0, 0, 0, 0.25));
    }
    .path-unit-zero .path-unit-play { background: #ede6f8; color: var(--brand-primary); }

    /* Kappale 0 (ruotsin orientaatio): tuotevärissä ei ole nollaa, ja
       "nollan väri" olisi valkoinen — näkymätön valkoisella pohjalla.
       Valkoinen täyttö + vahva kehys ratkaisee näkyvyyden muuttamatta
       väriperiaatetta.

       KEHYSVÄRI: BRÄNDIVIOLETTI (palautettu 21.8.2026).
       Siirsin sen 20.8. turkoosiksi peläten törmäystä mallikirjan violetin
       kanssa. Marko oikaisi: törmäystä ei ole, koska **ero on muodossa eikä
       sävyssä** — mallikirja on täytetty violetti kultaisin yksityiskohdin,
       orientaatiokappale valkoinen violetein äärivivoin. Turkoosi taas ei
       liittynyt Merkitysten-brändiin mitenkään.

       3D-käsittely kuten muillakin: värillinen reuna + yläpinnan vaalennus. */
    .path-unit-zero, .path-zero { --zero-color: #450693; --zero-rim: #2c0460; }

    .path-unit-zero {
      background: #fff;
      color: var(--brand-primary);
      border: 3px solid var(--zero-color);
      box-shadow: 0 4px 0 var(--zero-rim),
                  inset 0 2px 0 rgba(255, 255, 255, 0.9);
    }
    .path-zero .path-btn {
      /* background-lyhenne nollaisi .path-btn:n liukuvärin, joten se
         asetetaan tässä uudelleen — muuten kappale 0 jäisi litteäksi. */
      background: #fff;
      background-image: linear-gradient(180deg,
                        rgba(255, 255, 255, 0.95) 0%,
                        rgba(240, 235, 250, 0.5) 55%,
                        rgba(0, 0, 0, 0.06) 100%);
      color: var(--brand-primary);
      border: 3px solid var(--zero-color);
      box-shadow: 0 5px 0 var(--zero-rim),
                  inset 0 2px 0 rgba(255, 255, 255, 0.9),
                  inset 0 -3px 6px rgba(0, 0, 0, 0.06);
    }
    .path-zero .path-btn:active {
      box-shadow: 0 1px 0 var(--zero-rim),
                  inset 0 2px 0 rgba(255, 255, 255, 0.7);
    }
    .path-zero .path-ring {
      background: conic-gradient(
        var(--zero-color) calc(var(--conf) * 1%),
        #e6e8ec 0
      );
    }
    /* KOLMEPORTAINEN HIERARKIA, KIRJOITETTU AUKI (21.8.2026).
       `.path-zero` (kappaleen identiteetti) ja `.path-untouched` (edistymisen
       tila) ovat VERTAISIA luokkia — kumpikin voi olla päällä yhtä aikaa, eikä
       lukija voi päätellä kumpi voittaa, jos molemmat ovat 0-2-0.

       Aiemmin voittajan ratkaisi pelkkä järjestys. Nyt tarkkuus kertoo sen:

         0-2-0  .path-zero .path-btn                    kappale 0, pelattu
         0-3-0  .path-node.path-untouched .path-btn     mikä tahansa, ei vielä
         0-4-0  .path-node.path-zero.path-untouched     kappale 0, ei vielä

       Sääntö: **edistymisen tila voittaa identiteetin.** "Harmaa on ei vielä"
       koskee myös kappale 0:aa; sen valkoinen ilme ansaitaan pelaamalla. */
    .path-node.path-zero.path-untouched .path-btn {
      color: #9aa1ab;
      border-color: #d8dce2;
    }

    .path-node {
      position: relative;
      z-index: 1;          /* solmut viivojen päälle — viivat lisätään DOMiin
                              solmujen jälkeen, joten ilman tätä ne peittäisivät
                              numerot ja otsikot */
      /* Laatikko renkaan levyinen (mitattu 19.8.): nimi ankkuroituu
         `right/left: 100%` eli laatikon reunaan, joten 116 px laatikko
         työnsi nimen 16 px liian kauas ja ulos ruudusta. */
      /* Laatikko renkaan levyinen — ks. kommentti yllä. Muuttui 84 → 78
         yhdessä renkaan kanssa, muuten nimi irtoaisi kiekosta. */
      width: 78px;
      /* Kiekko madaltui 84 → 68, joten pystyväli kasvatettiin 34 → 40:
         siksakin pitää yhä lukea polkuna eikä pinona. */
      margin: 14px auto 40px;
      text-align: center;
    }
    /* Siksak: joka toinen solmu sivuun keskilinjasta */
    /* Siirtymä pienennetty 52 → 40 px: nimi tarvitsee ulkoreunalta tilan,
       ja mitattuna 52 px työnsi nimen ulos ruudusta 375 px leveydellä. */
    .path-left  { transform: translateX(-40px); }
    .path-right { transform: translateX(40px); }

    /* Yhdysviiva: piirretään JS:llä mittaamalla solmujen keskipisteet
       (ks. piirraPolkuviivat). CSS-kierto ei riittänyt, koska otsikot
       rivittyvät eri korkuisiksi eikä väliä voi tietää etukäteen. */
    .path-link {
      position: absolute;
      height: 5px;
      background: #e0e3e8;
      border-radius: 3px;
      transform-origin: left center;
      z-index: 0;
      pointer-events: none;
    }
    /* Kuljettu osuus värittyy — Markon havainto: polku värjääntyy */
    .path-link-done { background: var(--brand-accent); }

    /* Hallintarengas: conic-gradient piirtää --conf prosenttia kappaleen
       väriä ja lopun harmaata. Numerot symbolin taakse — rengas kertoo
       tason ilman prosenttilukua. */
    /* SOLMU ON ELLIPSI, EI YMPYRÄ (20.8.2026, Markon havainto).
       "Siihen 3D-mäisyyteen tarvitaan vielä perspektiiviä, eli että se solmu
       on hiukan lituska." Aivan oikein: reunavarjo yksin näyttää vain
       kohotetulta ympyrältä. Kun kiekko on leveämpi kuin korkeampi, se luetaan
       katsottavan VIISTOSTA ylhäältä — ja vasta siitä syntyy syvyys.
       Suhde säädetty 20.8. Markon silmällä: 84 × 68 (0,81) luki liian
       littanana, 78 × 68 (0,87) on lituska mutta ei puristettu.
       border-radius: 50% tuottaa ellipsin automaattisesti kun laatikko ei ole
       neliö, joten conic-gradient (hallinta-%) toimii ennallaan. */
    /* Pitkä painallus avaa tiedot, joten teksti ei saa maalautua eikä iOS
       saa avata omaa valikkoaan (Markon havainto 22.8.2026: "norsu
       maalautui"). */
    .path-btn, .path-ring, .path-node {
      -webkit-touch-callout: none;
      -webkit-user-select: none;
      user-select: none;
    }

    .path-ring {
      position: relative;
      width: 78px;
      height: 68px;
      margin: 0 auto;
      border-radius: 50%;
      background: conic-gradient(
        var(--node-color) calc(var(--conf) * 1%),
        #e6e8ec 0
      );
      padding: 6px;
      z-index: 1;
    }

    .path-btn {
      width: 100%;
      height: 100%;
      border-radius: 50%;
      border: none;
      background: var(--node-color);
      color: var(--node-ink, #fff);
      font-family: 'Poppins', -apple-system, sans-serif;
      font-size: 26px;
      font-weight: 700;
      cursor: pointer;
      /* 3D-nappi kahdesta osasta (20.8.2026, korjattu Markon havainnon jälkeen):
         "violetin alareunaa ei oikein erota".

         Reuna yksin ei riitä tummilla väreillä — mitattu: var(--brand-primary) saa reunalle
         kontrastin 1,25:1 ja musta 1,03:1, koska tummaa ei voi tummentaa.
         Erottelu tulee siksi YLÄPINNAN VALOSTA:

           1. liukuväri, joka on vaalea ylhäällä ja tummenee alas → kaareva pinta
           2. vaalea viiva pinnan ALAREUNASSA → terävä raja pinnan ja etureunan
              välillä, se sama jonka valo poimii oikeasta kiekosta
           3. tummempi reuna alla → kiekko on irti taustasta

         Vasta kolmen yhdistelmä lukee kohotettuna myös tummalla violetilla. */
      background-image: linear-gradient(180deg,
                        rgba(255, 255, 255, 0.26) 0%,
                        rgba(255, 255, 255, 0.04) 48%,
                        rgba(0, 0, 0, 0.12) 100%);
      box-shadow: 0 5px 0 var(--node-rim, rgba(0, 0, 0, 0.22)),
                  inset 0 -2px 0 rgba(255, 255, 255, 0.18),
                  inset 0 2px 0 rgba(255, 255, 255, 0.34);
      transition: transform 0.12s, box-shadow 0.12s;
    }
    /* Mitali on emoji, joten sen väriä ei voi muuttaa — mutta sen ympärille
       voi piirtää valon. drop-shadow seuraa emojin ÄÄRIVIIVAA (toisin kuin
       box-shadow, joka seuraisi laatikkoa), joten tuloksena on ohut valkoinen
       reunus. Se nostaa pronssin irti tummasta violetista, jota vasten se
       muuten näyttää tunkkaiselta. (Markon havainto 20.8.) */
    .path-btn-tier { font-size: 30px; line-height: 1; }

    /* Mitalin valkoinen reunus (20.8.2026, korjattu Markon havainnon jälkeen:
       "ei erotu ollenkaan").
       VIKA OLI KOHTEESSA: filter oli napissa, ja koska napilla on oma tausta,
       drop-shadow seurasi NAPIN ellipsiä eikä emojin ääriviivaa — reunus
       piirtyi napin taakse, missä sitä ei näe. Emoji tarvitsee oman
       läpinäkyvän elementin.
       Neljä kerrosta samaa valkoista tekee reunuksesta paksun mutta pehmeän;
       yksi paksu drop-shadow tuottaisi kovan sädekehän. */
    .tier-merkki {
      display: inline-block;
      /* Kaksi kerrosta, ei neljä (Markon havainto 20.8.: "näyttää
         sädekehältä"). Yksi terävä valkoinen ääriviiva + yksi tumma alla,
         joka antaa irtonaisuuden. Neljä kerrosta levitti valon niin laajalle
         että se luki hehkuna eikä reunuksena. */
      filter: drop-shadow(0 0 1.2px #fff)
              drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.45));
    }
    /* MATERIAALI VÄRINÄ (21.8.2026, korjattu 22.8.2026).
       Eilinen versio piirsi materiaalin KEHÄNÄ emojin ympärille, koska
       oletin ettei emojin väriä voi muuttaa. Voi: `background-clip: text`
       leikkaa gradientin glyfin muotoon, ja tekniikka on ollut koodissa
       17.8. alkaen `.login-title`issa.

       Nyt norsu itse on puuta, kiveä, pronssia, hopeaa, kultaa tai
       timanttia — yksi glyfi, kuusi täyttöä. Juuri se mitä asteikolta
       haluttiin. Gradientti (ei tasainen väri) antaa materiaalille
       pinnan: metalli ilman valon vaihtelua näyttää tarralta. */
    /* METALLIT SAAVAT KIILLON, LUONNONMATERIAALIT EIVÄT (22.8.2026).
       Markon havainto: "puu ja pronssi näyttää melkein samalta". Piti
       paikkansa — molemmat olivat keskiruskeita kaksipysähdyksisiä
       liukuvärejä, ja pelkkä sävyero ei riitä 40 pikselin kokoisena.

       Ero ei ole väri vaan PINTA. Metallissa on terävä kiiltoraita, koska
       se heijastaa valon yhdestä suunnasta; puu ja kivi hajottavat valon
       eivätkä kiillä. Se erottaa ne rakenteellisesti, ei nyanssilla — ja
       rakenne kestää pienenkin koon.

       Puu myös vaalennettiin ja keltaisennettiin (mänty), pronssi
       syvennettiin punaisemmaksi (kupari). */
    .tier-merkki.tier-puu {
      --tier-g:     var(--tier-puu-g);
      --tier-pinta: var(--tier-puu-pinta);
      --tier-sek:   var(--tier-puu-sek);
    }
    .tier-merkki.tier-kivi {
      --tier-g:     var(--tier-kivi-g);
      --tier-pinta: var(--tier-kivi-pinta);
      --tier-sek:   var(--tier-kivi-sek);
    }
    .tier-merkki.tier-pronssi {
      --tier-g:     var(--tier-pronssi-g);
      --tier-pinta: var(--tier-pronssi-pinta);
      --tier-sek:   var(--tier-pronssi-sek);
    }
    .tier-merkki.tier-hopea {
      --tier-g:     var(--tier-hopea-g);
      --tier-pinta: var(--tier-hopea-pinta);
      --tier-sek:   var(--tier-hopea-sek);
    }
    .tier-merkki.tier-kulta {
      --tier-g:     var(--tier-kulta-g);
      --tier-pinta: var(--tier-kulta-pinta);
      --tier-sek:   var(--tier-kulta-sek);
    }
    .tier-merkki.tier-timantti {
      --tier-g:     var(--tier-timantti-g);
      --tier-pinta: var(--tier-timantti-pinta);
      --tier-sek:   var(--tier-timantti-sek);
    }

    /* ⚠️ PINTAKUVIOT OVAT NYT KÄYTÖSSÄ (2.9.2026), ja niiden lähde on
       js/tasonorsut.js — EI tämä tiedosto. Alla olevat kuusi lohkoa vain
       osoittavat sinne; älä kirjoita niihin arvoja.

       ⚠️ ÄLÄ KÄYTÄ `background`-LYHENNETTÄ. Se nollaa `background-clip`in
       arvoon `border-box`, jolloin norsun tilalle tulee neliö. Mitattu.

       Tässä luki 22.8.2026 asti että kuvio kannattaa kytkeä vain
       juhlaruutuun koska "40 px:n solmussa siitä tulee keskiarvoväriä".
       Se pitää yhä paikkansa — mutta se ei ole syy jättää kuviota pois
       pienistä koista, koska keskiarvoväri ei ole vika vaan sama väri
       jonka gradientti antaisi muutenkin. */

.tier-merkki[class*="tier-"] {
      /* Pintakuvio glyfille samalla logiikalla kuin juhlaruudun pohjalle:
         puu ja kivi saavat rakenteen, metallit kiiltävät jo liukuvärissään.
         ⚠️ `background-image` eikä `background` — lyhenne nollaisi
         `background-clip: text`in ja norsun tilalle tulisi neliö. */
      /* ⚠️ TAUSTA ON `::before`ssa (2.9.2026) — ks. perustelu alempana.
         Tässä oli ennen `background-image`, `-size`, `-repeat`,
         `-position` ja `-blend-mode`; ne siirtyivät kokonaisuutena, koska
         puolikas siirto olisi jättänyt kaksi lähdettä samalle asialle.
         COVER eikä tiili: toistettuna syntyi näkyviä rajoja, metalliin
         raitoja ja kiteeseen neliöitä. Metallit soft-light,
         luonnonmateriaalit overlay — ks. js/tasonorsut.js */
      /* ⚠️ MASKI, EI `background-clip: text` (2.9.2026).
         Tässä luki 22.8. alkaen että emoji + leikkaus riittää eikä omaa
         SVG:tä tarvita — ja se piti paikkansa VÄRITETTÄVYYDEN osalta.
         Se ei ratkaissut sitä mitä Marko pyysi: emoji on laitteen
         piirtämä, joten tasonorsu oli eri muotoinen jokaisella laitteella.
         Maski ottaa muodon `icons/norsu.svg`:stä; pinta ja gradientti
         pysyvät ennallaan taustakerroksina.
         ⚠️ `font-size` ei enää luo laatikkoa — `width/height: 1em` tekee
         sen, joten kaikki olemassa olevat kokosäännöt (34/46/150–240 px)
         pätevät ennallaan. */
      display: inline-block;
      position: relative;
      width: 1em;
      height: 1em;
      vertical-align: -0.12em;
      /* ⚠️ MASKI ::before-KERROKSEEN, EI ELEMENTTIIN ITSEENSA (2.9.2026).
         Marko: *"nakyva nelio ymparilla kaikkialla."*

         Kun maski ja `filter` ovat SAMASSA elementissa, WebKit laskee
         varjon ennen maskia: `drop-shadow(0 0 1.2px #fff)` piirtyy
         LAATIKON muotoon, ja tulos on vaalea nelio norsun ymparilla.
         Chrome maskaa ensin, joten se ei nay siella lainkaan — ja siksi
         tama loytyi vasta Markon kuvasta.

         Nyt tausta ja maski ovat `::before`ssa ja `filter` on
         vanhemmassa, jolloin varjo laskee jo maskatusta sisallosta ja
         seuraa norsun muotoa. */
      background-image: none;
      /* Elementissä on yhä 🐘 saavutettavuutta varten — piiloon. */
      color: transparent;
      text-indent: -9999px;
      overflow: hidden;
      /* Ääriviiva erottaa norsun solmun taustasta. drop-shadow seuraa
         GLYFIN muotoa — box-shadow seuraisi laatikkoa. */
      filter: drop-shadow(0 0 1.2px rgba(255,255,255,0.95))
              drop-shadow(0 1px 1.5px rgba(0,0,0,0.35));
    }
    /* Pinta, liukuvari ja maski omaan kerrokseensa — ks. perustelu yllä. */
    .tier-merkki[class*="tier-"]::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image: var(--tier-pinta, none), var(--tier-g);
      background-size: cover, cover;
      background-repeat: no-repeat, no-repeat;
      background-position: center, center;
      background-blend-mode: var(--tier-sek, overlay), normal;
      -webkit-mask-image: var(--norsu-kuva);  mask-image: var(--norsu-kuva);
      -webkit-mask-size: contain;             mask-size: contain;
      -webkit-mask-repeat: no-repeat;         mask-repeat: no-repeat;
      -webkit-mask-position: center;          mask-position: center;
    }
    /* Timantti saa hohteen: se on huipputaso ja saa näyttää siltä. */
    /* VALKOINEN NORSU: "ei vielä yhtään" (23.8.2026, Marko: "Tuo haalea
       emojinorsu ei ole brändin mukainen tässä kohtaa").
       Läpinäkyvyydellä haalennettu emoji on emoji jonka päälle on kaadettu
       vettä — se näyttää rikkinäiseltä, ei tulevalta. Valkoinen siluetti on
       sama tekniikka kuin muillakin tasoilla, ja se lukee lupauksena:
       norsu on jo tuolla, se ei vain ole vielä saanut materiaaliaan. */
    .tier-merkki.tier-tyhja {
      --tier-g: linear-gradient(150deg, #FFFFFF 0%, #F4F2F8 45%, #DED8EA 100%);
      filter: drop-shadow(0 1px 2px rgba(0,0,0,0.28));
    }
    /* HARMAA NORSU: polun solmu jolla ei vielä ole tasonorsua (22.9.2026, Marko: *«muuta se
       polun tähtikuvake harmaaksi norsuksi»*). Sama maskattu muoto kuin tasonorsuilla, sävy
       samoista harmaista kuin ★ oli. Eri kuin `tier-tyhja` (valkoinen, tulosruudun värillinen
       pohja): polun solmu on vaalea, ja valkoinen katoaisi siihen. */
    .tier-merkki.tier-ei-viela {
      --tier-g: linear-gradient(150deg, var(--teksti-hento) 0%, var(--teksti-vaimea) 100%);
      filter: drop-shadow(0 1px 1px rgba(255,255,255,0.7));
    }
    .tier-merkki.tier-timantti {
      filter: drop-shadow(0 0 2px rgba(255,255,255,0.95))
              drop-shadow(0 0 6px rgba(79,195,218,0.55))
              drop-shadow(0 1px 1.5px rgba(0,0,0,0.3));
    }

    .path-btn:active {
      transform: translateY(4px);
      box-shadow: 0 1px 0 var(--node-rim, rgba(0, 0, 0, 0.22)),
                  inset 0 -1px 0 rgba(255, 255, 255, 0.20),
                  inset 0 2px 0 rgba(255, 255, 255, 0.20);
    }

    /* Ei vielä aloitettu = harmaa. Harmaa on Norsussa "ei vielä",
       ei "kuollut" — siksi se on vaalea eikä tumma. */
    /* Harmaan solmun rengas oli #e6e8ec ja nappi #e2e8f0 — kontrasti 1,00,
       eli TÄSMÄLLEEN sama vaaleus. Ne sulautuivat yhdeksi muodoksi, jolloin
       silmä luki solmun leveydeksi RENKAAN 78 px eikä napin 66 px. Värillisillä
       nappi erottuu renkaastaan kontrastilla 10,07, joten niissä silmä lukee
       napin. Sama geometria, 12 px eri havainto — Markon "onko harmaa leveämpi"
       oli oikea havainto, ei silmänpetos. (Mitattu 20.8.2026.)

       Korjaus kahdesta osasta: rengas vaalenee taaemmas JA napin ympärille
       tulee ohut valkoinen viiva. Sama keino kuin mitalilla: kun kaksi asiaa
       ovat samanvärisiä, väliin tarvitaan valoa. */
    /* ID-tason etuliite `.path-node`, jotta nämä voittavat `.path-zero`-säännöt
       tarkkuudella eivätkä järjestyksellä. Ks. selitys `.path-zero`-lohkossa. */
    .path-node.path-untouched .path-ring { background: #eef1f6; }
    .path-node.path-untouched .path-btn {
      /* Pinta oli #f2f4f7 — kontrasti valkoiseen sivuun 1,10:1, käytännössä
         näkymätön. Nyt hitusen syvempi, jolloin kiekolla on oma pinta jonka
         päällä valo ja varjo voivat näkyä. */
      background: #e2e8f0;
      /* Sama kolmiulotteisuus kuin väreillisillä (Markon havainto 20.8.):
         harmaa on "ei vielä", ei "ei ole". Litteä harmaa napin näköinen
         asia keskellä kohotettuja luki rikkinäisenä, ei odottavana. */
      /* Mitattu 19.8.: #9aa1ab antoi kontrastiksi 2.36 — alle 3.0:n rajan
         isolle tekstille. Harmaa saa viestiä "ei vielä", mutta kappaleen
         numeron pitää silti olla luettavissa. */
      color: #656D78;
      /* Liukuväri oli ylhäältä lähes valkoinen (0.95), jolloin kiekon YLÄOSA
         suli valkoiseen sivuun ja näkyviin jäi vain alapuolisko — siksi
         harmaa näytti muita lituskammalta vaikka mitat ovat samat.
         (Markon havainto 20.8.) Nyt vaalennus on hillitty, ja pinta pysyy
         omana pintanaan koko korkeudeltaan. */
      background-image: linear-gradient(180deg,
                        rgba(255, 255, 255, 0.55) 0%,
                        rgba(255, 255, 255, 0.10) 50%,
                        rgba(0, 0, 0, 0.06) 100%);
      position: relative;
      box-shadow: 0 5px 0 #b3bcc7,
                  inset 0 -2px 0 rgba(255, 255, 255, 0.5),
                  inset 0 2px 0 rgba(255, 255, 255, 0.75);
    }
    /* Valkoinen raja SKAALATTUNA KOPIONA (Markon havainto 20.8.:
       "eikö perspektiivin pitäisi tehdä juuri päinvastoin?").

       Kaksi hylättyä yritystä ja miksi:
       1. box-shadow 0 0 0 1.5px — levitys on tasapaksu joka suuntaan, joten se
          EI kutistu pystysuunnassa vaikka muoto kutistuu. Lituskalla kiekolla
          se lukee paksumpana ylhäällä ja alhaalla — perspektiiviä vastaan.
       2. border sivukohtaisin leveyksin (1,4 px / 1,2 px) — selain pyöristi
          MOLEMMAT yhteen pikseliin. Alle pikselin reunukset eivät säily.

       Skaalattu kopio ratkaisee molemmat: ellipsin suurentaminen kertoimella
       kasvattaa sädettä joka suunnassa SUHTEESSA paikalliseen säteeseen, joten
       raja on automaattisesti ohuempi siellä missä muoto on matalampi.
       Kerroin 1,045 → sivuilla ~1,5 px, ylä-alasuunnassa ~1,26 px.
       Ei pyöristysongelmaa, koska mitta syntyy skaalauksesta eikä leveydestä. */
    .path-untouched .path-btn::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 50%;
      background: #ffffff;
      transform: scale(1.045);
      z-index: -1;
      pointer-events: none;
    }

    .path-untouched .path-btn:active {
      box-shadow: 0 1px 0 #b3bcc7,
                  inset 0 2px 0 rgba(255, 255, 255, 0.7);
    }

    /* Kappale 1 on tuotevärinä musta. Kulta tekee siitä arvokkaan
       eikä kuolleen — musta ilman aksenttia luetaan sammuneeksi. */
    .path-gold:not(.path-untouched) .path-btn {
      box-shadow: 0 5px 0 #B8860B, 0 0 0 3px var(--brand-highlight) inset;
    }

    /* Nimi solmun VIEREEN, ei alle (19.8., Markon havainto).
       SISÄpuolelle, joka on selvästi laveampi: vasemmalle siirtyneen solmun
       oikealla puolella on 186 px, vasemmalla vain 106 px (mitattu).
       Ensin sijoitin nimen ulkoreunaan olettaen että yhdysviiva täyttäisi
       sisäpuolen — se ei täytä. Viiva lähtee renkaan KESKELTÄ alaviistoon,
       joten nimen korkeudella (renkaan pystykeskikohta ±15 px) viiva on vielä
       renkaan sisäpuolella eikä ehdi nimen kohdalle. Todennettu janatestillä.
       Absoluuttinen sijoitus myös tiivistää polkua, koska nimi ei enää
       kasvata solmun korkeutta. */
    .path-label {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      /* Leveys mitattiin: renkaan reunasta ruudun laitaan on 106 px, joten
         96 px + 8 px marginaali mahtuu. `anywhere` katkoi sanat keskeltä
         ("Esittäytymine/n") — `break-word` katkoo vasta jos sana ei muuten
         mahdu, ja 11 px riittää pisimmillekin kategorianimille. */
      /* Laveampi puoli sallii leveämmän sarakkeen ja luettavamman koon:
         useimmat kategorianimet mahtuvat nyt yhdelle tai kahdelle riville. */
      width: 150px;
      font-size: 12px;
      font-weight: 600;
      color: var(--ink);
      line-height: 1.3;
      /* Rivitys VAIN sanaväleistä (19.8.): `hyphens: auto` tavutti suomea
         kesken sanan ("Tervehdyk-set"), mikä on hitaampaa luettavaa kuin
         kaksi kokonaista sanaa allekkain. `break-word` jää hätävaraksi: se
         katkaisee vain jos yksittäinen sana EI mitenkään mahdu sarakkeeseen. */
      overflow-wrap: break-word;
    }
    .path-left  .path-label { left: 100%;  margin-left: 10px;  text-align: left; }
    .path-right .path-label { right: 100%; margin-right: 10px; text-align: right; }
    /* Sama mitattu harmaa kuin numeroissa: #9aa1ab jäi 2.61:een (raja 4.5). */
    .path-untouched .path-label { color: #656D78; }

    /* Nykyinen solmu: se jota pelaaja jatkaisi. Korvaa vanhan
       recency-lajittelun — polku pysyy järjestyksessä, ja tämä
       kertoo mistä jatketaan. */
    .path-current .path-ring {
      animation: pathPulse 2s ease-in-out infinite;
    }
    @keyframes pathPulse {
      0%, 100% { box-shadow: 0 0 0 0 rgba(255, 63, 127, 0.5); }
      50%      { box-shadow: 0 0 0 12px rgba(255, 63, 127, 0); }
    }
    /* Merkki solmun YLÄPUOLELLE (18.8. testi: alhaalla se peitti otsikon).
       Sama paikka kuin Duolingossa. */
    .path-badge {
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      top: -14px;
      background: var(--brand-cta);
      color: #fff;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 0.5px;
      text-transform: uppercase;
      padding: 3px 12px;
      border-radius: 10px;
      white-space: nowrap;
      z-index: 2;
    }

    @media (max-width: 360px) {
      .path-left  { transform: translateX(-28px); }
      .path-right { transform: translateX(28px); }
      .path-ring  { width: 68px; height: 68px; }
      .path-node  { width: 68px; }
      .path-btn   { font-size: 20px; }
      .path-label { width: 128px; font-size: 11px; }
    }

    .bite-card {
      background: white;
      border-radius: 12px;
      padding: 12px 14px;
      cursor: pointer;
      transition: all 0.2s;
      box-shadow: 0 4px 14px rgba(0,0,0,0.10);
      border-left: 5px solid;
      position: relative;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      gap: 6px;
    }

    .bite-card:hover {
      transform: translateY(-2px);
      box-shadow: 0 8px 22px rgba(0,0,0,0.18);
    }

    /* === Compact list-style card layout (C1, 2026-05-10) === */
    .bite-row-main {
      display: flex;
      align-items: center;
      gap: 12px;
    }
    .bite-icon-sm {
      font-size: 28px;
      line-height: 1;
      flex-shrink: 0;
      width: 42px;          /* reserve consistent slot so icon ↔ medal swap doesn't shift title */
      text-align: center;
      transition: font-size 0.25s ease;
    }
    /* === Mastery badge takes over the icon slot (2026-05-10c) ===
       Once a player reaches Pronssi (50%), the chapter's icon is replaced
       by the earned medal — bigger, glowing, more meaningful than 📖. */
    .bite-icon-sm.bite-icon-trophy {
      font-size: 36px;
      cursor: help;
      filter: drop-shadow(0 1px 3px rgba(0,0,0,0.18));
    }
    .bite-icon-sm.bite-icon-trophy.mastery-diamond { font-size: 40px; filter: drop-shadow(0 2px 10px rgba(100, 181, 246, 0.75)); }
    .bite-icon-sm.bite-icon-trophy.mastery-star    { font-size: 38px; filter: drop-shadow(0 2px 8px  rgba(255, 213, 79, 0.70)); }
    .bite-icon-sm.bite-icon-trophy.mastery-trophy  { font-size: 36px; filter: drop-shadow(0 2px 6px  rgba(255, 193, 7,  0.65)); }
    .bite-icon-sm.bite-icon-trophy.mastery-gold    { filter: drop-shadow(0 2px 5px rgba(255, 193, 7,   0.55)); }
    .bite-icon-sm.bite-icon-trophy.mastery-silver  { filter: drop-shadow(0 2px 5px rgba(158, 158, 158, 0.55)); }
    .bite-icon-sm.bite-icon-trophy.mastery-bronze  { filter: drop-shadow(0 2px 5px rgba(205, 127, 50,  0.55)); }
    .bite-title-block {
      flex: 1;
      min-width: 0;
      min-width: 0;          /* allow text truncation */
    }
    .bite-subtitle-mini {
      font-size: 11px;
      color: var(--teksti-kevyt);
      margin-top: 2px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .bite-mastery-block {
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      gap: 2px;
      flex-shrink: 0;
    }
    .bite-mastery-row {
      display: flex;
      align-items: center;
      gap: 6px;
    }
    .bite-progress-metrics {
      font-size: 11px;
      color: var(--teksti-kevyt);
      white-space: nowrap;
      letter-spacing: 0.2px;
    }
    .bite-mastery-icon {
      font-size: 22px;
      line-height: 1;
      cursor: help;
      filter: drop-shadow(0 1px 2px rgba(0,0,0,0.15));
    }
    .bite-mastery-icon.mastery-diamond { font-size: 26px; filter: drop-shadow(0 1px 6px rgba(100, 181, 246, 0.7)); }
    .bite-mastery-icon.mastery-star    { font-size: 25px; filter: drop-shadow(0 1px 5px rgba(255, 213, 79, 0.65)); }
    .bite-mastery-icon.mastery-trophy  { font-size: 23px; filter: drop-shadow(0 1px 4px rgba(255, 193, 7, 0.6)); }
    .bite-mastery-icon.mastery-gold    { filter: drop-shadow(0 1px 3px rgba(255, 193, 7, 0.5)); }
    .bite-mastery-icon.mastery-silver  { filter: drop-shadow(0 1px 3px rgba(158, 158, 158, 0.5)); }
    .bite-mastery-icon.mastery-bronze  { filter: drop-shadow(0 1px 3px rgba(205, 127, 50, 0.5)); }

    /* === Top-right activity stack (2026-05-10b) ===
       Rounds + time stacked vertically, sized close to the mastery %.
       Acts as the at-a-glance "have I worked on this" signal. */
    .bite-activity-block {
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      gap: 3px;
      flex-shrink: 0;
      line-height: 1.15;
      white-space: nowrap;
      letter-spacing: 0.2px;
    }
    .bite-activity-item {
      display: inline-flex;
      align-items: baseline;
      gap: 5px;
      font-size: 15px;
      font-weight: 700;
      color: #5f6570;
    }
    .bite-activity-icon {
      font-size: 14px;
      opacity: 1;
    }

    /* === Middle row: medals only (2026-05-10b) === */
    /* 18.8.: "🏆16 🥇3 🥈1" on numerorivi jota pelaaja ei osaa tulkita.
       Piilotettu; B2 päättää mikä sen korvaa (todennäköisesti yksi merkki
       täydellisestä kappaleesta + hallintarengas). Data säilyy. */
    .bite-row-medals { display: none; }

    .bite-row-medals-orig {
      display: flex;
      align-items: center;
      padding-left: 40px;    /* aligns under title, not icon */
      min-height: 20px;
      font-size: 12px;
      color: var(--teksti-vaimea);
    }
    .bite-medals-line {
      display: inline-flex;
      gap: 6px;
    }

    /* === Bottom row: mastery progress bar + % + tier badge (2026-05-10b) ===
       Bar fills card width minus icon column. Colour tracks the same stage
       classes as the % label so the visual story stays consistent. */
    .bite-row-progress {
      display: flex;
      align-items: center;
      gap: 10px;
      padding-left: 40px;
    }
    .bite-row-progress .bite-conf-bar {
      flex: 1;
      height: 9px;
      background: #ececec;
      border-radius: 5px;
      overflow: hidden;
    }
    .bite-row-progress .bite-conf-fill {
      height: 100%;
      border-radius: 5px;
      transition: width 0.5s ease, background 0.3s ease;
    }
    .bite-row-progress .bite-conf-fill.bite-conf-untouched { background: #d8d8d8; }

    /* Legacy footer class retained for any orphan reference paths. */
    .bite-row-footer {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 4px 10px;
      font-size: 12px;
      color: var(--teksti-vaimea);
      padding-left: 40px;
    }
    .bite-stats-line {
      color: var(--teksti-vaimea);
    }
    .bite-empty-stats {
      display: none;          /* karsinta 2026-08-17: tyhjyys ei kaipaa tekstiä */
      color: #6b7280;
      font-style: italic;
    }
    .bite-trouble-mini {
      color: var(--vaara);
      font-weight: 600;
    }
    /* Clickable trouble-words pill — opens a focused practice session */
    button.bite-trouble-btn {
      background: rgba(198, 40, 40, 0.08);
      border: 1px solid rgba(198, 40, 40, 0.3);
      border-radius: 12px;
      padding: 2px 10px;
      font-family: inherit;
      font-size: 12px;
      cursor: pointer;
      transition: background 0.15s, transform 0.1s;
    }
    button.bite-trouble-btn:hover { background: rgba(198, 40, 40, 0.16); }
    button.bite-trouble-btn:active { transform: scale(0.95); }
    .bite-time-mini {
      color: var(--teksti-hento);
    }
    .bite-resume-mini {
      color: #2e7d32;
      font-weight: 700;
      margin-left: auto;
    }

    .bite-time {
      position: absolute;
      top: 10px;
      right: 12px;
      font-size: 11px;
      font-weight: 600;
      color: var(--teksti-hento);
      pointer-events: none;
    }
    .bite-rounds {
      position: absolute;
      top: 28px;
      right: 12px;
      font-size: 11px;
      font-weight: 600;
      color: var(--teksti-hento);
      pointer-events: none;
    }

    .bite-card:active {
      transform: translateY(-4px);
    }

    .bite-icon {
      font-size: 48px;
      margin-bottom: 15px;
    }

    .bite-title {
      font-size: 16px;
      font-weight: 700;
      margin-bottom: 5px;
      color: var(--teksti);
    }

    .bite-subtitle {
      font-size: 13px;
      color: var(--teksti-kevyt);
      margin-bottom: 12px;
    }

    .bite-date {
      font-size: 12px;
      color: #bbb;
      margin-bottom: 15px;
    }

    .bite-stats {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 12px;
      color: var(--teksti-himmea);
    }

    .word-count {
      background: #f0f0f0;
      padding: 4px 10px;
      border-radius: 20px;
    }

    .stars {
      font-size: 14px;
      letter-spacing: 2px;
    }

    .stars.no-stars {
      color: var(--raja-tumma);
    }

    .star-btn {
      cursor: pointer;
      display: inline-block;
      transition: transform 0.15s;
      position: relative;
    }

    .star-btn:hover {
      transform: scale(1.3);
    }

    .star-btn.empty-star {
      cursor: default;
    }

    .star-btn.empty-star:hover {
      transform: none;
    }

    /* Aggregate medal summary — shows total counts of each medal type
       earned across infinite sessions. Same convention as Mitaliseinä:
       emoji directly followed by count, no "×" sign, items spaced apart. */
    .bite-medal-summary {
      display: inline-flex;
      gap: 8px;
      align-items: center;
      cursor: help;
    }

    .bite-medal-item {
      font-size: 14px;
      font-weight: 700;
      color: var(--teksti-vahva);
    }

    .bite-review-trophy {
      font-size: 16px;
      margin-left: 6px;
    }

    .bite-confidence {
      display: flex;
      align-items: center;
      gap: 6px;
      margin-top: 6px;
      padding: 0 2px;
    }

    .bite-conf-bar {
      flex: 1;
      height: 6px;
      background: var(--border);
      border-radius: 3px;
      overflow: hidden;
    }

    .bite-conf-fill {
      height: 100%;
      border-radius: 3px;
      transition: width 0.5s ease, background 0.3s ease;
    }
    /* Colour by mastery stage. Bar always visible (min 2% width) so player
       sees the metric exists even on a freshly seen chapter. */
    .bite-conf-fill.bite-conf-starting { background: linear-gradient(90deg, #ef5350, #c62828); }
    .bite-conf-fill.bite-conf-learning { background: linear-gradient(90deg, #ffa726, #f57c00); }
    .bite-conf-fill.bite-conf-good     { background: linear-gradient(90deg, #66bb6a, #43a047); }
    .bite-conf-fill.bite-conf-mastery  { background: linear-gradient(90deg, #26a69a, #00897b); }

    .bite-conf-label {
      font-size: 16px;
      font-weight: 700;
      color: var(--brand-primary);
      cursor: help;
    }
    .bite-conf-label.bite-conf-untouched { color: #6b7280; }
    .bite-conf-label.bite-conf-starting  { color: var(--vaara); }
    .bite-conf-label.bite-conf-learning  { color: #f57c00; }
    .bite-conf-label.bite-conf-good      { color: #2e7d32; }
    .bite-conf-label.bite-conf-mastery   { color: #00695c; }

    .bite-color-0 { border-left-color: var(--brand-primary); }
    .bite-color-1 { border-left-color: #f5576c; }
    .bite-color-2 { border-left-color: #43e97b; }
    .bite-color-3 { border-left-color: #fcb69f; }
    .bite-color-4 { border-left-color: #a18cd1; }
    .bite-color-5 { border-left-color: #fbc2eb; }

    .bite-card.bite-complete-star {
      box-shadow: 0 0 12px rgba(255, 193, 7, 0.4), 0 4px 15px rgba(0,0,0,0.1);
    }
    .bite-card.bite-complete-diamond {
      box-shadow: 0 0 16px rgba(100, 181, 246, 0.5), 0 0 30px rgba(100, 181, 246, 0.2), 0 4px 15px rgba(0,0,0,0.1);
      border-left-color: #42a5f5 !important;
    }
    /* Mastery-tier card glows — same hierarchy as the badge.
       Higher tier = brighter, more layered glow. */
    .bite-card.bite-mastery-bronze {  /* 50% */
      box-shadow: 0 0 10px rgba(205, 127, 50, 0.35), 0 4px 15px rgba(0,0,0,0.1);
    }
    .bite-card.bite-mastery-silver {  /* 60% */
      box-shadow: 0 0 12px rgba(158, 158, 158, 0.45), 0 4px 15px rgba(0,0,0,0.1);
    }
    .bite-card.bite-mastery-gold {    /* 75% — kultamitali */
      box-shadow: 0 0 12px rgba(255, 193, 7, 0.45), 0 0 24px rgba(255, 193, 7, 0.15), 0 4px 15px rgba(0,0,0,0.1);
    }
    .bite-card.bite-mastery-trophy {  /* 85% — pokaali */
      box-shadow: 0 0 14px rgba(255, 193, 7, 0.6), 0 0 30px rgba(255, 193, 7, 0.25), 0 4px 15px rgba(0,0,0,0.1);
    }
    .bite-card.bite-mastery-star {    /* 95% */
      box-shadow: 0 0 16px rgba(255, 213, 79, 0.65), 0 0 32px rgba(255, 213, 79, 0.3), 0 4px 15px rgba(0,0,0,0.1);
    }
    .bite-card.bite-mastery-diamond { /* 100% */
      box-shadow: 0 0 20px rgba(100, 181, 246, 0.7), 0 0 40px rgba(100, 181, 246, 0.3), 0 4px 15px rgba(0,0,0,0.1);
      border-left-color: #42a5f5 !important;
    }
    .bite-completion-badge {
      position: absolute;
      top: -8px;
      left: 50%;
      transform: translateX(-50%);
      font-size: 28px;
      z-index: 2;
      filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));
      animation: badgePop 0.4s ease-out;
    }
    .bite-completion-badge.diamond {
      font-size: 32px;
      filter: drop-shadow(0 2px 8px rgba(100, 181, 246, 0.6));
    }

    /* Tiered mastery badge — single corner overlay, scales with achievement
       level. Hierarchy from highest:
         100% 💎 Timantti
          95% ⭐ Tähti
          85% 🏆 Pokaali
          75% 🥇 Kultamitali
          60% 🥈 Hopea
          50% 🥉 Pronssi
       Higher tiers get larger size + stronger glow. */
    .bite-mastery-badge {
      position: absolute;
      top: -10px;
      left: 50%;
      transform: translateX(-50%);
      font-size: 30px;
      z-index: 2;
      filter: drop-shadow(0 2px 4px rgba(0,0,0,0.25));
      animation: badgePop 0.4s ease-out;
      cursor: help;
    }
    .bite-mastery-badge.mastery-diamond {
      font-size: 38px;
      filter: drop-shadow(0 2px 12px rgba(100, 181, 246, 0.75));
    }
    .bite-mastery-badge.mastery-star {
      font-size: 36px;
      filter: drop-shadow(0 2px 10px rgba(255, 213, 79, 0.7));
    }
    .bite-mastery-badge.mastery-trophy {
      font-size: 32px;
      filter: drop-shadow(0 2px 8px rgba(255, 193, 7, 0.65));
    }
    .bite-mastery-badge.mastery-gold {
      font-size: 30px;
      filter: drop-shadow(0 2px 6px rgba(255, 193, 7, 0.55));
    }
    .bite-mastery-badge.mastery-silver {
      filter: drop-shadow(0 2px 6px rgba(158, 158, 158, 0.5));
    }
    .bite-mastery-badge.mastery-bronze {
      filter: drop-shadow(0 2px 6px rgba(205, 127, 50, 0.5));
    }
    @keyframes badgePop {
      0% { transform: translateX(-50%) scale(0); }
      60% { transform: translateX(-50%) scale(1.3); }
      100% { transform: translateX(-50%) scale(1); }
    }
    @keyframes confettiFall {
      0% { top: -20px; opacity: 1; }
      80% { opacity: 1; }
      100% { top: 110vh; opacity: 0; transform: rotate(720deg); }
    }
    @keyframes bigBadgePop {
      0% { transform: translate(-50%, -50%) scale(0); opacity: 0; }
      50% { transform: translate(-50%, -50%) scale(1.3); opacity: 1; }
      70% { transform: translate(-50%, -50%) scale(0.95); }
      100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
    }

    .footer-link {
      text-align: center;
      margin-top: 30px;
    }

    .footer-link a {
      color: white;
      text-decoration: none;
      font-size: 14px;
      font-weight: 600;
      opacity: 0.8;
      transition: opacity 0.3s;
    }

    .footer-link a:hover {
      opacity: 1;
    }

    /* ===== GAME SCREEN ===== */
    #gameScreen {
      background: white;
      border-radius: 20px;
      padding: 30px;
      box-shadow: none;   /* B1b: sisältö istuu suoraan valkoisella, ei kelluvaa laatikkoa */
      max-height: 95vh;
      max-height: 95dvh;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }

    .game-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
      flex-wrap: wrap;
      gap: 15px;
    }

    .game-progress {
      font-size: 14px;
      color: var(--teksti-himmea);
      font-weight: 600;
    }

    .game-score {
      font-size: 16px;
      font-weight: 700;
      color: #1e8449;
    }

    .quit-btn {
      background: #f5f5f5;
      border: 1px solid var(--raja-tumma);
      color: var(--teksti-vahva);
      padding: 6px 14px;
      border-radius: 20px;
      font-size: 12px;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s;
    }

    .quit-btn:hover {
      background: #fdecea;
      border-color: var(--vaara);
      color: var(--vaara);
    }

    .report-btn-inline {
      background: none;
      border: 1px solid var(--raja-vaalea);
      color: #bbb;
      padding: 4px 8px;
      border-radius: 16px;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.2s;
      line-height: 1;
    }
    .report-btn-inline:hover,
    .report-btn-inline:active {
      background: #fff3e0;
      border-color: #ffb74d;
      color: #e65100;
    }

    /* Floating report button — small, low-prominence, bottom-right corner */
    .report-btn-floating {
      position: fixed;
      bottom: 10px;
      right: 10px;
      width: 32px;
      height: 32px;
      padding: 0;
      border: 1px solid var(--border);
      border-radius: 50%;
      background: var(--bg);
      color: #9aa0a8;
      font-size: 14px;
      line-height: 1;
      cursor: pointer;
      z-index: 90;
      display: none;            /* shown via JS while a game screen is active */
      transition: opacity 0.2s, background 0.2s;
    }
    .report-btn-floating:hover,
    .report-btn-floating:active {
      background: rgba(255,152,0,0.85);
      color: #fff;
      border-color: rgba(255,255,255,0.6);
    }
    @media (max-width: 480px) {
      .report-btn-floating { bottom: 8px; right: 8px; width: 28px; height: 28px; font-size: 12px; }
    }

    /* Ositettu etenemispalkki (19.8.). Yksi pätkä per kysymys, väri kertoo
       miten meni. Harmaa = ei vielä vastattu. */
    .progress-segments {
      display: flex;
      gap: 3px;
      width: 100%;
      margin: 0 0 22px;
    }
    .progress-segments .seg {
      flex: 1 1 0;
      height: 8px;
      border-radius: 4px;
      background: #e3e6ea;
      transition: background 0.25s ease;
    }
    /* HUOM tarkkuus (19.8.): nämä olivat aiemmin muodossa `.seg-ok { }`, jolloin
       `.progress-segments .seg` (kaksi luokkaa, tarkkuus 0-2-0) VOITTI ne
       (yksi luokka, 0-1-0) — pätkät saivat tilaluokan mutta pysyivät harmaina.
       Sama esivalitsin tekee näistä yhtä tarkkoja ja järjestys ratkaisee. */
    .progress-segments .seg-ok       { background: var(--onnistui); }
    .progress-segments .seg-osittain { background: var(--brand-highlight); }
    .progress-segments .seg-virhe    { background: var(--vaara); }

    /* Tarkenne (Marko 22.9.2026, js/samasuomi.js): kun sama suomi johtaa kappaleessa eri asiaan —
       *sataa lunta* · verbin perusmuoto / *Sataa lunta.* · lause. Pieni siru kysymyksen alla. */
    .tp-tarkenne, .mc-tarkenne {
      display: table; margin: 6px auto 0; padding: 3px 10px; border-radius: 10px;
      background: #F3F0FA; color: var(--brand-primary); font-size: 13px; font-weight: 600; line-height: 1.35;
    }
    .mc-tarkenne[hidden] { display: none; }
    .pairs-tarkenne { display: block; margin-top: 2px; font-size: 11px; font-weight: 600; color: var(--brand-primary); }

    /* Artikkelivihje (Marko 22.9.2026): sääntö joka pitää paikkansa tälle sanalle. Vaalea violetti
       pinta kuten valittu siru — tietoa, ei virhettä. */
    /* Oikea sana, väärä muoto (Marko 23.9.2026): keltainen kuten artikkelivihje — virhe,
       muttei sama virhe kuin väärä sana. */
    /* Koelapun kuvaus (Marko 23.9.2026): kamera ohjekentän vieressä, ei sen tilalla. */
    .koe-lappu-rivi { display: flex; align-items: center; gap: 10px; margin: -6px 0 12px; flex-wrap: wrap; }
    .koe-lappu-nappi {
      background: #fff; color: var(--brand-primary); border: 2px solid var(--brand-primary);
      border-radius: 10px; padding: 8px 14px; font-family: inherit; font-weight: 700;
      font-size: 0.9rem; cursor: pointer;
    }
    .koe-lappu-nappi:disabled { opacity: 0.55; cursor: default; }
    .koe-lappu-tila { font-size: 0.82rem; color: var(--teksti-vaimea); }
    .tp-muotovihje {
      margin-top: 8px; padding: 8px 10px; border-radius: 10px; background: #F3F0FA;
      color: var(--teksti); font-size: 14px; line-height: 1.4; font-weight: 500; text-align: left;
    }
    .tp-muotovihje::before { content: 'Muoto: '; font-weight: 700; color: var(--brand-primary); }
    .tp-artikkelivihje {
      margin-top: 8px; padding: 8px 10px; border-radius: 10px; background: #F3F0FA;
      color: var(--teksti); font-size: 14px; line-height: 1.4; font-weight: 500; text-align: left;
    }
    .tp-artikkelivihje::before { content: 'Sääntö: '; font-weight: 700; color: var(--brand-primary); }

    /* ===== SANAMITTARI (Marko 22.–23.9.2026, js/sanamittari.js) =====
       Erän palkin alla YKSI palkki: täyttö on sanan matka, pykälät ovat varmuusaskelia ja viiva
       on «hallussa»-raja (60 %, kerran osatulla 40 %). Brändiväri; vihreä on erän palkin asia.
       ⚠️ Erän palkki ja mittari ovat eri asioita: erän palkki kertoo MITEN TÄMÄ KIERROS meni
       (väri per vastaus), mittari kertoo MITEN TÄMÄ SANA ON HALLUSSA. Siksi eri korkeus ja askelviivat. */
    .progress-segments + .sanamittari-kehys { margin-top: -10px; }
    .sanamittari-kehys { display: block; width: 100%; margin: 0 0 18px; cursor: pointer; -webkit-tap-highlight-color: transparent; }
    .sanamittari-kehys:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 3px; border-radius: 6px; }
    .sanamittari { display: block; }
    /* ⚠️ YKSI PALKKI, EIKÄ VIHREÄÄ (Marko 23.9.2026).
       *«Siinä on kolme palkkia eikä oikein hahmota, että mikä niiden merkitys on. Väritkin menevät
       jotenkin epäloogisesti. … yksi ehjä palkki osaamisen määrittämiseen siten, että siinä on
       brändiväri. Eli ei sotketa tuota vihreää ollenkaan tähän, koska se kertoo miten ko erässä
       sana meni.»*
       Kaksi puoliskoa (tunnistus · tuottaminen) on nyt yksi palkki, ja «hallussa» näkyy VIIVANA
       eikä värinvaihdoksena — vihreä jää erän palkille, jossa se tarkoittaa yhtä vastausta. */
    .sm-palkki {
      position: relative; display: block; height: 12px; border-radius: 6px; overflow: hidden;
      background: var(--tint-purple);
      box-shadow: inset 0 0 0 1px rgba(92, 10, 192, 0.28);
    }
    .sm-taytto {
      position: absolute; left: 0; top: 0; bottom: 0; width: var(--sm-taytto, 0%);
      background: linear-gradient(90deg, var(--brand-primary) 0%, var(--brand-accent) 100%);
      transition: width 0.45s ease, background 0.3s ease;
    }
    /* ⚠️ «Hallussa» EI ole oma värinsä: se on se kohta jossa täyttö ohittaa viivan. Toinen väri
       olisi ollut kolmas merkitys samalla rivillä — juuri se mistä Marko sanoi 23.9. */
    /* Askelviivat: yksi pykälä per varmuusaskel (`--sm-askel`). */
    .sm-palkki::after {
      content: ''; position: absolute; inset: 0; pointer-events: none;
      background: repeating-linear-gradient(to right, transparent 0 calc(var(--sm-askel, 20%) - 1.5px),
                                            #fff calc(var(--sm-askel, 20%) - 1.5px) var(--sm-askel, 20%));
    }
    .sm-viiva {
      position: absolute; top: -2px; bottom: -2px; left: var(--sm-raja, 60%); width: 3px; margin-left: -2px;
      background: var(--brand-primary); border-radius: 2px; z-index: 1;
    }
    /* Muutos näkyväksi: oikea vastaus etenee ~1 s:ssa, joten palkki hehkuu violettina. */
    .sm-palkki.sm-muuttui { animation: smHehku 0.9s ease; }
    .sm-palkki.sm-muuttui .sm-taytto { animation: smSyke 0.6s ease; }
    @keyframes smSyke { 0% { filter: brightness(1); } 40% { filter: brightness(1.5); } 100% { filter: brightness(1); } }
    @keyframes smHehku {
      0%   { box-shadow: inset 0 0 0 1px rgba(92, 10, 192, 0.28), 0 0 0 0 rgba(140, 0, 255, 0.55); }
      35%  { box-shadow: inset 0 0 0 1px rgba(92, 10, 192, 0.28), 0 0 0 5px rgba(140, 0, 255, 0.35); }
      100% { box-shadow: inset 0 0 0 1px rgba(92, 10, 192, 0.28), 0 0 0 0 rgba(140, 0, 255, 0); }
    }
    .sanamittari-selite { display: block; margin-top: 6px; font-size: 12.5px; line-height: 1.35; color: var(--teksti-vaimea); text-align: center; }
    .sanamittari-selite[hidden] { display: none; }
    @media (prefers-reduced-motion: reduce) {
      .sm-taytto { transition: none; }
      .sm-palkki.sm-muuttui, .sm-palkki.sm-muuttui .sm-taytto { animation: none; }
    }

    /* Vanha yhtenäinen palkki jää DOMiin (koodi kirjoittaa siihen), mutta
       ositettu palkki korvaa sen näkyvänä mittarina. */
    #gameScreen .progress-bar-container { display: none; }

    /* ===== YKSI MITTARI KAIKISSA PELEISSÄ (24.8.2026) ====================
       Brändiauditointi näytti seitsemän peliä vierekkäin, ja otsikkorivi oli
       joka pelissä eri: palkki / murtoluku 3/10 / "2/6 yhdistetty" /
       ✅4 · ❌1. Sama tieto neljällä tavalla — sama havainto jonka Marko
       teki 18.8. yhden ruudun sisällä, nyt ruutujen välillä.

       Segmenttipalkkia ei tarvinnut rakentaa: kaikki seitsemän peliä
       KUTSUIVAT sitä jo (ensureProgressSegments / markProgressSegment).
       Vanhat mittarit olivat jääneet sen viereen. Tämä on siis poisto,
       ei lisäys.

       Jokainen näistä nimeää sen minkä pelaaja jo näkee palkista
       (UI-periaate 2): murtoluku = harmaiden pätkien määrä, ✅/❌ =
       vihreiden ja punaisten määrä.

       ID-etuliite eikä paljas luokka: silloin tarkkuus (1-1-0) ratkaisee
       eikä järjestys tiedostossa — ks. CLAUDE.md, CSS-järjestys. Elementit
       jäävät DOMiin, koska koodi kirjoittaa niihin eikä null-tarkistuksia
       ole joka kutsussa. */
    #gapFillScreen        .gf-progress    { display: none; }
    #pairsScreen          .pairs-progress { display: none; }
    #sentenceBuilderScreen .sb-progress   { display: none; }
    #matchScreen          .match-score    { display: none; }
    /* Verbimuodot unohtui 23.9. listalta ja oli 25.9.2026 asti ainoa
       pelimuoto jossa laskuri nakyi. Loytyi kun Marko pyysi tarkistamaan
       miksi tama muoto nayttaa erilaiselta kuin muut. */
    #verbimuodotScreen    .vm-progress    { display: none; }
    /* Putoavissa piilotetaan MOLEMMAT: "5 jäljellä" on harmaiden pätkien
       määrä ja ✅/❌ on vihreiden ja punaisten määrä. Tämä on niistä
       kaikista se jossa poisto tuntuu eniten — sanat liikkuvat ja ohi
       menneen huomaa helpommin laskurista kuin pätkästä. Jos se osoittautuu
       huonoksi, palautus on tämän kahden rivin poisto. */
    #fallingScreen .fw-score     { display: none; }
    #fallingScreen .fw-remaining { display: none; }

    /* Otsikkoriviä EI tarvitse asemoida uudelleen. `.game-header` on
       `space-between`, ja `display:none` poistaa lapsen flex-asettelusta —
       joten ainoaksi jäävä Lopeta asettuu samaan kohtaan kuin
       monivalinnassa ja kirjoituksessa jo nyt. Sääntö joka ei tee mitään
       on huonompi kuin sääntö jota ei ole: se näyttää siltä että
       asemointi vaatii erikoiskohtelun. */

    .progress-bar-container {
      width: 100%;
      height: 8px;
      background: var(--raja-vaalea);
      border-radius: 10px;
      margin-bottom: 30px;
      overflow: hidden;
    }

    .progress-bar {
      height: 100%;
      background: var(--brand-gradient);
      transition: width 0.4s ease;
    }

    .question-section {
      text-align: center;
      margin-bottom: 40px;
    }

    .finnish-word {
      font-size: 38px;
      font-weight: 700;
      margin-bottom: 20px;
      padding: 22px 25px;
      border-radius: 18px;
      color: var(--teksti);
      background: #fff !important;
      border: 4px solid var(--teksti);
      box-shadow: 0 4px 16px rgba(0,0,0,0.08);
      animation: slideDown 0.4s ease;
      text-align: center;
    }

    @keyframes slideDown {
      from { transform: translateY(-20px); opacity: 0; }
      to { transform: translateY(0); opacity: 1; }
    }

    .word-small-label {
      font-size: 13px;
      color: #6b7280;
      text-transform: uppercase;
      letter-spacing: 1.5px;
      margin-bottom: 6px;
      font-weight: 700;
    }

    .options-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 15px;
      margin-bottom: 20px;
    }

    /* B1b (2026-08-18): vastausvaihtoehdot vaaleiksi laatoiksi.
       Täysvärilliset lohkot valkoisella tekstillä olivat kontrastiltaan
       2.1–3.8 (lukukelvottomia), ja tummennettuina ne näyttivät tunkkaisilta.
       Duolingon referenssissä vaihtoehdot ovat vaaleita laattoja — väri-
       identiteetti tulee reunuksesta ja rivin alussa olevasta muotoikonista,
       ei koko pinnasta. Kevyempi, luettavampi ja jättää värin merkitseväksi. */
    .option-btn {
      padding: 18px 20px;
      border: 2px solid var(--border);
      border-bottom-width: 4px;    /* painettava 3D-tuntu */
      border-radius: 14px;
      background: #fff;
      font-size: 16px;
      font-weight: 700;
      cursor: pointer;
      color: var(--ink);
      transition: all 0.2s;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      min-height: 60px;
      position: relative;
      overflow: hidden;
    }

    .option-btn:not(.disabled):hover {
      transform: translateY(-3px);
      box-shadow: 0 8px 20px rgba(0,0,0,0.2);
    }
    .option-btn:not(.disabled):active {
      transform: scale(0.95);
      transition: transform 0.05s;
    }

    .option-btn.disabled {
      cursor: default;
    }

    /* ⚠️ TAYSI VARIPINTA ON SAANNON MUKAINEN TASSA (1.9.2026).
       CLAUDE.md: *"Valkoinen on kangas — poikkeus: juhlaruudut saavat
       taeyden varipinnan, koska ne ovat HETKIA eivatka tyokaluja."*
       Oikea vastaus on hetki. Vaihtoehdot ennen vastaamista ovat tyokalu
       ja pysyvat valkoisina.

       Marko: *"Se OIKEIN MENI pitaa huutaa naytolla. Ei mitaan teksteja
       lisaa, mutta variviesti vahvemmaksi -> palkinto isommaksi."* */
    .option-btn.correct {
      background: var(--onnistui) !important;
      color: #fff;
      border: 4px solid var(--onnistui);
      box-shadow: 0 8px 28px rgba(47, 158, 68, 0.55);
      transform-origin: center;
      animation: correctPop 0.5s cubic-bezier(0.2, 1.3, 0.4, 1),
                 korrektiHehku 0.6s ease-out;
    }

    .option-btn.correct::after {
      content: ' ✓';
      font-size: 30px;
      font-weight: 800;
    }

    .option-btn.wrong {
      background: white !important;
      color: var(--vaara);
      border: 4px solid var(--vaara);
      box-shadow: 0 0 15px rgba(231, 76, 60, 0.3);
      animation: wrongShake 0.5s ease;
      opacity: 0.85;
    }

    .option-btn.wrong::after {
      content: ' ✗';
      font-size: 24px;
      font-weight: 700;
    }

    /* ⚠️ SKAALAUS EI SOVI TAYSILEVEALLE NAPILLE (1.9.2026).
       Kokeilin 1.16:ta, ja Marko naki oikean: *"osa siita painetusta
       napista menee jopa ruudun ulkopuolelle ja rajautuu reunoista pois.
       Se nayttaa bugilta."*
       Vaihtoehdot ovat ruudun levyisia, joten mika tahansa yli ~1.02
       tyontaa reunat ulos nakymasta. Se ei ole tyylikysymys vaan
       geometriaa: leveys x 1.16 > ruutu.

       Palkinto tulee siis VARISTA ja HEHKUSTA, ei koosta. Pysty­suunnassa
       on tilaa, joten pieni kohoaminen on turvallista ja tuntuu silti. */
    @keyframes correctPop {
      0%   { transform: translateY(0) scale(1); }
      30%  { transform: translateY(-6px) scale(1.02); }
      60%  { transform: translateY(2px) scale(1); }
      100% { transform: translateY(0) scale(1); }
    }

    /* Hehku kasvaa ja laantuu: nakyva palkinto joka EI muuta kokoa eika
       siksi voi mennä ruudun ulkopuolelle. */
    @keyframes korrektiHehku {
      0%   { box-shadow: 0 0 0 0 rgba(47, 158, 68, 0.55); }
      45%  { box-shadow: 0 8px 34px 6px rgba(47, 158, 68, 0.65); }
      100% { box-shadow: 0 8px 28px 0 rgba(47, 158, 68, 0.55); }
    }

    @keyframes wrongShake {
      0%, 100% { transform: translateX(0); }
      20% { transform: translateX(-8px); }
      40% { transform: translateX(8px); }
      60% { transform: translateX(-5px); }
      80% { transform: translateX(5px); }
    }

    .option-btn.disabled:not(.correct):not(.wrong) {
      opacity: 0.4;
    }

    /* 18.8.: vaihtoehtojen värit olivat pelkkä paikkatunniste ilman
       merkitystä — sama rooli kuin poistetuilla muotosymboleilla. Neutraalit
       laatat vapauttavat värin merkitsemään SUKUA (ks. .gender-*), mikä on
       oikeaa tietoa. Duolingon vaihtoehdot ovat samasta syystä värittömiä. */
    .option-red, .option-blue, .option-green, .option-yellow {
      border-color: var(--border);
      background: #fff;
    }
    /* ⚠️ EI VASTATTUIHIN (1.9.2026). Tama rivi on TIEDOSTOSSA MYOHEMMIN
       kuin `.option-btn.correct`, ja tarkkuus on sama (0,2,0) — joten se
       VOITTI ja vaihtoi oikean vastauksen reunan harmaaksi.

       Kosketuslaitteella `:hover` jaa paalle napautuksen jalkeen, joten
       oikea vastaus nayttti harmaareunaiselta juuri silla laitteella
       jolla peli pelataan. Marko: *"Tama vihreys on edelleen aivan liian
       hailakka."* — se ei ollut sävykysymys vaan tama.

       Korjaus on TARKKUUS eika jarjestys: `:not()` kestaa lohkon
       siirtamisen, uudelleenjarjestys ei (CLAUDE.md, CSS-jarjestysansat). */
    .option-btn:not(.correct):not(.wrong):hover { border-color: #c9cdd4; }

    /* Suvun värikoodaus (BRAND.md tuotevärit 4, 9, 5). Punaista ja vihreää
       EI käytetä, vaikka saksan oppikirjoissa on tapana — ne ovat Norsussa
       varattu oikein/väärin-palautteelle, ja törmäys olisi hämmentävä. */
    .gender-m { color: #1072B9; font-weight: 700; }   /* der / en / le  */
    .gender-f { color: #E473AA; font-weight: 700; }   /* die / la       */
    .gender-n { color: #EF7925; font-weight: 700; }   /* das / ett      */

    .option-emoji {
      font-size: 20px;
    }

    /* ===== RESULTS SCREEN ===== */
    #resultsScreen {
      background: white;
      border-radius: 20px;
      padding: 18px 16px;
      box-shadow: none;   /* B1b: sisältö istuu suoraan valkoisella, ei kelluvaa laatikkoa */
      text-align: center;
      max-height: 95vh;
      max-height: 95dvh;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }




    .result-medal-big {
      font-size: 42px;
      line-height: 1;
      margin-bottom: 2px;
      animation: medalPop 0.5s ease;
    }

    @keyframes medalPop {
      0% { transform: scale(0.3) rotate(-20deg); opacity: 0; }
      60% { transform: scale(1.15) rotate(5deg); opacity: 1; }
      100% { transform: scale(1) rotate(0); }
    }

    .result-medal-label {
      font-size: 14px;
      font-weight: 700;
      color: var(--teksti);
      text-transform: uppercase;
      letter-spacing: 2px;
    }


    .wrong-words-section {
      background: #f9f9f9;
      border-radius: 12px;
      padding: 20px;
      margin-bottom: 30px;
      text-align: left;
      max-height: 300px;
      overflow-y: auto;
    }

    .wrong-words-title {
      font-weight: 700;
      margin-bottom: 8px;
      color: var(--teksti);
      font-size: 14px;
    }

    .wrong-word-item {
      padding: 5px 0;
      border-bottom: 1px solid var(--raja-vaalea);
      font-size: 13px;
    }

    .wrong-word-item:last-child {
      border-bottom: none;
    }

    .wrong-word-finnish {
      font-weight: 700;
      color: var(--teksti);
    }

    .wrong-word-english {
      color: var(--teksti-himmea);
      font-size: 13px;
    }


    .results-action-row {
      display: flex;
      gap: 10px;
      justify-content: center;
      margin-bottom: 6px;
    }


    .results-icon-btn.retry {
      background: var(--brand-gradient);
      color: white;
      box-shadow: 0 4px 12px rgba(69, 6, 147, 0.4);
    }

    .results-icon-btn.next {
      background: linear-gradient(135deg, #43e97b, #38f9d7);
      color: white;
      box-shadow: 0 4px 12px rgba(67, 233, 123, 0.4);
    }

    .results-icon-btn:hover {
      transform: scale(1.1);
    }

    .results-icon-btn:active {
      transform: scale(0.95);
    }


    .results-back-btn:hover {
      background: var(--raja-vaalea);
    }

    .btn {
      padding: 10px 16px;
      border: none;
      border-radius: 10px;
      font-size: 14px;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.3s;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
    }

    .btn-primary {
      background: var(--brand-gradient);
      color: white;
    }

    .btn-primary:hover {
      transform: translateY(-2px);
      box-shadow: 0 10px 25px rgba(69, 6, 147, 0.4);
    }

    .btn-secondary {
      background: white;
      color: var(--brand-primary);
      border: 2px solid var(--brand-primary);
    }

    .btn-secondary:hover {
      background: #f9f9f9;
    }

    /* ===== BATCH INFO ===== */
    .batch-info {
      text-align: center;
      color: #6b7280;
      font-size: 13px;
      font-weight: 600;
      margin-bottom: 8px;
    }

    /* ===== BITE CHOICE MODAL ===== */
    /* ===== KAPPALEEN TIEDOT — BANNERI AVAUTUU (24.8.2026) ================
       Ensin tämä oli pohjaan kiinnittyvä arkki, jossa kappaleen nimi
       toistui. Markon huomio: *"voisiko se lisätieto lähteä bannerista
       alaspäin, että tavallaan se banneri avautuu? Silloin otsikkoa ei
       tarvitsisi toistaa."*

       Oikein: paneeli on bannerin JATKE eikä oma ikkunansa. Siksi se
       jakaa bannerin geometrian tarkalleen — sama leveys, sama keskitys,
       sama fixed-asemointi — ja alkaa siitä mihin banneri loppuu
       (--bm-banneri-h, joka jo mitataan). Bannerin alakulmat suoristuvat
       auki ollessa, jolloin kahdesta tulee yksi kappale. */
    .kt-arkki {
      display: none;
      position: fixed; inset: 0;
      /* EI TUMMENNUSTA (Marko 24.8.: "tausta meni turhaan harmaaksi").
         Tummennus kuuluu modaaliin, joka VAATII huomion. Tämä ei vaadi —
         se on bannerin jatke, ja paneelin varjo erottaa sen jo polusta.
         Elementti jää silti paikalleen: se on ulkopuolen painalluksen
         kohdealue, jota ilman sulkeminen vaatisi kahvan löytämistä. */
      background: transparent;
      z-index: 29;              /* bannerin (30) ALLE — banneri jää näkyviin */
    }
    .kt-arkki.active { display: block; }

    .kt-kortti {
      position: fixed;
      /* Sama geometria kuin .path-banner, +2px sen alareunasta. */
      top: calc(var(--bm-topbar-h, calc(58px + var(--norsu-turva-yla)))
                + 2px + var(--bm-banneri-h, 96px) - 16px);
      left: 50%;
      width: min(892px, calc(100% - 8px));
      transform: translateX(-50%);
      z-index: 31;
      background: #fff;
      /* Yläkulmat suorat: paneeli JATKAA banneria eikä ala uudestaan. */
      border-radius: 0 0 18px 18px;
      box-shadow: 0 14px 34px -14px rgba(25,20,40,.4);
      max-height: calc(100vh - var(--bm-topbar-h, 96px) - var(--bm-banneri-h, 96px) - 40px);
      overflow-y: auto;
      /* ⚠️ VIERITYS EI SAA VUOTAA POLKUUN (7.9.2026). Kun paneelista
         poistettiin pyyhkaisyele (se soi sisallon vierityksen), vieritys
         jai ainoaksi eleeksi — ja ilman tata reunaan asti vieritetty
         paneeli jatkaa liikkeen TAUSTALLA olevaan polkuun. Silloin
         lukeminen siirtaa sivua, ja se nayttaa samalta vialta kuin se
         joka juuri korjattiin. */
      overscroll-behavior: contain;
      /* 16px vetää paneelin bannerin pyöristyksen alle, jolloin sauma
         katoaa — siksi yläreunan sisennys on 16px isompi. */
      padding: 30px 20px 16px;
      animation: ktAuki 0.24s cubic-bezier(.2,.8,.3,1);
      transform-origin: top center;
    }
    /* Valuu alas ja nousee takaisin ylös (Marko 24.8.). Ilmestyminen ja
       katoaminen ovat eri asia kuin avautuminen ja sulkeutuminen, vaikka
       kesto on sama — liike kertoo MISTÄ paneeli tuli ja minne se meni.
       `scaleY` yläreunasta on se joka tekee siitä valumista eikä
       liukumista: paneeli kasvaa bannerista ulos. */
    @keyframes ktAuki {
      from { opacity: 0; transform: translateX(-50%) scaleY(.6);  }
      to   { opacity: 1; transform: translateX(-50%) scaleY(1);   }
    }
    @keyframes ktKiinni {
      from { opacity: 1; transform: translateX(-50%) scaleY(1);   }
      to   { opacity: 0; transform: translateX(-50%) scaleY(.6);  }
    }
    .kt-arkki.kt-sulkeutuu .kt-kortti { animation: ktKiinni 0.18s ease-in forwards; }
    @media (prefers-reduced-motion: reduce) {
      .kt-kortti, .kt-arkki.kt-sulkeutuu .kt-kortti { animation: none; }
    }
    .kt-otsikko, .kt-kirja, .kt-kahva { display: none; }   /* banneri kertoo ne jo */
    .kt-focus {
      background: var(--tint-purple); color: var(--brand-primary);
      border-radius: 12px; padding: 10px 14px; font-size: 14px;
      line-height: 1.45; margin: 0 0 18px;
    }
    .kt-luvut { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 18px; }
    .kt-luku { background: #f7f7fb; border-radius: 12px; padding: 11px 13px; }
    /* HALLINTARUUTU VÄRITTYY (Marko 24.8.): "olisi hauska jos se myös
       värjääntyisi prosentin mukaan — silloin se viittaisi vaakabannerin
       alaosassa olevaan palkkiin."

       KONTRASTI RATKAISTIIN RAKENTEELLA, EI VÄRIVALINNALLA. Jos täyttö
       kulkisi luvun ALTA, luvun kontrasti riippuisi prosentista: 20 %:lla
       valkoisella, 90 %:lla värillä. Täyttö on siksi ruudun ALAREUNASSA
       omana palkkinaan — juuri kuten bannerissa — ja luku on aina
       valkoisella. Sama tieto, eikä yhtään tapausta jossa se ei erotu. */
    .kt-luku-taytto {
      position: relative; overflow: hidden;
      background: color-mix(in srgb, var(--kt-vari) 8%, #f7f7fb);
      padding-bottom: 17px;
    }
    .kt-luku-taytto::after {
      content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 7px;
      background: color-mix(in srgb, var(--kt-vari) 18%, #e9e9f0);
    }
    .kt-luku-palkki {
      position: absolute; left: 0; bottom: 0; height: 7px; z-index: 1;
      background: var(--kt-vari);
      border-radius: 0 4px 4px 0;
      transition: width .5s cubic-bezier(.2,.8,.3,1);
    }
    /* Luku samassa sävyssä mutta tummennettuna luettavaksi — tummennus
       tulee tummennaVari():stä, joka sitoo kertoimen luminanssiin. Vaalea
       tuoteväri (keltainen) ei siis jää harmaaksi valkoisella. */
    .kt-luku-taytto .kt-luku-arvo { color: var(--kt-muste); }
    .kt-luku-arvo { font-size: 21px; font-weight: 800; color: var(--ink);
                    font-variant-numeric: tabular-nums; line-height: 1.15; }
    .kt-luku-nimi { font-size: 11.5px; color: #8b8598; margin-top: 2px; }
    .kt-osio { font-size: 12px; font-weight: 800; letter-spacing: .07em;
               text-transform: uppercase; color: #9a94ab; margin: 0 0 8px; }
    .kt-palkki { display: flex; height: 12px; border-radius: 6px; overflow: hidden;
                 background: #ececf2; margin-bottom: 8px; }
    .kt-palkki span { display: block; height: 100%; }

    /* TARKEIDEN SANOJEN NIPUT (7.9.2026). Marko: 31 sanaa yhdessa potkossa
       on liikaa — pilkotaan 10 sanan nippuihin kuten polullakin.
       ⚠️ Oma luokka `.kt-palkki-pieni` eika `.kt-palkki`n uudelleen-
       maarittely: sama valitsin kahdesti samalla tarkkuudella ratkeaisi
       PELKAN JARJESTYKSEN perusteella (CLAUDE.md, viisi korjausta yhtena
       paivana). Modifier voittaa aina, siirtyi lohko minne tahansa. */
    .kt-palkki-pieni { height: 8px; border-radius: 4px; flex: 1; margin: 0; }
    .kt-nippu { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
    .kt-nippu-nimi { font-size: 0.78rem; color: var(--teksti-himmea);
      min-width: 64px; display: flex; gap: 5px; align-items: baseline; }
    .kt-nippu-maara { font-weight: 700; color: var(--teksti); }
    .kt-nippu-nappi { width: 30px; height: 30px; flex: 0 0 30px; border: none;
      border-radius: 50%; background: var(--brand-primary); color: #fff;
      font-size: 0.8rem; cursor: pointer; line-height: 1; }
    /* Vuorossa oleva nippu erottuu — se vastaa kysymykseen «mista aloitan»,
       joka on se ongelma josta koko Norsu alkoi. Valmiit jaavat taakse. */
    .kt-nippu-vuorossa .kt-nippu-nimi { color: var(--teksti); font-weight: 700; }
    .kt-nippu-vuorossa .kt-nippu-nappi { box-shadow: 0 0 0 3px var(--brand-highlight); }

    /* ===== REPPU: TEHTAVIEN PAIKKA (7.9.2026) =========================
       ⚠️ Omat luokat, ei `.ranking-*`:n uudelleenmaarittelya. Sama
       valitsin kahdesti samalla tarkkuudella ratkeaisi PELKAN
       JARJESTYKSEN perusteella (CLAUDE.md: viisi korjausta yhtena
       paivana). Kortin geometria peritaan `.ranking-card`ilta, sisalto
       on omansa. */
    .reppu-tehtava {
      background: #fff; border: 1px solid var(--raja-vaalea);
      border-radius: 14px; padding: 12px 14px; margin: 0 0 10px;
    }
    .reppu-tehtava-yla {
      display: flex; justify-content: space-between; align-items: baseline;
      font-size: 12px; margin-bottom: 2px;
    }
    /* Antaja on se joka vastaa kysymykseen «miksi tama on taalla», joten
       se on rivin ensimmainen asia eika alaviite. Ks. oma-harjoitus.md
       §7 ehto b. */
    .reppu-antaja { color: var(--brand-primary); font-weight: 700; }
    .reppu-antaja-oma { color: var(--teksti-hento); font-weight: 600; }
    .reppu-maara { color: var(--teksti); font-weight: 700;
                   font-variant-numeric: tabular-nums; }
    .reppu-tehtava-nimi { font-size: 14.5px; font-weight: 700;
                          color: var(--teksti); margin: 0 0 8px; }
    .reppu-tehtava-ala {
      display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
      gap: 8px 10px; margin-top: 8px;
    }
    .reppu-tila { font-size: 12.5px; color: var(--teksti-vaimea); white-space: nowrap; text-align: left; }
    /* Toisen kirjan tehtävä kertoo mistä kirjasta se on — ilman sitä
       kaksi samannimistä kappaletta eri kirjoista näyttäisivät samalta. */
    .reppu-kirja { font-size: 12px; color: var(--teksti-hento); margin: -4px 0 8px; }
    /* Lapsi tietää, että aikuinen näkee listan (reppu-palkinnot §9.2 ehto 2).
       Sama hento sävy kuin kirjan nimellä: tieto, ei varoitus. */
    .reppu-nakyvyys { font-size: 12px; color: var(--teksti-hento); margin: -4px 0 8px; }
    /* Aiheet (19.9.2026): yksi asia kerrallaan. Siru on NAPPI — pyöristetty
       suorakaide kuten `.reppu-nappi` (UI-periaate 4), ei pilleri eikä tagi.
       Valmis aihe vihreällä ✓:lla: väri merkitsee jotain (periaate 1). */
    /* ?puhe=1 (19.9.2026): diagnostiikka, ei käyttöliittymää. Ei ota kosketuksia vastaan,
       jottei se estä pelaamista sen päällä. */
    .norsu-puheloki {
      position: fixed; left: 8px; right: 8px; top: 8px; z-index: 99999; pointer-events: none;
      background: rgba(0, 0, 0, .78); color: #fff; border-radius: 10px; padding: 6px 8px; /* tyylikoe: poikkeus — diagnostiikkaloki, ei käyttöliittymää: musta pohja luettava minkä tahansa ruudun päällä */
      font: 11px/1.35 ui-monospace, Menlo, monospace; white-space: pre-wrap;
    }
    .reppu-aiheet-otsikko { font-size: 12px; color: var(--teksti-vaimea); margin: 10px 0 6px; text-align: left; }
    .reppu-aiheet { display: flex; flex-wrap: wrap; gap: 6px; }
    .reppu-aihe {
      border: 1.5px solid var(--border); border-radius: 10px; padding: 6px 12px;
      background: var(--surface); color: var(--teksti); cursor: pointer;
      font-family: inherit; font-size: 13px; font-weight: 600;
    }
    .reppu-aihe:active { border-color: var(--brand-primary); }
    .reppu-aihe.reppu-aihe-valmis { color: var(--onnistui); border-color: var(--onnistui); }
    /* Kaksi uutta tekoa rinnakkain: sanat ja koe. Yhdistelmävalitsin, koska
       `.reppu-nappi.reppu-uusi` on muuten koko rivin levyinen. */
    .reppu-uudet { display: flex; gap: 8px; margin: 0 0 12px; }
    .reppu-uudet .reppu-nappi.reppu-uusi { flex: 1 1 0; width: auto; margin: 0; padding: 11px 8px; }

    /* ===== KOE KALENTERIIN V1 (työjono 31 rivi 6) =====================
       Sama rivi repussa ja lapsen sivulla (`koeRiviHtml`, js/koe.js).
       ⚠️ Ei väriä valmiudelle: *«vielä harjoiteltavia: 4»* on tekemistä, ei
       tuomio (koe-kalenteriin §4a), joten se ei ole punainen eikä vihreä. */
    .reppu-kokeet { margin: 0 0 12px; }
    .koe-rivi {
      border: 1px solid var(--raja-vaalea); border-radius: 12px;
      padding: 10px 14px; margin: 0 0 8px; background: #fff; text-align: left;
    }
    #laumaKokeetKortti .koe-rivi { border: 0; border-top: 1px solid var(--raja-vaalea); border-radius: 0; padding: 10px 4px; margin: 0; }
    .koe-rivi-yla, .koe-rivi-ala {
      display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap;
    }
    .koe-rivi-nimi { font-weight: 700; font-size: 15px; color: var(--teksti); }
    .koe-rivi-paiva { font-size: 13px; color: var(--teksti-vaimea); }
    .koe-rivi-paiva b { color: var(--brand-primary); }
    /* ⚠️ `nowrap` PITI RIVIN YHTENA, JA NELJA NAPPIA EI MAHDU (Marko 26.9.2026:
       *«ne ei myöskään mahdu kaikki näkyviin»*). Mitattu hanen ruudultaan: nelja nappia
       (*Päivitä osaamisesi · Kertaa kokeeseen · Harjoittele apuverbit · Muokkaa*) on
       yhdella rivilla ~600 px, kortti puhelimessa ~330 px. `flex: 0 0 auto` esti myos
       kutistumisen, joten tilateksti litistyi muutaman merkin levyiseksi ja napit
       valuivat kortin ulkopuolelle. Nappirivi saa katketa; tilateksti pysyy luettavana. */
    .koe-rivi-ala { margin-top: 6px; align-items: center; flex-wrap: wrap; }
    .koe-rivi-tiedot { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 140px; }
    .koe-rivi-tila { font-size: 13px; color: var(--teksti); }
    .koe-rivi-merkitsija { font-size: 12px; color: var(--teksti-hento); }
    .koe-rivi-ala .reppu-nappi-toinen { margin-right: 0; flex: 0 0 auto; }
    .koe-rivi-napit { display: flex; gap: 6px; flex: 1 1 auto; flex-wrap: wrap; justify-content: flex-end; }

    /* ===== TUETTU TUNNISTUS KIRJOITUKSESSA (Marko 26.9.2026) ===========
       «En tiedä» on askel taaksepäin tunnistukseen, ei luovutus — siksi se on
       ääriviiva eikä täyttö: `Tarkista ✓` on yhä se teko jota pyydetään. */
    .tp-tunnista-avaa { display: block; margin: 8px auto 0; padding: 9px 14px; font: inherit;
      font-size: 13px; font-weight: 600; color: var(--brand-primary); background: transparent;
      border: 1.5px solid var(--raja-vaalea); border-radius: 10px; cursor: pointer; }
    .tp-tunnista-avaa:disabled { opacity: .45; cursor: default; }
    .tp-tunnista-otsikko { font-size: 13px; color: var(--teksti-vaimea); margin-bottom: 6px; }
    .tp-tunnista-napit { display: flex; flex-direction: column; gap: 6px; }
    .tp-tunnista-nappi { padding: 11px 14px; font: inherit; font-size: 15px; text-align: left;
      color: var(--teksti); background: #fff; border: 1.5px solid var(--raja-vaalea);
      border-radius: 10px; cursor: pointer; }
    .tp-tunnista-nappi.tp-tunnista-oikein { border-color: var(--onnistui); color: var(--onnistui); font-weight: 700; }
    .tp-tunnista-nappi.tp-tunnista-vaarin { border-color: var(--vaara); color: var(--vaara); }
    .tp-tunnista-ohje { margin-top: 8px; font-size: 13px; color: var(--teksti-vaimea); }

    /* ===== KOEALUEEN MITTARI (Marko 26.9.2026) =========================
       *«Nyt tytär ei yhtään näe sitä miten hän edistyy suhteessa kokeen sisältöihin.»*
       Sama palkki ja sama luku kuin repun listarivilla — yksi visuaalinen kieli. */
    .koe-rivi-mittari { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
    .koe-rivi-mittari .kt-palkki { flex: 1 1 auto; margin: 0; }
    .koe-rivi-luku { flex: 0 0 auto; font-size: 13px; color: var(--teksti-vaimea);
      font-variant-numeric: tabular-nums; }
    .koe-rivi-luku b { color: var(--teksti); font-size: 15px; }
    .koe-rivi-vaje { flex: 1 1 100%; font-size: 11.5px; color: var(--teksti-hento); }
    /* Tyhjana ei vie tilaa: viesti syntyy vasta jos jokin kieltaytyy. */
    .koe-rivi-viesti:not(:empty) { margin-top: 6px; font-size: 12.5px; color: var(--huomio); }

    /* Lapsen kalenterin päiväpaneeli (aikuisen-nakyma §19, K5). Siirretty
       inline-tyyleistä luokiksi samalla kun paneeli sai kokeet ja teon. */
    /* Kieliyhteenvetorivi (aikuisen-nakyma §32.3): kalenterin alla, jokainen kieli on
       painike Oppimiseen. Tekstilinkin näköinen — se on tieto johon voi tarttua,
       ei toinen välilehtinauha. */
    /* Seurannan neljä lukua ja alaviite — inline-tyyleistä luokiksi kun ne siirtyivät
       Seuranta-välilehdelle (§32.2, tyylikoe). Arvot ennallaan. */
    .lauma-luvut { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
    .lauma-alaviite { font-size: 0.78rem; color: var(--teksti-hento); line-height: 1.5; }
    /* C71 (19.9.2026): lapsen nimeämät sanat huoltajan Oppiminen-välilehdellä. Sama kortti kuin Tärkeät sanat. */
    .lauma-nimetyt { border: 1px solid var(--raja-vaalea); border-radius: 12px; padding: 2px 0 10px; margin-bottom: 14px; }
    .lauma-nimetyt-otsikko { padding: 10px 16px 2px; }
    .lauma-nimetyt-selite { padding: 0 16px 8px; font-size: 0.78rem; color: var(--teksti-vaimea); line-height: 1.45; }
    .lauma-nimetyt-rivi { display: flex; gap: 10px; padding: 3px 16px; font-size: 0.9rem; line-height: 1.35; color: var(--teksti); }
    .lauma-nimetyt-sana, .lauma-nimetyt-nimi { flex: 1 1 50%; min-width: 0; overflow-wrap: anywhere; }
    .lauma-nimetyt-nimi { font-weight: 700; }
    /* Lapsen sivun ➕ ja ⚙️ vierekkäin (§32.4 D1). */
    .norsu-lapsisivu-napit { display: flex; align-items: center; }
    /* ⚠️ ARKKI LAPSEN SIVUN PÄÄLLE (§32.4 D2, mitattu z-indekseistä 15.9.): `.bm-arkki`
       on z 200 ja päättyy navin yläreunaan, mutta lapsen sivu on z 1400 ja peittää
       navin. Ilman tätä ➕-arkki avautuisi sivun alle näkymättömänä. Koeruutu on
       1600, joten se jää yhä arkin päälle. Sama kuvio kuin kirjautumisruuduilla. */
    body:has(#laumaLapsiSivu.auki) .bm-arkki { z-index: 1500; bottom: 0; }
    .lauma-kielirivi {
      display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px;
      margin: 0 0 12px; font-size: 13px; color: var(--teksti-vaimea);
    }
    .lauma-kielirivi-kieli {
      border: 0; background: none; padding: 2px 0; cursor: pointer;
      font: inherit; color: var(--teksti);
    }
    .lauma-kielirivi-kieli b { color: var(--brand-primary); font-weight: 700; }
    .lauma-paiva-paneeli {
      border: 1px solid var(--raja-vaalea); border-radius: 12px;
      padding: 9px 12px; margin-bottom: 14px; font-size: 0.82rem;
    }
    .lauma-paiva-otsikko { font-weight: 700; margin-bottom: 2px; }
    .lauma-paiva-tieto { color: var(--teksti-vaimea); }
    .lauma-paiva-lisa { color: var(--teksti-hento); margin-top: 4px; }
    /* Kolmas tila (§34.2): harjoiteltu, kieli ei tiedossa. Katkoviiva on sama kielikuva
       kuin kalenterin `lauma-kieleton`-ruudussa oli: ääriviiva ilman täyttöä = jotain on,
       muttei mitattuna. */
    /* Vetopäivitys ja hakuaika (§36.4, erä 18). Korkeus tulee JS:stä vedon mukaan. */
    .lauma-veto {
      height: 0; overflow: hidden; display: flex; align-items: center; justify-content: center;
      font-size: 13px; color: var(--teksti-vaimea); transition: height .12s;
    }
    /* Hakuaikarivi on painike (erä 32, §45.1:8): ulkoasu pysyy rivinä, ↻ kertoo että sen voi
       napauttaa. Nuoli brändivärillä, koska väri merkitsee tekoa; teksti pysyy harmaana tietona. */
    .lauma-haettu {
      display: block; background: none; border: 0; margin: 0; text-align: left; cursor: pointer;
      font-family: inherit; font-size: 12px; color: var(--teksti-vaimea); padding: 8px 12px 0;
    }
    .lauma-haettu::after { content: ' ↻'; color: var(--brand-primary); font-weight: 600; }
    .lauma-haettu:empty { display: none; }
    .lauma-paiva-lisa.lauma-kieleton { padding-left: 8px; border-left: 2px dashed var(--raja-vaalea); }
    /* Mallin ruudut C ja D (`mallit/aikuisen-koti.html`). Mallin vaalea
       sävy on brändiväristä sekoitettu, jottei tuotteeseen tule uutta heksaa. */
    .lauma-paiva-teko {
      display: inline-flex; align-items: center; gap: 7px; margin-top: 10px;
      background: color-mix(in srgb, var(--brand-primary) 5%, white);
      border: 1px solid var(--raja-vaalea); border-radius: 10px; padding: 9px 12px;
      font-family: inherit; font-size: 13px; font-weight: 600; color: var(--brand-primary); cursor: pointer;
    }
    .lauma-paiva-koe { display: flex; align-items: baseline; gap: 8px; margin-top: 8px; }
    .lauma-paiva-koe-nimi { font-weight: 600; font-size: 13.5px; color: var(--teksti); }
    .lauma-paiva-valmius { color: var(--teksti-vaimea); margin-top: 3px; }
    .lauma-paiva-valmius b { color: var(--brand-primary); }
    .lauma-paiva-linkit { margin-top: 9px; display: flex; gap: 14px; }
    /* Lapsen Kirjahyllyn yleinen pyyntö (§23: kirjakohtainen poistui). */
    .norsu-cta.norsu-cta-pyynto { margin-top: 14px; }
    .lauma-paiva-linkit button {
      border: 0; background: none; padding: 0; font-family: inherit;
      font-size: 12.5px; color: var(--brand-primary); cursor: pointer;
    }

    /* ⚠️ RUUTU AVAUTUU MYÖS LAPSEN SIVUN PÄÄLLE (mitattu kuvasta 14.9.): sivu on
       z 1400 (`.norsu-lapsisivu`, jotta alanavigaatio jää alle) ja
       `.settings-overlay` 1000 — ilman tätä ruutu jäi näkymättömäksi sivun alle. */
    #koeOverlay, #koeTulosOverlay, #koeOsaanOverlay { z-index: 1600; }
    /* 🔴 MERKINTARUUTU AVAUTUI KOERUUDUN ALLE (mitattu Markon ruudulta 24.9.2026).
       Marko: «napit ei toimi. Eli en voi lisätä sanoja nappia painamalla.» — ja napit
       toimivat: `khAvaa` loi ruudun, mutta `.settings-overlay` on z 1000 ja koeruutu
       1600, joten se jai nakymattomiin. Tasan sama vika kuin yllä 14.9., yksi kerros
       ylempana.
       ⚠️ Ja se on hiljainen: mikaan ei kaadu, mikaan ei kirjaudu konsoliin, ja ruutu
       ON olemassa ja aktiivinen. Vain `elementFromPoint` kertoo totuuden.
       Jakoruutu avautuu merkintaruudusta, joten se on viela sen paalla. */
    #khOverlay { z-index: 1700; }
    #khJakoOverlay { z-index: 1750; }
    /* «Mitkä osaat jo?» (§111): kysymyspuoli siruina, valittu kehystettynä — sama asu kuin repun
       merkintäruudussa (`khPiirra`), ei omaa kieltä samalle teolle. Lista joustaa ja vierii,
       otsikko ja napit pysyvät (sama ratkaisu kuin `.kh-kortti`, 19.9.). */
    .settings-card.koe-osaan { display: flex; flex-direction: column; max-height: calc(100vh - 40px); max-height: calc(100dvh - 40px); }
    .settings-card.koe-osaan > * { flex-shrink: 0; }
    .koe-osaan-ohje { font-size: 14px; color: var(--teksti); line-height: 1.4; margin: 4px 0 8px; }
    .koe-osaan-osatut { font-size: 13px; color: var(--teksti-vaimea); margin: 0 0 6px; }
    .settings-card.koe-osaan > .koe-osaan-sanat { flex: 1 1 auto; min-height: 96px; overflow-y: auto; padding: 2px; }
    .koe-osaan-ryhma { display: flex; flex-wrap: wrap; gap: 6px; }
    .koe-osaan-sana {
      padding: 7px 11px; border-radius: 10px; font: inherit; font-size: 14px; font-weight: 600; line-height: 1.3;
      border: 1.5px solid var(--raja-vaalea); background: #fff; color: var(--teksti); cursor: pointer; text-align: left;
    }
    .koe-osaan-sana.valittu { border: 2px solid var(--brand-primary); background: #F3F0FA; padding: 6.5px 10.5px; }
    .koe-osaan-sana.valittu::before { content: '✓ '; color: var(--brand-primary); }
    .koe-kortti { max-width: 460px; text-align: left; }
    .koe-kortti .settings-title { text-align: center; }
    .koe-kentta { display: block; margin: 12px 0 0; }
    .koe-kentta > span { display: block; font-size: 13px; font-weight: 700; color: var(--teksti); margin-bottom: 6px; }
    .koe-kentta input[type="text"], .koe-kentta input[type="date"], .koe-pisteet input {
      width: 100%; box-sizing: border-box; padding: 11px 12px; font: inherit; font-size: 16px;
      border: 1.5px solid var(--raja-tumma); border-radius: 10px; background: #fff; color: var(--teksti);
    }
    .koe-alue { max-height: 38vh; overflow-y: auto; border: 1px solid var(--raja-vaalea); border-radius: 12px; padding: 4px 10px; }
    .koe-lohko { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
                 color: var(--teksti-vaimea); margin: 10px 0 4px; }
    .koe-valinta { display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; cursor: pointer; }
    .koe-valinta input { width: 18px; height: 18px; margin: 2px 0 0; flex: 0 0 auto; }
    .koe-valinta-teksti { font-size: 14px; color: var(--teksti); line-height: 1.35; }
    .koe-valinta-lisa { display: block; font-size: 12px; color: var(--teksti-hento); }
    .koe-tyhja { font-size: 13px; color: var(--teksti-vaimea); padding: 10px 0; }
    /* Rastirivin sanat näkyviin (Marko 4.10.2026: «sika säkissä»). Nappi on labelin ulkopuolella,
       joten se sisennetään rastin kohdalle; lista on sama fi + kohdekieli -pari kuin sanalistassa. */
    .koe-valinta-sanat { display: inline-block; margin: -4px 0 6px 28px; padding: 3px 10px;
                         border: 1.5px solid var(--raja-tumma); border-radius: 10px; background: transparent;
                         font-size: 12px; font-weight: 600; color: var(--teksti); cursor: pointer; }
    .koe-sanalista { list-style: none; margin: 0 0 8px 28px; padding: 0; font-size: 13px; }
    .koe-sanalista li { display: flex; gap: 10px; padding: 3px 0; border-bottom: 1px solid var(--raja-vaalea); }
    .koe-sana-fi { flex: 1 1 50%; color: var(--teksti); }
    .koe-sana-tg { flex: 1 1 50%; color: var(--teksti-vaimea); }
    .koe-sanalista-tila { margin: 0 0 8px 28px; font-size: 12px; color: var(--teksti-hento); }
    /* Vaiheistus V3 (Marko 4.10.2026): 🟡/📷-kohta yksi kerrallaan ennen listaa. */
    .koe-vaihe { border: 1.5px solid var(--raja-tumma); border-radius: 12px; padding: 12px; margin: 8px 0; }
    .koe-vaihe-otsikko { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
                         color: var(--teksti-vaimea); }
    .koe-vaihe-selite { font-size: 14px; color: var(--teksti); margin: 4px 0 6px; line-height: 1.35; }
    .koe-vaihe-napit { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
    .koe-vaihe-nappi { flex: 0 0 auto; width: auto; }
    /* Selkeys (Marko 5.10.2026: «mihin tässä edes vastataan?»): ohjeen kohta → Norsun luenta → kysymys. */
    .koe-vaihe-tunnus { font-size: 12px; font-weight: 700; color: var(--teksti-vaimea); margin: 10px 0 2px; }
    .koe-vaihe-ohjeteksti { font-size: 15px; font-weight: 600; color: var(--teksti); }
    .koe-vaihe-kysymys { font-size: 15px; font-weight: 700; color: var(--teksti); margin: 12px 0 0; }
    .koe-vaihe-valinta { max-height: 40vh; overflow-y: auto; border: 1px solid var(--raja-vaalea);
                         border-radius: 12px; padding: 4px 10px; margin-top: 6px; }
    .koe-vaihe-aihe .koe-valinta { padding-left: 1.3em; }
    /* 🔴 LUKUIKKUNA AVAUTUI KOERUUDUN TAAKSE (Marko 5.10.2026: «avautuu tuonne taakse näkymättömiin»).
       Mitattu z-indekseistä: «Lue teksti» avaa `.bm-arkki`n (z 200, lapsen sivulla 1500), koeruutu on
       1600. Sama kuvio kuin `#khOverlay` 1700 yllä — kun koeruutu on auki, arkki on sen päällä.
       ⚠️ TARKKUUS, EI JÄRJESTYS: koeruutu avataan lapsen sivulta, ja `body:has(#laumaLapsiSivu.auki)
       .bm-arkki` (1500) on tarkempi kuin `body:has(#koeOverlay) .bm-arkki` — ensimmäinen korjaus hävisi
       sille (mitattu selainsarjassa: «päällä false»). Id nostaa tarkkuuden kummankin yli. */
    body:has(#koeOverlay) #norsuArkki.bm-arkki { z-index: 1650; bottom: 0; }

    /* ===== A40: kieli ensin (20.9.2026) =====
       Marko: *«ensin valita kieli ja vasta sen jälkeen kirjat ja kappaleet»*.
       Nauha on sen yläpuolella mitä se rajaa (sama sääntö kuin lapsen sivun
       kielinauhalla), ja muotokieli on 10 px pyöristetty suorakaide — ei pilleri. */
    .koe-kielet:not(:empty) { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
    .koe-kieli { display: inline-flex; align-items: center; gap: 6px; padding: 7px 11px;
      border: 1.5px solid var(--raja-tumma); border-radius: 10px; background: var(--surface);
      font: inherit; font-size: 13.5px; font-weight: 600; color: var(--teksti); cursor: pointer; }
    .koe-kieli.valittu { border-color: var(--brand-primary); background: var(--tint-purple); color: var(--brand-primary); }
    .koe-kieli-maara { font-size: 11.5px; font-weight: 700; color: var(--teksti-hento); }
    .koe-kieli.valittu .koe-kieli-maara { color: var(--brand-primary); }
    .koe-kielivaihto:not(:empty) { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
      margin-bottom: 8px; font-size: 12.5px; color: var(--teksti-vaimea); line-height: 1.4; }
    /* ===== C59 E1: opettajan ohjeen tulkinta (21.9.2026) =====
       Jokainen opettajan rivi omana lohkonaan, tila merkkinä rivin alussa. Kortti ei
       ole kortin sisällä: tulkinta on osa lomaketta, joten vain ohut raja erottaa sen.
       Tyhjä säiliö ei vie tilaa (`:empty`). */
    .koe-tulkinta:not(:empty) { margin: 14px 0 0; padding: 10px 12px; border: 1px solid var(--raja-vaalea);
      border-radius: 12px; background: var(--surface); }
    /* Järjestys 21.9.2026: tulkinta tulee perustietojen JÄLKEEN, ja sen ensimmäinen
       rivi sanoo mitä aikuiselta odotetaan. Ei väriä: ohje, ei signaali. */
    .koe-seuraava { font-size: 14px; font-weight: 700; color: var(--teksti); line-height: 1.4; margin: 2px 0 6px; }
    /* «Lisää käsin»: käsivalinta yhden napautuksen takana kun ohje on luettu.
       ⚠️ `[hidden]` erikseen: oma `display` voittaisi sen (mitattu A30:ssa). */
    .koe-kasin-nappi { display: block; width: 100%; margin: 10px 0 0; padding: 10px 12px; text-align: left;
      border: 1.5px solid var(--raja-tumma); border-radius: 10px; background: var(--surface);
      font: inherit; font-size: 14px; font-weight: 600; color: var(--teksti); cursor: pointer; }
    .koe-kasin-nappi[hidden] { display: none; }
    .koe-tulkinta-otsikko { font-size: 13px; color: var(--teksti-vaimea); margin-bottom: 4px; }
    .koe-tulkinta-otsikko b { color: var(--teksti); }
    /* Tulkinnan kirjan vaihto (Marko 3.10.2026): aikuisen valinta voittaa Norsun valinnan. */
    .koe-tulkinta-kirja { display: flex; align-items: center; gap: 8px; margin: 2px 0 8px;
      font-size: 13px; color: var(--teksti-vaimea); }
    .koe-tulkinta-kirja select { flex: 1; min-width: 0; padding: 6px 8px; border: 1.5px solid var(--raja-tumma);
      border-radius: 10px; background: var(--surface); font: inherit; font-size: 13px; color: var(--teksti); }
    .koe-tulkinta-selite { font-size: 12px; color: var(--teksti-vaimea); line-height: 1.4; margin-bottom: 6px; }
    .koe-kohta { padding: 8px 0; border-top: 1px solid var(--raja-vaalea); }
    .koe-kohta:first-of-type { border-top: 0; }
    .koe-kohta-rivi { display: flex; gap: 8px; align-items: baseline; font-size: 14px; font-weight: 600;
      color: var(--teksti); line-height: 1.35; }
    .koe-kohta-merkki { flex: 0 0 auto; width: 1.3em; text-align: center; }
    .koe-kohta-teksti { min-width: 0; overflow-wrap: anywhere; }
    .koe-kohta .koe-valinta { padding: 4px 0 4px 1.3em; margin-left: 8px; }
    .koe-kohta-huomio { display: flex; gap: 8px; align-items: baseline; padding: 2px 0 0 calc(1.3em + 8px);
      font-size: 12.5px; color: var(--teksti-vaimea); line-height: 1.4; }
    .koe-kohta-ala { margin: 4px 0 0 calc(1.3em + 8px); padding-left: 10px; border-left: 2px solid var(--raja-vaalea); }
    .koe-kohta-ala-teksti { font-size: 13px; color: var(--teksti); line-height: 1.4; overflow-wrap: anywhere; }
    .koe-kohta-ala .koe-kohta-huomio { padding-left: 0; }
    /* C59 E2: 📷 vie velhoon oikeaan kappaleeseen. Rivin sisäinen teko, ei lomakkeen
       päänappi — siksi ohut reuna eikä täyttöä. */
    .koe-kohta-kuvaa { margin: 6px 0 2px calc(1.3em + 8px); padding: 7px 12px; border-radius: 10px;
      border: 1.5px solid var(--brand-primary); background: var(--surface); color: var(--brand-primary);
      font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer; }

    .koe-kumoa { padding: 4px 10px; border: 1.5px solid var(--brand-primary); border-radius: 10px;
      background: var(--surface); font: inherit; font-size: 12.5px; font-weight: 700;
      color: var(--brand-primary); cursor: pointer; }

    /* ===== A36: opettajan ohje ja kokeen laji (20.9.2026) =====
       Marko: *«pitäisi olla mahdollisuus liittää teksti ja/tai kuva opettajan ohjeesta»*.
       ⚠️ `pre-line`: opettajan ohje on usein lista, ja yhdeksi möykyksi litistettynä
       se lakkaa olemasta ohje. Kuva on A37 eikä tule tässä. */
    .koe-laji { display: flex; gap: 10px; margin-top: 2px; }
    .koe-laji-valinta { display: flex; align-items: center; gap: 7px; padding: 8px 12px; cursor: pointer;
      border: 1.5px solid var(--raja-tumma); border-radius: 10px; font-size: 14px; color: var(--teksti); }
    .koe-laji-valinta input { width: 17px; height: 17px; margin: 0; flex: 0 0 auto; }
    .koe-laji-selite:not(:empty) { font-size: 12px; color: var(--teksti-vaimea); line-height: 1.4; margin-top: 6px; }
    .koe-ohje-kentta {
      width: 100%; box-sizing: border-box; padding: 11px 12px; font: inherit; font-size: 16px;
      border: 1.5px solid var(--raja-tumma); border-radius: 10px; background: var(--surface); color: var(--teksti);
      resize: vertical; line-height: 1.4;
    }
    .koe-rivi-laji { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 10px;
      background: var(--raja-vaalea); color: var(--teksti-vaimea);
      font-size: 11px; font-weight: 700; letter-spacing: .03em; vertical-align: 1px; }
    /* ===== C59 E3: opettajan lista kokeen rivillä (21.9.2026) =====
       Norsun kohdat ilman rastia (tila tulee harjoittelusta), kirjan kohdat rastilla.
       Tehty kohta haalenee eikä katoa: lista on opettajan, ei Norsun. */
    .koe-rivi-lista { margin-top: 8px; padding: 8px 10px; border-radius: 10px; background: var(--tint-yellow); }
    .koe-lista-rivi { display: flex; gap: 8px; align-items: baseline; padding: 4px 0; font-size: 13px;
      color: var(--teksti); line-height: 1.4; }
    label.koe-lista-rivi { cursor: pointer; }
    .koe-lista-rivi input { width: 16px; height: 16px; margin: 0; flex: 0 0 auto; align-self: center; }
    .koe-lista-ala { padding-left: 24px; }
    .koe-lista-merkki { flex: 0 0 auto; width: 1.3em; text-align: center; }
    .koe-lista-teksti { min-width: 0; overflow-wrap: anywhere; }
    .koe-lista-selite { display: block; font-size: 11.5px; color: var(--teksti-vaimea); }
    .koe-lista-rivi.tehty .koe-lista-teksti { color: var(--teksti-vaimea); text-decoration: line-through; }
    .koe-rivi-ohje { margin-top: 8px; padding: 8px 10px; border-radius: 10px; background: var(--tint-yellow);
      font-size: 13px; color: var(--teksti); line-height: 1.45; white-space: pre-line; }
    .koe-rivi-ohje-otsikko { display: block; font-size: 11px; font-weight: 700; letter-spacing: .04em;
      text-transform: uppercase; color: var(--teksti-vaimea); margin-bottom: 3px; }
    /* Opettajan lista aukeaa pyydettaessa (Marko 26.9.). Summary on `.koe-rivi-ohje-otsikko`,
       joten suljettuna lohko on tasan yhden otsikkorivin korkuinen. */
    summary.koe-rivi-ohje-otsikko { cursor: pointer; margin-bottom: 0; }
    details.koe-rivi-lista[open] > summary.koe-rivi-ohje-otsikko { margin-bottom: 3px; }
    summary.koe-rivi-ohje-otsikko::-webkit-details-marker { display: none; }
    summary.koe-rivi-ohje-otsikko::marker { content: ''; }
    summary.koe-rivi-ohje-otsikko::after { content: ' ▾'; }
    details.koe-rivi-lista[open] > summary.koe-rivi-ohje-otsikko::after { content: ' ▴'; }
    /* Harjoituslohko on TEKO eika lahde: valkoinen pohja ja aariviiva erottavat sen
       opettajan listan keltaisesta muistilapusta. */
    .koe-rivi-harjoitukset { background: #fff; border: 1px solid var(--raja-vaalea); }
    .koe-kohta-aihe.koe-kohta-valmis { background: var(--onnistui); }

    /* ===== A38: palan nimi merkintänäkymässä (20.9.2026) =====
       Marko: *«itse luoda norsun paloja, joita ottaa haltuun pienissä erissä»*.
       Kenttä on valinnainen: tyhjänä merkintä toimii kuten ennen. */
    .kh-pala-rivi { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
    .kh-pala-otsikko { font-size: 13px; color: var(--teksti-vaimea); white-space: nowrap; }
    .kh-pala-kentta { flex: 1 1 auto; min-width: 0; padding: 7px 10px; font: inherit; font-size: 14px;
      border: 1.5px solid var(--raja-tumma); border-radius: 10px; background: var(--surface); color: var(--teksti); }
    .kh-sana-pala { font-size: 11px; font-weight: 700; color: var(--brand-primary); }

    /* ===== A38b: pala joka ylittää kappaleet (20.9.2026) =====
       Samanniminen pala kahdessa kappaleessa on sama pala, ja se saa oman rivinsä.
       Yhden kappaleen pala näkyy edelleen vain oman kappaleensa sirussa. */
    .reppu-palat { margin: 0 0 12px; }
    .reppu-palat-otsikko { font-size: 12px; font-weight: 700; letter-spacing: .04em;
      text-transform: uppercase; color: var(--teksti-vaimea); margin: 0 0 6px; }
    .reppu-pala { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
      width: 100%; text-align: left; padding: 10px 14px; margin-bottom: 6px; cursor: pointer;
      border: 1.5px solid var(--raja-tumma); border-radius: 12px; background: var(--surface);
      font: inherit; color: var(--teksti); }
    .reppu-pala-nimi { font-weight: 700; font-size: 15px; }
    .reppu-pala-tiedot { font-size: 12px; color: var(--teksti-vaimea); }

    /* ===== A39: kokeen sisällöt (20.9.2026) =====
       Marko: *«koe pitää sisällään käsitteitä ja sisältöjä»*. Sisältö on erittelyä,
       ei lupaus harjoituksesta — siksi rivillä on myös se mitä Norsu EI tee. */
    .koe-alue-sisallot { max-height: 24vh; }
    .koe-rivi-sisallot { margin-top: 8px; padding: 8px 10px; border-radius: 10px;
      background: var(--tint-purple); font-size: 13px; color: var(--teksti); line-height: 1.45; }
    .koe-rivi-sisalto { display: block; }
    .koe-rivi-sisalto::before { content: '· '; color: var(--teksti-vaimea); }
    .koe-rivi-sisalto-selite { display: block; margin-top: 4px; font-size: 11px; color: var(--teksti-vaimea); }
    .koe-viesti { min-height: 18px; font-size: 13px; color: var(--vaara); margin: 8px 0 0; }
    .koe-napit { display: flex; gap: 10px; margin-top: 10px; }
    .koe-napit > button { flex: 1 1 0; margin: 0; }
    /* Kokeelle tulos (erä 22): kokeen nimi otsikon alla, pisteet «18 / 20» yhdellä rivillä. */
    .koe-alaotsikko { text-align: center; font-size: 13px; color: var(--teksti-vaimea); margin: -4px 0 4px; }
    .koe-kortti .reppu-nakyvyys { text-align: center; margin: 0 0 4px; }
    .koe-pisteet { display: flex; align-items: center; gap: 8px; }
    .koe-pisteet input[type="number"] { width: 88px; text-align: center; }
    .koe-pisteet > span { font-size: 18px; color: var(--teksti-vaimea); }
    .koe-poista { display: block; margin: 12px auto 0; border: 0; background: transparent;
                  font: inherit; font-size: 13px; color: var(--teksti-vaimea); cursor: pointer; text-decoration: underline; }

    /* Koepäivä kalenterissa: PISTE numeron alla, ei täyttöä eikä kehystä.
       Täyttö tarkoittaa harjoiteltua ja kehys tätä päivää — koe ei saa
       näyttää kummaltakaan. Sama merkki molemmissa kalentereissa (§7.6). */
    .norsu-kal-paiva.koe { position: relative; }
    .norsu-kal-paiva.koe::after {
      content: ''; position: absolute; left: 50%; bottom: 2px; width: 6px; height: 6px;
      margin-left: -3px; border-radius: 3px; background: var(--brand-primary);
    }
    .norsu-kokeet { margin: 10px 0 4px; }
    .norsu-koe-rivi { display: flex; align-items: baseline; gap: 8px; font-size: 13px; padding: 4px 2px; }
    .norsu-koe-merkki { width: 6px; height: 6px; border-radius: 3px; background: var(--brand-primary); flex: 0 0 auto; align-self: center; }
    .norsu-koe-nimi { font-weight: 700; color: var(--teksti); }
    .norsu-koe-paiva { color: var(--teksti-vaimea); margin-left: auto; }
    /* ⚠️ PYORISTETTY SUORAKAIDE, EI PILLERI (Marko 8.9.2026, CLAUDE.md
       UI-periaate 4). Tassa oli `999px`, ja se on markkinointisivun ilme
       — sovelluksessa `.button-primary` on 10 px ja 12 px on yleisin.
       Marko: *"Tasta on rasittavaa aina sanoa erikseen."* */
    .reppu-nappi {
      border: 0; border-radius: 10px; padding: 8px 16px; cursor: pointer;
      background: var(--brand-primary); color: #fff;
      font-family: inherit; font-size: 13px; font-weight: 700;
    }
    /* Listan tekeminen on repun OMA teko, joten se on rivin levyinen eika
       piilossa toisen napin vieressa. Ks. `reppuUusiLista`.

       🔴 YHDISTELMAVALITSIN, EI PELKKA `.reppu-uusi` (mitattu 8.9.2026).
       `.reppu-nappi-toinen` antaa `margin-right: 6px`, joka on oikein
       rivin sisalla mutta vaarin taysleveallä napilla: 100 % + 6 px teki
       `#reppuSisalto`sta 6 px liian leveän (574 > 568) ja ruudulle
       ilmestyi **vaakavierityspalkki**, joka nayttaa violetilta pillerilta
       kortin alla. Marko huomasi sen kuvasta ennen minua.

       ⚠️ Sama tarkkuus ei olisi riittanyt: kaksi yhden luokan valitsinta
       ratkeaa JARJESTYKSELLA, ja lohkon siirto rikkoisi taman hiljaa
       (CLAUDE.md: viisi korjausta yhtena paivana). */
    .reppu-nappi.reppu-uusi {
      display: block; width: 100%; margin: 0 0 12px; padding: 11px 14px;
      font-size: 14px;
    }
    /* Jakoruudun ja kappalevalinnan isot napit: sama muoto kuin
       `.kt-jaa`ssa, koska ne ovat sama asia — koko rivin toiminto. */
    .kh-jako-lapsi {
      display: flex; align-items: center; justify-content: space-between;
      width: 100%; padding: 14px 16px; margin: 0 0 8px;
      border: 1.5px solid var(--brand-primary); border-radius: 12px;
      background: #fff; color: var(--brand-primary);
      font-family: inherit; font-size: 15px; font-weight: 700;
      cursor: pointer; text-align: left;
    }
    .kh-jako-lapsi:active { background: #F3F0FA; }
    .reppu-kappale-maara { font-size: 12.5px; font-weight: 600;
                           color: var(--teksti-vaimea); }
    /* Tyhja reppu KERTOO MITA SE ON, ei etta se on tyhja — «ei tehtavia»
       on tosi muttei kerro mista tehtava tulisi. */
    /* Toissijainen teko samalla rivilla: sama muoto, hillitympi pinta —
       «anna» ei kilpaile «harjoittele»n kanssa. */
    .reppu-nappi-toinen {
      background: #fff; color: var(--brand-primary);
      border: 1.5px solid var(--brand-primary); margin-right: 6px;
    }
    /* Rivin napit yhtenä ryhmänä oikealla (Muokkaa · Jaa · Harjoittele).
       ⚠️ RYHMÄ SIIRTYY KOKONAISENA OMALLE RIVILLEEN, EI NAPPI KERRALLAAN
       (mitattu 375 px:llä 14.9.2026): kolme nappia ei mahdu tilatekstin
       viereen, ja ilman tätä *Harjoittele* putosi yksin alas ja tilateksti
       katkesi kahdelle riville. `margin-left: auto` pitää ryhmän oikealla
       myös silloin kun se on rivin ainoa.
       ⚠️ Yhdistelmävalitsin: väli tulee `gap`ista, ja `.reppu-nappi-toinen`in
       oma `margin-right` tuplaisi sen (sama järjestysansa kuin `.reppu-uusi`). */
    .reppu-napit {
      display: flex; justify-content: flex-end; gap: 6px; margin-left: auto;
    }
    .reppu-napit .reppu-nappi-toinen { margin-right: 0; }
    .reppu-viesti { font-size: 12.5px; margin-top: 8px; color: var(--teksti-vaimea); }
    /* Listan valmistuminen (reppu-palkinnot §11.4). Vihreä on toiminnallinen
       signaali (onnistui), ei koriste; kolikko on sama merkki kuin muualla. */
    .reppu-valmis {
      display: inline-flex; align-items: center; gap: 5px;
      font-size: 13.5px; font-weight: 700; color: var(--onnistui);
    }
    .reppu-valmis b { color: var(--teksti); }
    .reppu-tyhja { text-align: center; padding: 34px 24px; color: var(--teksti-vaimea); }
    .reppu-tyhja-kuva { font-size: 44px; line-height: 1; margin-bottom: 10px; opacity: .5; }
    .reppu-tyhja p { margin: 0 0 8px; font-size: 14px; line-height: 1.5; }
    .reppu-vihje { font-size: 12.5px; color: var(--teksti-hento); }
    .kt-selite { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12.5px;
                 color: #6f6980; margin-bottom: 18px; }
    .kt-selite i { display: inline-block; width: 9px; height: 9px; border-radius: 3px;
                   margin-right: 5px; vertical-align: baseline; }
    /* TASONORSUT KUVAKKEINA (Marko 24.8.: "ehdottomasti kuvakkeina ei
       teksteinä"). Merkki on sama `.tier-merkki`, joka värittää 🐘-glyfin
       tason liukuvärillä background-clipillä — sama kuvake kuin solmussa
       ja juhlaruudussa. Uutta kuvaketta ei tarvittu eikä saanut tehdä:
       kaksi eri kuvaa samasta palkinnosta olisi kahden totuuden alku. */
    .kt-norsut { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 18px; }
    .kt-norsu { position: relative; display: inline-block; line-height: 1; }
    .kt-norsu .tier-merkki { font-size: 34px; }
    /* Määrä pallona kulmassa: luku on kuvakkeen ominaisuus eikä sen vieressä
       oleva toinen asia. Piilotetaan kun määrä on 1 — "1×" ei kerro mitään. */
    .kt-norsu-maara {
      position: absolute; right: -6px; bottom: -3px;
      min-width: 17px; height: 17px; padding: 0 4px;
      border-radius: 9px; background: var(--ink); color: #fff;
      font-size: 11px; font-weight: 800; line-height: 17px; text-align: center;
      font-variant-numeric: tabular-nums;
      box-shadow: 0 0 0 2px #fff;
    }
    /* ===== TASOMITTARI (24.8.2026, Markon idea) ==========================
       "Vasempaan reunaan puunorsu, oikeaan kivinorsu, väliin palkki joka
       on menossa kohti kivinorsua."

       Marko ehdotti myös elohopeaa ympyrän SISÄLLÄ ja pyysi valitsemaan
       parhaan. Valittu kaksi ympyrää + väliviiva, kolmesta syystä:

       1. POLKU ON JO TÄMÄ. Solmut ovat ympyröitä joita viiva yhdistää.
          Mittarista tulee polun pienoismalli — sama kielioppi, jonka
          pelaaja on jo oppinut.
       2. ELOHOPEA PEITTÄISI NORSUN. Glyfi ON palkinto; täyttö sen päällä
          vaihtaisi merkityksellisen asian mitattavaan.
       3. KAKSI YMPYRÄÄ KERTOO MISTÄ JA MIHIN. Elohopea kertoo vain
          paljonko.

       Saavutettu norsu siirtyy vasemmalle itsestään: kun conf ylittää
       seuraavan kynnyksen, masteryTier() palauttaa sen — mittari piirtyy
       uusiksi eikä siirtoa tarvitse animoida erikseen. */
    /* Palkki kohdistetaan YMPYRÖIDEN keskelle, ei koko sarakkeen (johon
       kuuluu myös kuvateksti). 54/2 − 12/2 = 21px. Ilman tätä palkki
       roikkuisi norsujen alapuolella juuri sen verran että se näyttää
       vahingolta. */
    /* ===== SOLMURUUTU MATERIAALIPOHJALLA (24.8.2026, Markon linjaus) =====
       "Parasta olisi, jos tuo badge olisi sen värinen taustaltaan kuin
        millä tasolla ollaan… Tausta hopea, kuten juhlintaruuduissakin.
        Mieti miten ne laatikoidaan, kun taustasta tulee puu/kivi/…"

       VASTAUS LAATIKKOKYSYMYKSEEN: materiaali on KEHYS, ei paperi.
       Kaikki kuusi `--juhla-ala`-sävyä ovat tummia (#6F4727 … #055863),
       joten otsikko ja merkki ovat valkoisella turvallisesti jokaisella
       tasolla. Faktat menevät VALKOISELLE kortille materiaalin päälle —
       sama rakenteellinen ratkaisu kuin hallintaprosentin ruudussa:
       vaihtuvan värin päälle ei laiteta tekstiä jonka kontrasti riippuu
       siitä väristä. Yksi valkoinen kortti, ei kuutta kontrastisääntöä.

       Materiaalimuuttujat (--juhla-yla/-ala/-nauha/-pinta) tulevat
       sellaisenaan norsu.css:n `.juhla-*`-luokista — sama pinta kuin
       juhlaruudussa, ei toista toteutusta. */
    .sr-materiaali {
      margin: -22px -22px 0;      /* kattelyKehyksen oma padding pois */
      padding: 22px 22px 20px;
      border-radius: 18px 18px 0 0;
      /* ⚠️ Kuosi mukaan: ylla oleva kommentti lupasi jo *"sama pinta kuin
         juhlaruudussa, ei toista toteutusta"*, mutta `--juhla-kuosi`
         puuttui. Lupaus oli oikea, toteutus vajaa. */
      background-image: var(--juhla-pinta, none),
                        var(--juhla-kuosi, none),
                        linear-gradient(160deg, var(--juhla-yla) 0%, var(--juhla-ala) 100%);
      background-size: var(--juhla-pinta-koko, auto), cover, auto;
      background-repeat: repeat, no-repeat, no-repeat;
      background-blend-mode: normal, soft-light, normal;
      color: #fff;
    }
    .sr-otsikko { font-size: 19px; font-weight: 800; line-height: 1.25;
                  margin: 0 0 4px; text-wrap: balance; color: #fff; }
    /* TASON NIMI KELTAISELLA, ILMAN KUVAKETTA (Marko 24.8.: "ei kuvaketta,
       vain nimi, koska se tausta on se kuvake"). Sama ratkaisu kuin
       juhlaruudussa, jossa tason nimi on --brand-highlight harvennettuna
       versaalina materiaalin päällä. Keltainen on ainoa väri joka lukee
       kaikilla kuudella materiaalilla — se on brändin korostus juuri
       tähän tarkoitukseen. */
    /* SAMAT ARVOT KUIN JUHLARUUDUSSA (`.results-award-name`): 15px / 700 /
       3px harvennus / versaali / --brand-highlight. Kyseessä on sama asia
       samalla pinnalla, joten se saa saman kohtelun — kaksi eri tyyliä
       tasonimelle olisi kahden totuuden alku.

       ⚠️ Mitattu kontrasti keltaisella materiaalien vaaleimmasta päästä:
       3,01–3,52 : 1. Se riittää WCAG AA:n suurelle tekstille mutta ei
       normaalille (4,5). Tämä on tietoinen kompromissi ja sama joka on jo
       tuotannossa juhlaruudussa — ei uusi poikkeus. Valkoinen otsikko sen
       yläpuolella on 4,80–5,63 : 1, joten kappaleen tunnistaminen ei
       koskaan ole keltaisen varassa. */
    .sr-taso {
      font-size: 15px; font-weight: 700; letter-spacing: 3px;
      text-transform: uppercase; color: var(--brand-highlight);
      margin: 0 0 2px;
    }
    .sr-materiaali.sr-tyhja .sr-taso { color: #8b8598; }
    /* Ei tasoa vielä: ei materiaalia, vaan vaalea neutraali. */
    .sr-materiaali.sr-tyhja {
      background-image: none; background: #f2f1f6; color: var(--ink);
    }
    .sr-materiaali.sr-tyhja .sr-merkki { background: #dcdce4; }
    /* Valkoinen kortti faktoille — materiaali jää kehykseksi ympärille. */
    .sr-faktat {
      background: #fff; margin: 0 -22px -6px; padding: 18px 22px 0;
      border-radius: 18px 18px 0 0;
    }


    /* ===== APUVERBI + PÄÄVERBI (24.8.2026) ==============================
       Kaksi aukkoa, kaksi pankkia. Aukot ovat lauseessa eri kohdissa, ja
       juuri se etäisyys on koko tehtävä.

       VÄRI (24.8.2026, Markon havainto: "nyt se on mustaa ja harmaata
       valkoisella taustalla"). Ratkaisu ei ole koristelu vaan UI-periaate 1:
       väri merkitsee jotain. Tässä se merkitsee sitä ainoaa asiaa jota peli
       opettaa — **verbi on kahdessa osassa**:

         apuverbi = violetti     pääverbi = amber

       Sama väri on aukossa JA sen omassa pankissa, joten silmä yhdistää
       puolikkaan lähteeseensä ilman että sitä tarvitsee sanoa. Duolingo saa
       värinsä hahmosta; meillä ei ole hahmoa pelinäkymässä, joten värin on
       tultava rakenteesta.

       MIKSI AMBER EIKÄ --brand-highlight SUORAAN: mitattu 24.8., var(--brand-highlight)
       tekstinä valkoisella on kontrastiltaan 1.60 — lukukelvoton. Keltainen
       jää täytöksi ja reunaksi, muste on tummennettu #8A5200 (5.83/6.39).

       ⚠️ TARKKUUS: perussäännöt EIVÄT aseta väriä lainkaan — vain rakenteen.
       Väri tulee roolisäännöistä (0,1,0) ja tila voittaa ne (0,2,0). Näin
       yksikään väripari ei ratkea järjestyksellä. Ks. CLAUDE.md CSS-järjestys. */
    #verbipariScreen {
      --vp-aux-muste:  #5C0AC0;   /* 9.29 valkoisella, 7.93 kentällä */
      --vp-aux-kentta: #F6F0FF;
      --vp-aux-reuna:  #D9C2F5;
      --vp-main-muste: #8A5200;   /* 6.39 valkoisella, 5.83 kentällä */
      --vp-main-kentta:#FFF7E3;
      --vp-main-reuna: #EFD08F;
    }
    .vp-nimi {
      font-size: 11.5px; font-weight: 800; letter-spacing: .12em;
      text-transform: uppercase; color: var(--brand-primary);
      text-align: center; margin-bottom: 4px;
    }
    /* #8b8598 oli 3.56 eli alle AA:n 15px:llä (mitattu 24.8.). */
    .vp-vihje { font-size: 15px; color: #6f6880; text-align: center; margin-bottom: 14px; }
    .vp-lause {
      font-size: 21px; font-weight: 700; line-height: 1.9; text-align: center;
      color: var(--ink); margin-bottom: 22px;
    }
    /* Perussääntö: pelkkä rakenne, ei väriä — ks. tarkkuushuomio yllä. */
    .vp-aukko {
      display: inline-block; min-width: 74px; padding: 2px 8px;
      border-bottom: 3px solid; font-variant-numeric: tabular-nums;
    }
    .vp-aukko-aux  { color: var(--vp-aux-muste);  border-bottom-color: var(--vp-aux-reuna); }
    .vp-aukko-main { color: var(--vp-main-muste); border-bottom-color: var(--vp-main-reuna); }
    /* Tilat (0,2,0) voittavat roolivärit (0,1,0) tarkkuudella, eivät järjestyksellä. */
    .vp-aukko.taytetty { color: var(--ink); }
    .vp-aukko.oikein   { color: #1B6E36; border-bottom-color: #1B6E36; }
    .vp-aukko.vaarin   { color: #A61E1E; border-bottom-color: #A61E1E; }

    /* Värillinen kenttä = se pinta joka poistaa valkoisen tyhjyyden.
       Kaksi kenttää, kaksi roolia — ei kolmea, koska verbissä on kaksi osaa. */
    .vp-puoli {
      border: 1px solid; border-radius: 16px;
      padding: 12px 14px 14px; margin-bottom: 14px;
    }
    .vp-puoli-aux  { background: var(--vp-aux-kentta);  border-color: var(--vp-aux-reuna); }
    .vp-puoli-main { background: var(--vp-main-kentta); border-color: var(--vp-main-reuna); }
    .vp-pankki-otsikko {
      font-size: 11.5px; font-weight: 800; letter-spacing: .07em;
      text-transform: uppercase; margin: 0 0 8px;
    }
    /* Jälkeläisvalitsin (0,2,0) — ei tasapeliä perussäännön kanssa. */
    .vp-puoli-aux  .vp-pankki-otsikko { color: var(--vp-aux-muste); }
    .vp-puoli-main .vp-pankki-otsikko { color: var(--vp-main-muste); }
    .vp-pankki { display: flex; flex-wrap: wrap; gap: 8px; }
    .vp-nappi {
      border: 2px solid; background: #fff;
      border-radius: 12px; padding: 10px 16px; font-size: 16px; font-weight: 700;
      font-family: inherit; cursor: pointer; transition: transform .1s, box-shadow .15s;
    }
    .vp-nappi:active { transform: scale(0.96); }
    .vp-nappi-aux  { border-color: var(--vp-aux-reuna);  color: var(--vp-aux-muste); }
    .vp-nappi-main { border-color: var(--vp-main-reuna); color: var(--vp-main-muste); }
    /* Valinta täyttää napin oman roolinsa värillä: valittu puolikas näkyy
       samanvärisenä sekä pankissa että lauseessa. Ei yleistä .valittu-sääntöä,
       koska se olisi samantarkkuuksinen näiden kanssa. */
    .vp-nappi-aux.valittu {
      background: var(--vp-aux-muste); border-color: var(--vp-aux-muste); color: #fff;
      box-shadow: 0 3px 10px rgba(92, 10, 192, .28);
    }
    .vp-nappi-main.valittu {
      background: var(--vp-main-muste); border-color: var(--vp-main-muste); color: #fff;
      box-shadow: 0 3px 10px rgba(138, 82, 0, .28);
    }
    /* ===== PALAUTEPALKKI =================================================
       Markon kysymys: "Tuleeko siihen jopa liikaa väriä, jos siihen nyt
       lisätään se punainen tai vihreä osuus? (Se saattaa olla syy, että
       Duolingossa keskusta on selkeä ja vaaleansävyinen.)"

       Kyllä tulisi — jos molemmat palaisivat yhtä aikaa. Siksi ne eivät pala:
       kun vastaus lukitaan, tehtävä on ohi ja **pankit luopuvat väristään**
       (.vp-puoli.himmea). Kerrallaan on siis yksi hallitseva väripinta:
       ratkaisuvaiheessa violetti+amber, palautevaiheessa vihreä tai punainen.
       Väri merkitsee sitä mikä on juuri nyt tärkeää.

       Mitattu 24.8.: nykyinen vihreä var(--onnistui) vaalealla kentällä = 3.11,
       liian heikko. Tummennettu #1B6E36 = 5.70. Punainen #A61E1E = 6.51. */
    .vp-puoli.himmea {
      background: #FAFAFB; border-color: #EAEAEF;
      transition: background .25s ease, border-color .25s ease;
    }
    .vp-puoli.himmea .vp-nappi { opacity: .4; }
    .vp-puoli.himmea .vp-nappi.valittu { opacity: 1; }

    .np-palkki {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
      padding: 16px 20px calc(16px + var(--norsu-turva-ala));
      border-top: 1px solid; text-align: center;
      animation: npPalkkiNousee .22s ease-out;
    }
    @keyframes npPalkkiNousee {
      from { transform: translateY(100%); }
      to   { transform: translateY(0); }
    }
    @media (prefers-reduced-motion: reduce) {
      .np-palkki { animation: none; }
    }
    /* Perussääntö ei aseta väriä — tilat asettavat. Ei tasapeliä. */
    .np-oikein { background: #E8F7EE; border-top-color: #BFE6CD; color: #1B6E36; }
    .np-vaarin { background: #FDECEC; border-top-color: #F3C9C9; color: #A61E1E; }
    .np-lause  { font-size: 17px; font-weight: 800; line-height: 1.4; }
    .np-selite { font-size: 14px; font-weight: 500; margin-top: 4px; }
    .np-selite:empty { display: none; }
    .np-jatka {
      display: block; width: 100%; max-width: 460px; margin: 12px auto 0;
      border: 0; border-radius: 14px; padding: 14px;
      font-family: inherit; font-size: 16px; font-weight: 800;
      letter-spacing: .04em; text-transform: uppercase;
      color: #fff; cursor: pointer;
    }
    .np-oikein .np-jatka { background: #1B6E36; }
    .np-vaarin .np-jatka { background: #A61E1E; }
    .np-jatka:active { transform: scale(0.98); }
    .np-palkki[hidden] { display: none; }
    /* Palkki on kiinteä, joten sisältö tarvitsee tilaa sen alle. Bodylla
       eikä ruudulla, koska palkki on jaettu kaikkien pelimuotojen kesken.
       `body.np-auki` (0,1,1) voittaa `body`-perussäännön (0,0,1)
       tarkkuudella — ei järjestyksellä. */
    body.np-auki { padding-bottom: calc(210px + var(--norsu-turva-ala)); }

    /* ===== NIMEÄMISHETKI AUKKOPELISSÄ (24.8.2026) =======================
       Lapsen oma yksityiskohta lauseessa. Rivi on tarkoituksella pieni ja
       lauseen ALLA: se on tarjous eikä tehtävä — aukko on yhä se mihin
       vastataan. Jos tästä tulisi iso, se veisi huomion siltä sanalta jota
       ollaan opettelemassa. */
    .gf-nimeaminen { margin: 10px 0 4px; text-align: center; }
    .gf-nimi-kysymys {
      font-size: 12.5px; color: #8b8598; margin-bottom: 6px;
    }
    .gf-nimi-rivi { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
    .gf-nimi {
      border: 1.5px solid var(--brand-primary); background: #fff;
      color: var(--brand-primary); border-radius: 10px;
      padding: 5px 13px; font-size: 13.5px; font-weight: 700;
      font-family: inherit; cursor: pointer;
    }
    .gf-nimi:active { background: var(--tint-purple); }
    .gf-nimi-oma { border-style: dashed; }
    /* Jo nimetty: pelkkä hiljainen vaihtolinkki, ei uutta kysymystä joka
       kierroksella. Nimi on annettu kerran. */
    .gf-nimi-vaihda {
      border: none; background: none; color: #9a94ab;
      font-size: 12px; font-family: inherit; cursor: pointer;
      text-decoration: underline; padding: 4px;
    }

    /* ===== TASOMITTARI ON KEHÄ, EI PALKKI (24.8.2026) ===================
       Marko: "Ei tuo palkki ja norsu ympyröineen näytä siltä, että ne
       kuuluisi yhteen. Minähän haluaisin, että se ympyräkin sitten lopulta
       värjäytyy. Onko tämä edes mahdollista?"

       On — ja Norsu tekee sitä jo: `.path-ring` piirtää jokaisen solmun
       ympärille `conic-gradient`-kehän joka täyttyy hallinnan mukaan.
       Palkki + erillinen ympyrä oli väärä muoto alusta asti, koska siinä
       on **sauma**: kaksi esinettä joiden pitäisi olla yksi. Kehässä ei
       ole saumaa — matka ON ympyrän reuna, ja täyttyessään ympyrä
       värjäytyy juuri niin kuin Marko halusi.

       Ja kun kehä sulkeutuu, taso vaihtuu ja KOKO KORTIN MATERIAALI
       muuttuu siksi norsuksi. Se on vahvin mahdollinen "värjäytyy".

       Sama kielioppi kuin polulla, ei uutta keksintöä. */
    .tm { display: flex; justify-content: center; margin: 18px 0 4px; }

    /* KEHÄ = MATKA. Sama conic-gradient kuin polun `.path-ring`illa.
       Pehmeä varjo irrottaa koko kolikon materiaalista — ilman sitä kehä
       näyttää taustaan piirretyltä kuvalta, ei esineeltä sen päällä
       (Markon havainto 24.8.). `filter` seuraa ympyrän muotoa, `box-shadow`
       piirtäisi neliön reunat. */
    .tm-rengas {
      display: block;
      width: 116px; height: 116px; border-radius: 50%; padding: 10px;
      box-sizing: border-box;
      background: conic-gradient(var(--tm-seur) calc(var(--tm-conf) * 1%),
                                 rgba(0,0,0,.30) 0);
      filter: drop-shadow(0 7px 16px rgba(0,0,0,.42));
    }

    /* KOLIKKO = TAVOITE. Sama kolmiulotteisuuden resepti kuin polun
       `.path-btn`illa (dokumentoitu siellä 20.8.):
         1. liukuväri vaalea ylhäällä → tummenee alas = kaareva pinta
         2. vaalea viiva pinnan ALAREUNASSA = terävä raja pinnan ja
            etureunan välillä
         3. tummempi etureuna alla = kiekko on irti alustastaan
       Vasta kolmen yhdistelmä lukee kohotettuna. */
    .tm-kolikko {
      display: flex; align-items: center; justify-content: center;
      width: 100%; height: 100%; border-radius: 50%;
      background: #e8ecf3;
      background-image: linear-gradient(180deg,
                        rgba(255,255,255,.92) 0%,
                        rgba(255,255,255,.18) 48%,
                        rgba(0,0,0,.10) 100%);
      box-shadow: 0 3px 0 #aab3c0,
                  inset 0 -2px 0 rgba(255,255,255,.55),
                  inset 0 2px 0 rgba(255,255,255,.9);
      font-size: 46px; line-height: 1;
    }
    /* Saavutettu taso: kolikon pinta saa tason oman värin. Vaalea neutraali
       tarkoittaa "ei vielä"; värillinen pinta tarkoittaa "sinun".
       (Norsu itse pysyy täydessä väreissään kummassakin — maali jonka
       väriä ei näe ei ole maali, todettu 24.8.) */
    .tm-rengas.tm-saavutettu .tm-kolikko {
      /* ⚠️ `background-color`, EI `background`. Lyhenne nollaisi
         `background-image`in ja kolikko jäisi litteäksi — sama ansa jonka
         `.path-zero .path-btn` dokumentoi tässä samassa tiedostossa.
         Mitattu: timantin liukuväri oli poissa ennen tätä. */
      background-color: var(--tm-seur);
      box-shadow: 0 3px 0 rgba(0,0,0,.3),
                  inset 0 -2px 0 rgba(255,255,255,.35),
                  inset 0 2px 0 rgba(255,255,255,.6);
    }
    /* ⚠️ Norsun glyfille EI aseteta `filter`iä: `.tier-merkki.tier-timantti`
       käyttää sitä jo omaan hehkuunsa, ja `.tm-kolikko .tier-merkki` olisi
       samalla tarkkuudella (0-2-0) — järjestys ratkaisisi voittajan.
       Ks. CLAUDE.md, CSS-järjestys. Syvyys tulee kolikosta, ei glyfistä. */

    /* Tasoton tila (vaalea pohja): kehän ura tummana ei toimi valkoisella. */
    .sr-materiaali.sr-tyhja .tm-rengas {
      background: conic-gradient(var(--tm-seur) calc(var(--tm-conf) * 1%), #dcdce4 0);
      filter: drop-shadow(0 4px 10px rgba(25,20,40,.18));
    }
    /* Valkoinen kortti faktoille — materiaali jää kehykseksi ympärille. */
    .sr-faktat {
      background: #fff; margin: 0 -22px -6px; padding: 18px 22px 0;
      border-radius: 18px 18px 0 0;
    }

    /* PALKKI JA MAALI YHTENÄ (Marko 24.8.: "yhdistä palkki ja tuo pallura,
       eli että tavoitenorsu on tavallaan osa sitä kasvavaa palkkia").
       Ura pujotetaan ympyrän ALLE negatiivisella marginaalilla, jolloin
       matka päättyy norsuun eikä sen viereen. Ympyrä on omalla
       tasollaan (z-index), muuten ura piirtyisi sen päälle. */
    .tm { display: flex; align-items: center; margin: 14px 0 2px; }

    .kt-sanat { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
    .kt-sana { background: #fff4e5; border: 1px solid #ffd8a8; color: var(--huomio);
               border-radius: 9px; padding: 5px 10px; font-size: 13px; }
    .kt-tyhja { font-size: 13.5px; color: #8b8598; margin: 0 0 16px; }
    /* Kysymyksen suunta (27.8.2026). Kaksi nappia, valittu brändivärillä. */
    .kt-suunta { display: flex; gap: 8px; margin: 2px 0 0; }
    .kt-suunta button {
      flex: 1 1 0; padding: 9px 8px; border-radius: 10px; cursor: pointer;
      border: 1.5px solid var(--raja-vaalea); background: #fff;
      font-family: inherit; font-size: 13px; font-weight: 600; color: var(--teksti-himmea);
    }
    .kt-suunta button.valittu {
      background: var(--brand-primary); border-color: var(--brand-primary); color: #fff;
    }

    /* Kappaleen jakaminen tiedostona (27.8.2026). Brändiväri, koska tämä
       on toiminto eikä signaali — ks. CLAUDE.md värit. */
    .kt-jaa {
      display: block; width: 100%; padding: 11px 14px; margin: 0 0 6px;
      border: 0; border-radius: 12px; cursor: pointer;
      background: var(--brand-primary); color: #fff;
      font-family: inherit; font-size: 14px; font-weight: 700;
    }
    .kt-jaa:disabled { opacity: .55; cursor: default; }
    .kt-jaa-viesti { font-size: 12.5px; color: var(--teksti-vaimea);
                     min-height: 16px; margin: 0 0 10px; }
    /* Sulkukahva alareunassa: paneeli aukesi alaspäin, joten se myös
       sulkeutuu ylöspäin. Ilman näkyvää kohtaa ainoa keino olisi taustan
       painaminen, jota ei näe kukaan. */
    .kt-sulje {
      display: block; width: 100%; border: 0; background: none; cursor: pointer;
      padding: 10px 0 4px; color: #9a94ab; font-size: 18px; line-height: 1;
      font-family: inherit;
    }
    /* Auki oleva banneri: alakulmat suoriksi, varjo pois — yksi kappale. */
    .path-unit.path-banner.kt-auki {
      border-bottom-left-radius: 0;
      border-bottom-right-radius: 0;
      box-shadow: none;
    }

    .bite-status-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 8px;
      font-size: 12px;
      font-weight: 700;
    }

    .bite-trouble {
      color: var(--vaara);
    }

    .bite-resume {
      color: var(--onnistui);
      margin-left: auto;
    }

    .bite-review-pending {
      color: #f39c12;
      margin-left: auto;
      animation: pendingGlow 2s ease-in-out infinite;
    }

    @keyframes pendingGlow {
      0%, 100% { opacity: 0.7; }
      50% { opacity: 1; }
    }

    /* ===== ADJECTIVE MATCHING GAME ===== */
    .match-header {
      text-align: center;
      color: white;
      margin-bottom: 10px;
    }

    .match-header h2 {
      font-size: 20px;
      margin-bottom: 4px;
    }

    .match-header p {
      font-size: 13px;
      opacity: 0.8;
    }

    .match-prompt {
      text-align: center;
      background: rgba(255,255,255,0.95);
      border-radius: 15px;
      padding: 18px 20px;
      margin-bottom: 12px;
      animation: slideDown 0.4s ease;
    }

    .match-prompt-fi {
      font-size: 28px;
      font-weight: 700;
      color: var(--teksti);
      margin-bottom: 4px;
    }

    .match-prompt-hint {
      font-size: 12px;
      color: var(--teksti-kevyt);
    }

    .match-picks {
      display: flex;
      justify-content: center;
      gap: 10px;
      margin-bottom: 15px;
      min-height: 44px;
    }

    .match-pick-slot {
      background: var(--bg);
      border: 2px dashed #cbd5e1;
      border-radius: 10px;
      padding: 10px 16px;
      min-width: 80px;
      text-align: center;
      font-size: 12px;
      color: rgba(255,255,255,0.5);
      font-weight: 600;
      transition: all 0.3s;
    }

    .match-pick-slot.filled {
      background: rgba(69, 6, 147, 0.3);
      border: 2px solid rgba(69, 6, 147, 0.6);
      color: white;
      font-size: 14px;
    }

    .match-pick-slot.wrong-flash {
      animation: matchWrong 0.5s ease;
      border-color: var(--vaara);
      background: rgba(231, 76, 60, 0.2);
    }

    .match-grid-wrap {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      justify-content: center;
      margin-bottom: 20px;
    }

    .match-word {
      padding: 12px 16px;
      border-radius: 10px;
      text-align: center;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s;
      border: 2px solid transparent;
      background: white;
      color: var(--teksti);
    }

    .match-word:hover:not(.matched):not(.selected) {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    }

    .match-word.selected {
      border-color: var(--brand-primary);
      background: #eef0ff;
      box-shadow: 0 0 12px rgba(69, 6, 147, 0.4);
    }

    .match-word.matched {
      background: #d4edda;
      color: #155724;
      border-color: #28a745;
      cursor: default;
      pointer-events: none;
      opacity: 0.5;
    }

    .match-word.locked {
      border-color: #28a745;
      background: #d4edda;
      color: #155724;
      cursor: default;
      pointer-events: none;
    }

    .match-pick-slot.locked {
      background: rgba(40, 167, 69, 0.25);
      border: 2px solid rgba(40, 167, 69, 0.6);
      color: white;
      font-size: 14px;
      font-weight: 700;
    }

    .match-word.wrong-flash {
      animation: matchWrong 0.5s ease;
      border-color: var(--vaara);
      background: #fdecea;
    }

    @keyframes matchWrong {
      0%, 100% { transform: translateX(0); }
      20% { transform: translateX(-5px); }
      40% { transform: translateX(5px); }
      60% { transform: translateX(-3px); }
      80% { transform: translateX(3px); }
    }

    .match-score {
      text-align: center;
      color: white;
      font-size: 14px;
      font-weight: 600;
      margin-bottom: 10px;
    }

    @media (max-width: 500px) {
      .match-word {
        font-size: 12px;
        padding: 10px 12px;
      }
      .match-prompt-fi {
        font-size: 22px;
      }
    }

    /* ===== GAP-FILL STYLES ===== */
    .gf-progress {
      color: white;
      font-size: 14px;
      font-weight: 600;
    }

    .gf-sentence {
      background: rgba(255,255,255,0.95);
      border-radius: 15px;
      padding: 24px 20px;
      margin-bottom: 20px;
      text-align: center;
      font-size: 20px;
      font-weight: 600;
      color: var(--teksti);
      line-height: 1.6;
      animation: slideDown 0.4s ease;
    }

    .gf-blank {
      display: inline-block;
      min-width: 100px;
      border-bottom: 3px solid var(--brand-primary);
      color: var(--brand-primary);
      font-weight: 700;
      padding: 2px 8px;
      margin: 0 4px;
      transition: all 0.3s;
    }

    /* ===== RAAHAUS AUKKOON (2026-08-17) ===== */
    .gf-option-btn { touch-action: none; }        /* estää sivun vierityksen raahatessa */
    .gf-option-btn.gf-dragging { opacity: 0.3; }
    .gf-ghost { box-shadow: 0 8px 24px rgba(69, 6, 147, 0.35) !important; }
    /* Aukko "syttyy" kun raahattava on sen päällä — pelaaja näkee osuvansa */
    .gf-blank.gf-blank-hot {
      background: var(--tint-purple);
      border-radius: 8px;
      border-bottom-color: var(--brand-cta);
      transform: scale(1.12);
      box-shadow: 0 0 0 3px rgba(255, 63, 127, 0.25);
    }

    .gf-blank.correct {
      border-color: var(--onnistui);
      color: var(--onnistui);
    }

    .gf-blank.wrong {
      border-color: var(--vaara);
      color: var(--vaara);
    }

    .gf-hint {
      text-align: center;
      margin-bottom: 8px;
    }

    .gf-hint-btn {
      background: var(--bg);
      border: 1px dashed #cbd5e1;
      padding: 6px 18px;
      border-radius: 10px;
      display: inline-block;
      cursor: pointer;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      font-size: 14px;
      font-weight: 600;
      color: #6b7280;
      transition: all 0.3s;
      user-select: none;
    }

    .gf-hint-btn:hover {
      background: rgba(255,255,255,0.25);
    }

    .gf-hint-btn .gf-hint-word {
      filter: blur(6px);
      transition: filter 0.3s;
      display: inline-block;
    }

    /* ⚠️ TÄRKEÄT SANAT -IKKUNA MAHTUU RUUTUUN (19.9.2026, Markon kuva puhelimelta). Sanalistalla oli
       kiinteä `max-height: 46vh`, ja sen päälle otsikko, seitsemän pikanappia (Tummennetut, Sivu …) ja
       kaksi alanappia: kortti oli ruutua pidempi, *Jaa* jäi alareunan alle ja vieritys oli kahdessa
       sisäkkäisessä laatikossa. Nyt kortti on korkeintaan ruudun korkuinen pystylaatikko: otsikko,
       napit ja alaosa pysyvät, VAIN sanalista joustaa ja vierii. `min-height` pitää listasta aina
       palan näkyvissä; jos sekään ei mahdu, overlay vierii kuten ennen. */
    .settings-card.kh-kortti {
      max-width: 520px;
      position: relative;
      display: flex;
      flex-direction: column;
      max-height: calc(100vh - 40px);
      max-height: calc(100dvh - 40px);
    }
    .settings-card.kh-kortti > * { flex-shrink: 0; }
    .settings-card.kh-kortti > .kh-sanat {
      flex: 1 1 auto;
      min-height: 96px;
      display: flex;
      flex-wrap: wrap;
      align-content: flex-start;
      gap: 6px;
      overflow-y: auto;
      padding: 2px;
    }

    /* ⚠️ AVATTU VIHJE PYSYY LUETTAVANA (19.9.2026, lapsen palaute: «vihje tullee liian nopiasti
       sitä ei ehri kattoo»). Tässä oli `color: white` läpikuultavalla valkoisella — jäänne violetista
       taustasta (ennen B1-ilmettä). Sumennus poistui 0,3 s:ssa ja sana katosi valkoiseen: vihje
       «vilahti». Nyt avattu vihje on lukutekstin värinen vaalealla sävyllä. */
    .gf-hint-btn.revealed {
      background: var(--tint-purple);
      border: 1px solid var(--tint-purple);
      color: var(--teksti);
      cursor: default;
    }

    .gf-hint-btn.revealed .gf-hint-word {
      filter: none;
    }

    .gf-hint-label {
      font-size: 11px;
      display: block;
      margin-bottom: 2px;
      opacity: 1;
      color: #565d68;
    }

    .gf-options {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 8px;
      max-width: 500px;
      margin: 0 auto;
    }

    /* B1b-korjaus 18.8.: nappi oli valkoinen LÄPINÄKYVÄLLÄ reunuksella —
       näkyi violetilla pohjalla, mutta katosi kokonaan valkoiselle. Sama
       painettava tyyli kuin monivalinnan vaihtoehdoissa. */
    .gf-option-btn {
      padding: 12px 8px;
      border-radius: 12px;
      font-size: 14px;
      font-weight: 700;
      cursor: pointer;
      border: 2px solid var(--border);
      border-bottom-width: 4px;
      background: #fff;
      color: var(--ink);
      transition: all 0.2s;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      text-align: center;
    }

    .gf-option-btn:hover:not(.disabled) {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    }
    .gf-option-btn:not(.disabled):active {
      transform: scale(0.93);
      transition: transform 0.05s;
    }

    .gf-option-btn.correct {
      background: white;
      color: var(--onnistui);
      border-color: var(--onnistui);
      box-shadow: 0 0 20px rgba(46, 204, 113, 0.4);
    }

    .gf-option-btn.correct::after { content: ' ✓'; font-size: 16px; font-weight: 700; }

    .gf-option-btn.wrong {
      background: white;
      color: var(--vaara);
      border-color: var(--vaara);
    }

    .gf-option-btn.wrong::after { content: ' ✗'; font-size: 16px; font-weight: 700; }

    .gf-option-btn.disabled:not(.correct):not(.wrong) {
      opacity: 0.4;
      pointer-events: none;
    }

    @media (max-width: 500px) {
      .gf-sentence { font-size: 17px; padding: 18px 14px; }
      .gf-option-btn { font-size: 12px; padding: 10px 6px; }
      .gf-options { gap: 6px; }
    }

    /* ===== PAIRS GAME STYLES ===== */
    .pairs-progress { font-size: 16px; font-weight: 700; color: var(--brand-primary); }
    .pairs-otsikot { margin-bottom: 6px; }
    .pairs-otsikko {
      text-align: center;
      font-size: 12px;
      font-weight: 700;
      letter-spacing: 0.04em;
      color: #8b93a3;
      padding: 2px 0 4px;
    }
    .pairs-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      max-width: 540px;
      margin: 0 auto;
      padding: 0 8px;
    }
    .pairs-col {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .pairs-card {
      padding: 14px 12px;
      border-radius: 12px;
      font-size: 15px;
      font-weight: 700;
      cursor: pointer;
      border: 2px solid #cbd5e1;
      background: white;
      color: var(--teksti);
      transition: transform 0.12s, box-shadow 0.12s, background 0.2s, border-color 0.2s, opacity 0.4s;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      text-align: center;
      min-height: 48px;
      line-height: 1.3;
    }
    /* Kahdenlaiset kortit erottuvat sävyllä eivätkä vain paikalla: kapealla
       ruudulla sarakkeet ovat lähekkäin, ja pelkkä sijainti hukkuu. Sävyt
       ovat vaaleita — kortti on yhä valkoinen kangas, ei värillinen nappi
       (UI-periaate 1). Merkitsevä väri (valittu/oikein/väärin) voittaa nämä. */
    /* ⚠️ JÄRJESTYS KANTAA: nämä ovat samaa tarkkuutta (0,2,0) kuin
       .pairs-card.selected/.correct/.wrong alla. Merkitsevä väri voittaa,
       koska se on MYÖHEMMIN. Älä siirrä näitä niiden alle. */
    .pairs-card.pairs-fi { background: #fbfbfd; border-color: #d7dbe3; }
    .pairs-card.pairs-en { background: #f7faff; border-color: #c9d8ee; }
    .pairs-card:hover:not(.matched) { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(0,0,0,0.08); }
    .pairs-card:active:not(.matched) { transform: scale(0.96); }
    .pairs-card.selected {
      background: #eef2ff;
      border-color: var(--brand-primary);
      color: #4f46e5;
      box-shadow: 0 0 0 3px rgba(69, 6, 147, 0.2);
    }
    .pairs-card.correct {
      background: #e8f5e9;
      border-color: #43a047;
      color: #2e7d32;
    }
    .pairs-card.wrong {
      background: #ffebee;
      border-color: #e53935;
      color: var(--vaara);
      animation: pairs-shake 0.4s;
    }
    .pairs-card.matched {
      opacity: 0.25;
      pointer-events: none;
      transform: scale(0.95);
    }
    @keyframes pairs-shake {
      0%, 100% { transform: translateX(0); }
      25%      { transform: translateX(-4px); }
      75%      { transform: translateX(4px); }
    }
    @media (max-width: 500px) {
      .pairs-card { font-size: 13px; padding: 12px 8px; }
      .pairs-grid { gap: 6px; }
      .pairs-col { gap: 6px; }
    }

    /* ===== SENTENCE BUILDER STYLES ===== */
    .sb-progress { font-size: 16px; font-weight: 700; color: var(--brand-primary); }
    .sb-prompt-label {
      display: none;          /* karsinta 2026-08-17: kehote puhuu puolestaan */
      text-align: center;
      font-size: 13px;
      color: #6b7280;
      margin-top: 12px;
      letter-spacing: 0.5px;
      text-transform: uppercase;
    }
    .sb-prompt {
      text-align: center;
      font-size: 22px;
      font-weight: 700;
      color: var(--ink);
      padding: 12px 18px 18px;
    }
    .sb-composed {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
      align-content: flex-start;
      min-height: 64px;
      padding: 12px;
      margin: 0 auto 16px;
      max-width: 500px;
      background: var(--bg);
      border: 2px dashed #cbd5e1;
      border-radius: 14px;
      transition: all 0.2s;
    }
    .sb-composed.sb-correct { border-color: var(--onnistui); background: rgba(46,204,113,0.08); }
    .sb-composed.sb-wrong   { border-color: var(--vaara); background: rgba(231,76,60,0.08); }
    .sb-placeholder {
      width: 100%;
      text-align: center;
      color: #6b7280;
      font-style: italic;
      font-size: 14px;
      align-self: center;
    }
    .sb-pool {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      justify-content: center;
      max-width: 540px;
      margin: 0 auto 16px;
      padding: 0 6px;
    }
    .sb-tile {
      padding: 10px 14px;
      border-radius: 10px;
      font-size: 16px;
      font-weight: 700;
      cursor: pointer;
      border: 2px solid #cbd5e1;
      background: white;
      color: var(--teksti);
      transition: transform 0.1s, box-shadow 0.1s, opacity 0.15s;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
    }
    .sb-tile:hover { transform: translateY(-1px); box-shadow: 0 3px 8px rgba(0,0,0,0.1); }
    .sb-tile:active { transform: scale(0.95); }
    .sb-tile-placed {
      background: #eef2ff;
      border-color: var(--brand-primary);
      color: #4f46e5;
    }
    /* ===== RAAHAUS (2026-08-17) ===== */
    .sb-tile { touch-action: none; }          /* estää sivun vierityksen raahatessa */
    .sb-tile.sb-dragging { opacity: 0.3; }    /* alkuperäinen haalistuu, haamu seuraa sormea */
    .sb-ghost {
      box-shadow: 0 8px 24px rgba(69, 6, 147, 0.35) !important;
      transform-origin: center;
    }
    .sb-composed.sb-drop-active {
      border-color: var(--brand-primary);
      background: var(--tint-purple);
    }
    .sb-drop-marker {
      width: 4px;
      align-self: stretch;
      min-height: 38px;
      border-radius: 2px;
      background: var(--brand-cta);
      animation: sbMarkerPulse 0.8s ease-in-out infinite;
    }
    @keyframes sbMarkerPulse {
      0%, 100% { opacity: 1; }
      50%      { opacity: 0.45; }
    }

    .sb-tile-punct {
      padding: 10px 10px;
      font-size: 18px;
      color: #5f6570;
      background: #f8fafc;
    }
    .sb-check-btn {
      display: block;
      margin: 0 auto 12px;
      padding: 12px 28px;
      background: var(--brand-gradient);
      color: white;
      border: none;
      border-radius: 12px;
      font-size: 16px;
      font-weight: 700;
      cursor: pointer;
      transition: transform 0.1s, opacity 0.15s;
    }
    .sb-check-btn:hover:not(:disabled) { transform: translateY(-1px); }
    .sb-check-btn:active:not(:disabled) { transform: scale(0.97); }
    .sb-check-btn:disabled { cursor: not-allowed; }
    .sb-feedback {
      text-align: center;
      font-size: 15px;
      min-height: 24px;
      padding: 0 14px;
    }
    @media (max-width: 500px) {
      .sb-prompt { font-size: 18px; padding: 8px 14px 14px; }
      .sb-tile { font-size: 14px; padding: 8px 11px; }
      .sb-tile-punct { padding: 8px 8px; font-size: 16px; }
    }

    /* ===== TYPING PRACTICE STYLES ===== */
    .tp-progress {
      font-size: 16px;
      font-weight: 700;
      color: var(--brand-primary);
    }
    .tp-prompt {
      text-align: center;
      padding: 20px;
      margin: 10px 0;
    }
    .tp-prompt-fi {
      font-size: 28px;
      font-weight: 700;
      color: var(--teksti);
      margin-bottom: 8px;
      background: #f0f4ff;
      border-radius: 14px;
      padding: 14px 20px;
      display: inline-block;
      box-shadow: 0 2px 8px rgba(69, 6, 147, 0.10);
      border: 1.5px solid #e0e6f6;
    }
    .tp-prompt-hint {
      font-size: 14px;
      color: #6b7280;
    }
    .tp-input-area {
      display: flex;
      gap: 10px;
      padding: 0 20px;
      margin-bottom: 16px;
    }
    .tp-input {
      flex: 1;
      /* min-width:0 (2026-08-17): ilman tätä flex-elementin oletus
         min-width:auto estää kutistumisen sisältöä kapeammaksi → rivi
         vuoti 43 px yli ruudun ja "Tarkista" jäi näkymättömiin. */
      min-width: 0;
      padding: 14px 16px;
      font-size: 18px;
      font-weight: 700;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      border: 3px solid var(--raja-vaalea);
      border-radius: 12px;
      outline: none;
      transition: border-color 0.2s;
    }
    .tp-input:focus {
      border-color: var(--brand-primary);
    }
    .tp-input.correct {
      border-color: var(--onnistui);
      background: #f0fff4;
    }
    .tp-input.wrong {
      border-color: var(--vaara);
      background: #fff5f5;
    }
    .tp-check-btn {
      padding: 14px 20px;
      font-size: 16px;
      white-space: nowrap;
    }
    .tp-feedback {
      padding: 0 20px;
      min-height: 60px;
    }
    .tp-feedback-correct {
      text-align: center;
      font-size: 20px;
      font-weight: 700;
      color: var(--onnistui);
      animation: correctPop 0.5s ease;
    }
    /* «Melkein» (§39.2, erä 20): keltainen kuten muut lievennetyt, oikea asu korostettuna. */
    .tp-feedback-melkein { background: var(--tint-yellow); border: 1px solid var(--huomio); border-radius: 12px; padding: 10px; }
    .tp-melkein-selite { font-size: 0.9rem; color: var(--teksti-vahva); margin-top: 6px; }
    /* Erä 37: täsmällinen vaihtoehto — kirjan sana tietona, ei korjauksena. */
    .tp-myos { font-size: 0.9rem; color: var(--teksti-vahva); margin-top: 6px; }
    /* Erä 26: aikuisen listarivillä — mistä lista tuli ja muokkasiko lapsi sitä. */
    .lauma-reppu-annettu { display: block; font-size: 0.78rem; color: var(--teksti-hento); margin-top: 2px; }
    .tp-myos-sana { font-weight: 800; }
    .tp-melkein-muoto { display: inline-block; font-weight: 800; color: var(--huomio); margin-left: 4px; font-size: 1.05rem; }
    .tp-feedback-wrong {
      text-align: center;
      padding: 12px;
      background: #fff5f5;
      border-radius: 12px;
      border: 2px solid #ffcdd2;
    }
    .tp-diff {
      font-size: 20px;
      font-weight: 700;
      margin-bottom: 8px;
      letter-spacing: 1px;
    }
    .tp-diff-char-ok { color: var(--onnistui); }
    .tp-diff-char-wrong { color: var(--vaara); background: #ffebee; border-radius: 3px; padding: 0 2px; text-decoration: underline; }
    .tp-diff-char-missing { color: var(--vaara); background: #ffcdd2; border-radius: 3px; padding: 0 2px; opacity: 0.7; }
    /* ⚠️ YLIMAARAINEN MERKKI ON VIRHE, JA JOKAINEN VIRHE ON PUNAINEN
       (31.8.2026). Tama oli oranssi #ff9800, mika teki siita ainoan
       virhetilan joka ei nayta virheelta -- ja se olisi tormannyt
       `--huomio`-varin kanssa, joka tarkoittaa PAINVASTAISTA: hyvaksytty,
       mutta tarkista. Kaksi lahes samaa savya vastakkaisilla
       merkityksilla samassa lauseessa on huonompi kuin yksi.
       Yliviivaus kantaa "ylimaarainen"-merkityksen yksin. */
    .tp-diff-char-extra { color: var(--vaara); background: #ffebee; border-radius: 3px; padding: 0 2px; text-decoration: line-through; }

    /* HYVAKSYTTY, MUTTA TARKISTA TAMA (31.8.2026, Markon ehdotus).
       Kolmas tila vihrean ja punaisen valissa: sana kelpasi, mutta siina
       on jotain opittavaa -- saksan substantiivin iso alkukirjain on
       mitattu 19 %:ssa saksan riveista. `--huomio` on jo olemassa juuri
       tahan (#8A5A00, "varoitus, tarkista tama"), ja se on tumma jotta
       se on luettava valkoisella. Kirkas keltainen ei olisi.
       ⚠️ KOKO SANA, ei yksi merkki: isoa alkukirjainta ei korosteta
       kirjaimena vaan sanan ominaisuutena. */
    .tp-diff-word-huomio {
      color: var(--huomio); background: #fff8e1; border-radius: 3px;
      padding: 0 2px; text-decoration: underline;
      text-decoration-style: dotted;
    }
    .tp-correct-answer {
      font-size: 15px;
      color: var(--teksti-himmea);
      margin-top: 6px;
    }
    .tp-correct-answer strong {
      color: var(--onnistui);
      font-weight: 700;
    }
    .tp-retry-msg {
      text-align: center;
      font-size: 14px;
      color: var(--teksti-kevyt);
      margin-top: 4px;
    }

    /* ===== FALLING WORDS STYLES ===== */
    /* KORKEUS NÄKYVÄSTÄ IKKUNASTA, EI 95vh (23.8.2026).
       Marko: "putoavat sanat pelimuoto on tosi rasittava, koska et näe
       niitä putoavia sanoja. Ruutu skaalautuu ihan miten sattuu."
       Syy: näppäimistö pienentää VISUAL viewportia muttei LAYOUT
       viewportia. `95vh` lupasi siis tilaa jota ruudulla ei ollut, ja
       areena sekä syöttörivi valuivat näppäimistön alle.
       `--fw-nakyva` tulee `visualViewport`ista (js/game-falling.js).
       Pystysuunnan flex jakaa tilan: areena venyy, syöttörivi ei. */
    #fallingScreen {
      background: var(--ink);
      border-radius: 20px;
      padding: 15px 20px 20px;
      box-shadow: 0 20px 60px rgba(0,0,0,0.4);
      height: var(--fw-nakyva, 95dvh);
      max-height: var(--fw-nakyva, 95dvh);
      overflow: hidden;
      box-sizing: border-box;
      flex-direction: column;
    }
    /* ID + luokka (1,1,0) voittaa `.screen.active`n (0,2,0) tarkkuudella
       eikä järjestyksellä — ks. CLAUDE.md:n järjestysansa. */
    #fallingScreen.active { display: flex; }

    #fallingScreen .game-header {
      margin-bottom: 8px;
    }

    #fallingScreen .batch-info {
      color: rgba(255,255,255,0.7);
    }

    .fw-score {
      font-size: 16px;
      font-weight: 700;
      color: #eee;
    }

    .fw-remaining {
      font-size: 13px;
      color: rgba(255,255,255,0.5);
      font-weight: 600;
    }

    #fallingScreen .quit-btn {
      background: rgba(255,255,255,0.1);
      border-color: rgba(255,255,255,0.2);
      color: rgba(255,255,255,0.6);
    }

    #fallingScreen .quit-btn:hover {
      background: rgba(231,76,60,0.3);
      border-color: var(--vaara);
      color: var(--vaara);
    }

    .fw-arena {
      position: relative;
      width: 100%;
      /* Venyy jäljelle jäävään tilaan. Kiinteä 400px oli se toinen puoli
         samasta viasta: pienellä ruudulla se ei mahtunut, isolla se jätti
         tilaa käyttämättä. */
      flex: 1 1 auto;
      min-height: 90px;
      height: auto;
      border-radius: 12px;
      background: linear-gradient(180deg, #16213e 0%, #0f3460 100%);
      overflow: hidden;
      margin-bottom: 12px;
      border: 2px solid rgba(69, 6, 147, 0.3);
    }

    .fw-danger-zone {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 50px;
      background: linear-gradient(0deg, rgba(231,76,60,0.25) 0%, transparent 100%);
      border-top: 2px dashed rgba(231,76,60,0.4);
      pointer-events: none;
    }

    .fw-word {
      position: absolute;
      padding: 8px 18px;
      border-radius: 10px;
      font-size: 18px;
      font-weight: 700;
      color: white;
      /* Yksi brändipohja kaikille — ks. game-falling.js, värit poistettu
         24.8.2026. Valkoinen ääriviiva erottaa päällekkäin osuvat sanat
         toisistaan; valkoisella areenalla se näkyy vain siellä missä sitä
         tarvitaan. `outline` eikä `border`, jottei laatikon koko muutu,
         eikä `box-shadow`, jonka .fw-active tarvitsee hehkulleen. */
      background: var(--brand-primary);
      outline: 2px solid #fff;
      white-space: nowrap;
      transition: background 0.3s, transform 0.3s;
      text-shadow: 0 1px 3px rgba(0,0,0,0.4);
      cursor: default;
      z-index: 2;
    }

    .fw-word.fw-active {
      box-shadow: 0 0 18px rgba(69, 6, 147, 0.7);
      transform: scale(1.08);
    }

    .fw-word.fw-correct {
      background: var(--onnistui) !important;
      animation: fwPop 0.4s ease forwards;
    }

    .fw-word.fw-missed {
      background: var(--vaara) !important;
      opacity: 0.7;
    }

    @keyframes fwPop {
      0% { transform: scale(1); }
      50% { transform: scale(1.3); opacity: 1; }
      100% { transform: scale(0); opacity: 0; }
    }

    @keyframes fwMissedFlash {
      0% { opacity: 1; }
      100% { opacity: 0; transform: translateY(20px); }
    }

    .fw-word .fw-en-reveal {
      display: block;
      font-size: 12px;
      font-weight: 600;
      opacity: 0.8;
      margin-top: 2px;
    }

    .fw-input-row {
      display: flex;
      gap: 10px;
      align-items: center;
      flex: 0 0 auto;      /* ei kutistu areenan puolesta */
    }

    .fw-input {
      flex: 1;
      min-width: 0;
      padding: 14px 18px;
      font-size: 20px;
      font-weight: 700;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      border: 3px solid rgba(69, 6, 147, 0.5);
      border-radius: 12px;
      background: rgba(255,255,255,0.1);
      color: white;
      outline: none;
      transition: border-color 0.3s;
    }

    .fw-input:focus {
      border-color: var(--brand-primary);
      background: rgba(255,255,255,0.15);
    }

    .fw-input::placeholder {
      color: rgba(255,255,255,0.3);
    }

    .fw-input.fw-flash-correct {
      border-color: var(--onnistui);
      background: rgba(46,204,113,0.2);
    }

    .fw-input.fw-flash-wrong {
      border-color: var(--vaara);
      background: rgba(231,76,60,0.2);
      animation: wrongShake 0.4s ease;
    }

    .fw-submit-btn {
      padding: 14px 20px;
      font-size: 22px;
      font-weight: 700;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      background: var(--brand-gradient);
      color: white;
      border: none;
      border-radius: 12px;
      cursor: pointer;
      transition: transform 0.2s;
    }

    .fw-submit-btn:hover {
      transform: scale(1.05);
    }

    @media (max-width: 500px) {
      .fw-arena { height: 55vh; height: 55dvh; }
      .fw-word { font-size: 15px; padding: 6px 14px; }
      .fw-input { font-size: 17px; padding: 12px 14px; }
    }

    .batch-next-section {
      text-align: center;
      margin-top: 20px;
    }

    .btn-next-batch {
      background: linear-gradient(135deg, #43e97b, #38f9d7);
      color: #1a4731;
      padding: 15px 35px;
      border: none;
      border-radius: 12px;
      font-size: 18px;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.3s;
      font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
      animation: pulse 1.5s ease-in-out infinite;
    }

    .btn-next-batch:hover {
      transform: translateY(-3px);
      box-shadow: 0 10px 25px rgba(67, 233, 123, 0.4);
    }

    @keyframes pulse {
      0%, 100% { box-shadow: 0 0 0 0 rgba(67, 233, 123, 0.4); }
      50% { box-shadow: 0 0 0 10px rgba(67, 233, 123, 0); }
    }

    .trouble-words-panel {
      background: rgba(255,255,255,0.1);
      backdrop-filter: blur(10px);
      border-radius: 15px;
      padding: 20px;
      margin-top: 25px;
      color: white;
    }

    .trouble-words-panel h3 {
      font-size: 16px;
      margin-bottom: 12px;
    }

    .trouble-word-chip {
      display: inline-block;
      background: rgba(231, 76, 60, 0.15);
      border: 1px solid rgba(231, 76, 60, 0.3);
      color: rgba(255,255,255,0.9);
      padding: 5px 12px;
      border-radius: 20px;
      font-size: 12px;
      margin: 3px;
    }

    /* ===== VOITTORUUTU (2026-08-18) =====================================
       Marko: "Voittoruutu saisi olla selkeästi erilainen kuin peliruutu.
       Siinä saa vähän revitellä."

       B1b linjasi valkoisen KANGAAKSI — mutta se koskee TYÖSKENTELYnäkymiä.
       Voittoruutu on hetki, ei työkalu, ja siksi se saa rikkoa säännön
       tarkoituksella: täysi väripinta erottaa sen pelistä yhdessä silmän-
       räpäyksessä. Juuri se, että muu sovellus on vaalea, tekee tästä
       tapahtuman. Duolingo tekee saman (ks. NOTES: "Uusi sana", juhlinta). */
    #resultsScreen {
      position: fixed;
      inset: 0;
      z-index: 900;
      /* Ylikirjoitettava (2026-08-18): .screen/.main-card antoivat
         max-height: 95vh ja border-radius: 20px → väripinta jäi 41 px
         vajaaksi ja kulmat pyöristyivät vaaleiksi. Juhlaruudun pitää
         täyttää koko ruutu reunasta reunaan. */
      max-width: none;
      max-height: none;
      border-radius: 0;
      margin: 0;
      /* PYSTYSUORA eikä vino, ja kangas samaan loppusävyyn — sama
         resepti kuin latausruudussa. Vinon liukuvärin alareuna on eri
         sävy joka kohdassa, joten jos iOS leikkaa kiinteän kerroksen
         ruudun alalaitaan, sauma näkyy. Pystysuoran alin rivi on koko
         leveydeltä loppusävy, joten se kohtaa tasaisen kankaan
         saumatta. Ks. memory/decisions.md. */
      /* Pohja muuttujista: tasonousussa juhlaruutu ottaa norsun värin
         (ks. .juhla-*), muuten brändivioletti. */
      /* ⚠️ EI `background`-LYHENNETTÄ: kuviokerros tulee erikseen, ja
         lyhenne nollaisi sen joka kerta kun pohjaa säädetään. */
      background-color: var(--juhla-ala, var(--brand-primary-deep));
      /* ⚠️ NELJA KERROSTA, SAMA KUIN #lunastusScreen (css/norsu.css).
         Marko 3.9.: *"erän jälkeisen juhlaruudun tausta on vanhaa
         kuosia."* Tama on SE ruutu jota han tarkoitti — tasonorsun juhla
         (`#lunastusScreen`) korjattiin ensin, ja se on eri ruutu.
         Mitattu: tama luki `--juhla-pinta`n (vanha proseduraalinen)
         muttei `--juhla-kuosi`a (valokuva MASTERY_TIERSista).
         Kerrosjarjestys ja perustelut: css/norsu.css `#lunastusScreen`. */
      background-image:
        radial-gradient(ellipse 70% 50% at 50% 42%,
          rgba(0,0,0,.28) 0%, rgba(0,0,0,.07) 55%, transparent 100%),
        var(--juhla-pinta, none),
        var(--juhla-kuosi, none),
        linear-gradient(180deg, var(--juhla-yla, var(--brand-primary)) 0%,
                                var(--juhla-ala, var(--brand-primary-deep)) 100%);
      background-size: auto, var(--juhla-pinta-koko, auto), cover, auto;
      background-repeat: no-repeat, repeat, no-repeat, no-repeat;
      background-position: center, center, center, center;
      background-blend-mode: normal, normal, soft-light, normal;
      color: #fff;
      display: none;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      /* Kiinteä ruutu on bodyn pehmusteen ulkopuolella, joten turva-alue
         tähän — ja nimenomaan TÄHÄN lyhenteeseen. Erillinen
         `padding-top` samassa lohkossa hävisi tälle riville
         (mitattu 23.8.2026: 24px, ei 47px). */
      /* VÄLJEMMÄKSI (Marko 23.8.: "Koontiruutu on liian tiukka, vaikka
         siinä on tilaa vaikka kuinka paljon"). Sisältö keskelle,
         nappirivi omaan päähänsä — `justify-content: center` puristi
         kaiken yhteen kasaan keskelle ja jätti molemmat päät tyhjiksi. */
      padding: calc(24px + var(--norsu-turva-yla)) 22px calc(24px + env(safe-area-inset-bottom));
      justify-content: flex-start;
      gap: 0;
      text-align: center;
      overflow-y: auto;
      animation: resultsIn 0.35s ease;
    }
    #resultsScreen.active { display: flex; }

    @keyframes resultsIn {
      from { opacity: 0; }
      to   { opacity: 1; }
    }

    /* Palkinto on sankari: iso, ja saapuu pomppaamalla */
    .results-hero {
      font-size: 92px;
      line-height: 1;
      margin-bottom: 6px;
      /* ⚠️ KAARE (4.9.2026) — sama korjaus kuin `.lun-norsu`ssa.
         Tama elementti kantoi ENNEN seka `heroPop`in etta `.tier-merkki`n
         maskin, eli tasan sen yhdistelman joka tuottaa nelion Safarissa:
         animaatio promotoi kompositiokerroksen, ja `filter` laskee
         kerroksen laatikosta. Nyt maski on sisakkaisessa `<span>`issa
         (js/utils.js `resHeroMerkki`) joka ei animoidu. */
      display: flex; align-items: center; justify-content: center;
      animation: heroPop 0.55s cubic-bezier(.2,1.4,.35,1) 0.1s both;
      filter: drop-shadow(0 6px 18px rgba(0,0,0,0.25));
    }
    @keyframes heroPop {
      0%   { transform: scale(0.3) rotate(-12deg); opacity: 0; }
      70%  { transform: scale(1.12) rotate(3deg);  opacity: 1; }
      100% { transform: scale(1) rotate(0deg); }
    }

    .results-award-name {
      font-size: 15px;
      font-weight: 700;
      letter-spacing: 3px;
      text-transform: uppercase;
      color: var(--brand-highlight);
      margin-bottom: 18px;
    }

    .results-score {
      font-size: 64px;
      font-weight: 700;
      line-height: 1.05;
      margin-bottom: 2px;
      color: #fff;
      background: none;
      -webkit-text-fill-color: #fff;
    }
    .results-score-label {
      font-size: 13px;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      color: rgba(255,255,255,0.75);
      margin-bottom: 22px;
    }

    .results-message {
      font-size: 16px;
      color: rgba(255,255,255,0.92);
      margin-bottom: 26px;
      line-height: 1.5;
      max-width: 340px;
    }

    /* Ensisijainen toiminto isona pillerinä — ei pientä ympyrää */
    .results-primary-btn {
      width: 100%;
      max-width: 340px;
      padding: 17px;
      border: none;
      border-radius: 10px;
      background: var(--brand-highlight);
      color: #3a2a00;
      font-size: 17px;
      font-weight: 700;
      font-family: 'Poppins', -apple-system, sans-serif;
      cursor: pointer;
      box-shadow: 0 5px 0 #d9a600;
      transition: transform 0.12s, box-shadow 0.12s;
    }
    .results-primary-btn:active {
      transform: translateY(3px);
      box-shadow: 0 2px 0 #d9a600;
    }

    .results-secondary-btn {
      margin-top: 14px;
      padding: 10px 18px;
      border: none;
      background: none;
      color: rgba(255,255,255,0.8);
      font-size: 14px;
      font-weight: 600;
      font-family: 'Poppins', -apple-system, sans-serif;
      cursor: pointer;
    }
    .results-secondary-btn:hover { color: #fff; }

    .results-stars { margin-bottom: 10px; text-align: center; }

    #wrongWordsContainer { width: 100%; max-width: 340px; }

    /* ===== MATALA RUUTU: TULOSRUUDUN ON MAHDUTTAVA (Marko 20.9.2026) =====
       Marko: *«Näyttö ei muuten mahdu edes ihan kokonaan yhdelle ruudulle»*
       (läppäri). 📐 Mitattu oikeasta selaimesta Markon kuvan sisällöllä
       (8/10 · 100 % · timantti · kaksi vaikeaa lausetta · kolme nappia):

         ikkuna 768 px   sisältö 766 px   mahtuu tasan
         ikkuna 660 px   sisältö 766 px   YLIVUOTO 106 px, «Kertaa…» taitteen alla
         ikkuna 667 px   sisältö 804 px   YLIVUOTO 137 px  (iPhone SE)

       Koot ovat kiinteitä pikseleitä (norsu 92, luku 82) eikä yhtään
       korkeuteen perustuvaa sääntöä ollut. Ruutu on vieritettävä, joten
       mitään ei kadonnut — mutta **pääpainike oli taitteen alla**, ja
       lapsen seuraava teko ei saa olla piilossa.

       ⚠️ TYHJÄT LOHKOT VIEVÄT 64 px. `#finalScore`, `#finalScoreLabel`,
       `#resultStars` ja `#resultsMessage` ovat usein tyhjiä, mutta niiden
       marginaalit lasketaan silti. Se on tässä suurin yksittäinen erä.

       ⚠️ RAJA 780 px JÄTTÄÄ PUHELIMEN RAUHAAN: 812 ja 844 eivät osu tähän
       (mitattu: norsu pysyy 92 px:nä), mutta iPhone SE:n 667 osuu — ja se
       on oikein, koska siellä ylivuoto oli suurin. */
    @media (max-height: 780px) {
      #resultsScreen { padding: 14px 22px; }
      #resultsScreen > :empty { margin-top: 0; margin-bottom: 0; }
      #resultsScreen .results-hero { font-size: 64px; }
      #resultsScreen .tulos-luku { font-size: 56px; }
      #resultsScreen .tulos-luku .pros { font-size: 30px; }
      #resultsScreen .tulos-selite { margin-top: 6px; }
      #resultsScreen .tulos-laatat { margin-top: 12px; }
      /* Vaikeat sanat saa vierittää — napit eivät. */
      #resultsScreen #wrongWordsContainer { max-height: 30vh; overflow-y: auto; }
    }

    .results-title {
      display: none;
    }

    /* 18.8.: "Sessio valmis" -pilleri poistettu voittoruudusta. "Sessio" on
       sisäistä sanastoa, ja ruutu kertoo jo POKAALI + 3/3 — pilleri ei lisännyt
       mitään, mutta rikkoi värillisen pinnan valkoisella laatikolla. */
    #resultsScreen .batch-summary { display: none; }

    .batch-summary {
      background: #f5f5ff;
      border-radius: 10px;
      padding: 10px;
      margin-bottom: 10px;
      text-align: center;
      font-size: 13px;
      color: var(--teksti-vahva);
    }

    /* ===== TROPHY ===== */
    .trophy-display {
      font-size: 80px;
      margin-top: 10px;
      animation: trophyAppear 0.8s ease-out;
      filter: drop-shadow(0 4px 15px rgba(255, 215, 0, 0.5));
    }

    @keyframes trophyAppear {
      0% { transform: scale(0) rotate(-20deg); opacity: 0; }
      50% { transform: scale(1.3) rotate(5deg); opacity: 1; }
      70% { transform: scale(0.9) rotate(-3deg); }
      100% { transform: scale(1) rotate(0deg); }
    }

    /* ===== CONFETTI ===== */
    .confetti {
      position: fixed;
      /* Z-INDEX PUUTTUI (23.8.2026). Marko: "konfetit jäi taas saamatta."
         `z-index: auto` maalautuu kerroksessa 0, eli juhla- ja
         lunastusruudun (900/950) ALLE — konfetit satoivat koko ajan, vain
         ruudun takana. Splash on 9999, joten tämä jää sen alle. */
      z-index: 9998;
      pointer-events: none;
      font-size: 22px;
      will-change: transform, opacity;
      contain: strict;
      animation: confettiFall 1.6s ease-out forwards;
    }

    @keyframes confettiFall {
      0% { transform: translateY(0) scale(0.5) rotate(0deg); opacity: 1; }
      100% { transform: translateY(60vh) scale(1.1) rotate(var(--rot, 360deg)); opacity: 0; }
    }

    /* ===== RETRY MESSAGE ===== */
    .retry-message {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      color: white;
      padding: 20px;
      border-radius: 12px;
      margin-bottom: 30px;
      text-align: center;
      font-weight: 700;
      font-size: 18px;
    }

    /* ===== RESPONSIVE ===== */
    @media (max-width: 600px) {
      .container {
        padding: 8px;
      }

      .login-title {
        font-size: 36px;
      }

      .bite-grid {
        grid-template-columns: 1fr;
        gap: 12px;
      }

      .bite-card {
        padding: 20px 16px;
      }

      .bite-map-title {
        font-size: 22px;
      }

      .bite-map-subtitle {
        font-size: 15px;
      }

      .finnish-word {
        font-size: 26px;
        padding: 14px 18px 18px;
      }

      .results-score {
        font-size: 48px;
      }

      .results-buttons {
        flex-direction: column;
        align-items: stretch;
      }

      .results-buttons .btn {
        max-width: 100%;
      }

      .options-grid {
        grid-template-columns: 1fr;
      }

      #gameScreen {
        padding: 15px;
        border-radius: 16px;
      }

      #fallingScreen {
        padding: 10px 12px 12px;
        border-radius: 16px;
        max-height: 100vh;
        max-height: -webkit-fill-available;
      }

      .fw-arena {
        height: 55vh;
        height: 55dvh;
      }

      .fw-word {
        font-size: 14px;
        padding: 5px 12px;
      }

      .fw-input {
        font-size: 16px;
        padding: 10px 12px;
      }

      .fw-submit-btn {
        padding: 10px 16px;
        font-size: 18px;
      }

      .trophy-display {
        font-size: 60px;
      }

      .player-info {
        font-size: 12px;
        padding: 8px 14px;
      }

      .player-name-row {
        font-size: 16px;
      }

      .medal-counts {
        gap: 8px;
        font-size: 13px;
      }
    }

    /* Extra small screens like iPhone 12 mini */
    @media (max-width: 380px) {
      .container {
        padding: 4px;
      }

      #loginScreen {
        padding: 18px 12px;
      }

      .login-title {
        font-size: 30px;
        margin-bottom: 6px;
      }

      .login-subtitle {
        font-size: 13px;
        margin-bottom: 20px;
      }

      .bite-map-title {
        font-size: 20px;
      }

      .bite-map-subtitle {
        font-size: 13px;
      }

      .bite-card {
        padding: 16px 12px;
        border-radius: 12px;
      }

      .finnish-word {
        font-size: 22px;
        padding: 12px 14px 16px;
      }

      #gameScreen {
        padding: 12px;
        border-radius: 14px;
      }

      .gf-sentence {
        font-size: 15px;
        padding: 14px 10px;
      }

      .gf-option-btn {
        font-size: 11px;
        padding: 8px 4px;
      }

      #fallingScreen {
        padding: 8px 8px 10px;
      }

      .fw-arena {
        height: 50vh;
        height: 50dvh;
      }

      .fw-word {
        font-size: 13px;
        padding: 4px 10px;
      }

      .fw-input {
        /* ⚠️ 16px EIKA 15px (4.9.2026). Tama oli ainoa saanto joka vei
           kentan alle iOS:n 16px:n rajan 375px:lla, ja kohdistus
           zoomasi ruudun. Tiivis taytto jaa — vain fontti nousee. */
        font-size: 16px;
        padding: 8px 10px;
      }

      .fw-submit-btn {
        padding: 8px 14px;
        font-size: 16px;
      }

      .results-score {
        font-size: 40px;
      }

      .trophy-display {
        font-size: 50px;
      }

      .data-transfer-section {
        margin-top: 16px !important;
        padding-top: 12px !important;
      }
    }

    /* ===== SCROLLBAR ===== */
    ::-webkit-scrollbar {
      width: 8px;
    }

    ::-webkit-scrollbar-track {
      background: #f1f1f1;
      border-radius: 10px;
    }

    ::-webkit-scrollbar-thumb {
      background: var(--brand-primary);
      border-radius: 10px;
    }

    ::-webkit-scrollbar-thumb:hover {
      background: var(--brand-primary-deep);
    }

    /* Error report popup (long-press to report) */
    .report-overlay {
      position: fixed; top: 0; left: 0; right: 0; bottom: 0;
      z-index: 10000; background: rgba(0,0,0,0.35);
      display: flex; align-items: center; justify-content: center;
      animation: fadeIn 0.2s ease;
    }
    .report-card {
      background: #fff; border-radius: 18px; padding: 24px 22px;
      max-width: 320px; width: 90%; text-align: center;
      box-shadow: 0 12px 40px rgba(0,0,0,0.25);
      animation: bigBadgePop 0.3s ease;
    }
    .report-word {
      font-size: 1.1rem; font-weight: 700; color: var(--teksti);
      background: #f0f0f5; border-radius: 10px; padding: 10px 14px;
      margin: 10px 0 14px; word-break: break-word;
    }
    .report-word .report-word-pair {
      display: flex; justify-content: center; gap: 8px; align-items: center;
    }
    .report-word .rw-source { color: var(--brand-primary); }
    .report-word .rw-arrow { color: var(--teksti-hento); font-size: 0.9rem; }
    .report-word .rw-target { color: var(--vaara); }
    .report-input {
      width: 100%; padding: 10px 12px; border: 2px solid var(--raja-vaalea);
      /* ⚠️ `1rem` EIKA `0.95rem` (4.9.2026): 0.95 × 16 = 15,2px, eli
         alle iOS:n 16px:n rajan → Safari zoomaa kohdistettaessa.
         Loytyi lapiluvulla joka kysyi *jokainen saanto joka voi osua
         syottokenttaan* — ei nimeltä, koska nimi ei kerro mihin
         valitsin osuu. `rem`-arvo oli piilossa kaikilta px-hauilta. */
      border-radius: 10px; font-size: 1rem; margin-bottom: 14px;
      box-sizing: border-box; font-family: inherit;
    }
    .report-input:focus { border-color: var(--brand-primary); outline: none; }
    .report-btn-send {
      background: var(--vaara); color: #fff; border: none; border-radius: 12px;
      padding: 12px 28px; font-size: 1rem; font-weight: 600; cursor: pointer;
      width: 100%; margin-bottom: 8px;
    }
    .report-btn-send:active { transform: scale(0.97); }
    .report-btn-cancel {
      background: none; border: none; color: var(--teksti-kevyt); font-size: 0.9rem;
      cursor: pointer; padding: 6px;
    }
    .report-sent {
      color: #27ae60; font-weight: 600; font-size: 1rem;
      padding: 10px 0;
    }
    .reportable { -webkit-user-select: none; user-select: none; }

/* ⚠️ `#roleRequestForm input` SIIRRETTIIN `css/norsu.css`:ään luokaksi `.lunastus-koodi`
   (aikuisen-nakyma §73, 16.9.2026): sama koodikenttä on nyt myös *Kappaleet ja koodit*
   -kortissa. Siirretty, ei kopioitu. */

/* ═══════════════════════════════════════════════════════════════════
   POP-UP: ARKKI KESKELLE (2.9.2026)
   ═══════════════════════════════════════════════════════════════════
   Marko sahkopostin vahvistuksesta: *"Tama ilmoitus nakyy nyt rumasti
   tassa osittain kaiken paalla. Voisiko tama olla vain pop-up?"*

   ⚠️ SYY EI OLE ULKOASU VAAN SIJAINTI. Alalaidan arkki paattyy
   sisallon mittaan, ja sen ALLE jaa nakyviin se mita ruudulla sattuu
   olemaan — Markon tapauksessa aloitusruudun Merkitysten-logo, puoliksi
   kortin alta. Mitattu 390x844: kortti 529-768, logo 711. Omalla
   korkeudellani kortti peitti sen, hanen laitteellaan ei.

   Keskitetty dialogi poistaa sen RAKENTEELLISESTI: kun kortti ei ole
   kiinni alareunassa, sen alle ei jaa reunaa jossa mitaan voisi nakya.

   ⚠️ MODIFIOIJA, EI UUSI KOMPONENTTI. `norsuArkkiAvaa` on yksi lahde
   kaikille arkeille (lauma, kauppa, unohdin-ohje). Toinen komponentti
   ajautuisi siita erilleen; tama on sama arkki eri asemoinnilla.

   ⚠️ Ja veto-ele jaa pois: alaspain vetaminen on alalaidan arkin ele,
   ja keskella se olisi opittu liike joka ei tee mitaan. */
.bm-arkki.arkki-keskella { align-items: center; }
.bm-arkki.arkki-keskella .bm-arkki-kortti {
  border-radius: 20px;
  margin: 0 14px;
  /* ⚠️ PROSENTTI, EI `vh` (2.9.2026, toinen kierros). `86vh` mitataan
     IKKUNASTA, mutta `.bm-arkki` paattyy jo navin ylareunaan
     (`bottom: var(--norsu-nav-h)`). Kortti sai siis olla korkeampi kuin
     tila johon se mahtuu, ja emojinappaimiston kanssa se ulottui navin
     alle: napit ja onnistumisviesti jaivat peittoon.

     Prosentti on suhteessa ARKKIIN, joten se ei voi ylittaa kaytettavissa
     olevaa tilaa — eika navin korkeutta tarvitse arvata. */
  max-height: 92%;
  /* Pystysuunnan flex: otsikko ja napit pysyvat nakyvissa ja VAIN
     keskiosa vierittyy. Ilman tata koko kortti vierittyy ja
     "Aseta salasana" katoaa juuri kun se pitaisi painaa. */
  display: flex;
  flex-direction: column;
  transform: translateY(0) scale(0.97);
  transition: transform 0.18s ease, opacity 0.18s ease;
}
.bm-arkki.arkki-keskella.auki .bm-arkki-kortti { transform: translateY(0) scale(1); }
.bm-arkki.arkki-keskella .bm-arkki-kahva { display: none; }
.bm-arkki.arkki-keskella .bm-arkki-sisalto {
  display: flex; flex-direction: column; min-height: 0;
}
/* Vain tama osa vierittyy. Luokka annetaan siella missa sisalto on
   pitkaa (emojinappaimisto); muut arkit eivat sita tarvitse. */
.bm-arkki.arkki-keskella .arkki-vieritys {
  overflow-y: auto; min-height: 0; -webkit-overflow-scrolling: touch;
}

/* ===== B3: RIVI AIKUISELLE VIERAAN POLKUNAKYMASSA (C5, 10.9.2026) =====
   Aikuisen ovi pelin alareunassa. Rivi eika nappi: se ei saa kilpailla
   lapsen polun kanssa (B3).

   ⚠️ Sijainti on navipalkin YLAPUOLELLA, ja korkeus luetaan
   `--norsu-nav-h`:sta eika kirjoiteta lukuna. CLAUDE.md: navipalkin
   korkeus oli kolmessa paikassa kolmena eri lukuna (76 / 74 / 64), ja
   muuttuja oli olemassa koko ajan.

   ⚠️ Pyoristys 12 px eika 999 px — UI-periaate 4. */
.vieras-aikuisrivi {
  display: block;
  width: calc(100% - 32px);
  max-width: 420px;
  margin: 18px auto calc(var(--norsu-nav-h, 76px) + 18px);
  padding: 12px 14px;
  border: 1px dashed var(--raja-tumma, #cfcfd4);
  border-radius: 12px;
  background: transparent;
  color: var(--teksti-himmea, #6b6b73);
  font-family: inherit;
  font-size: 13.5px;
  line-height: 1.4;
  text-align: center;
  cursor: pointer;
}
.vieras-aikuisrivi:hover,
.vieras-aikuisrivi:focus-visible {
  border-color: var(--brand-primary, #6b3fa0);
  color: var(--brand-primary, #6b3fa0);
}

/* `.ostohalu-syyt` siirretty css/norsu.css:aan 11.9.2026: `js/ostohalu.js`
   ajetaan myos velhossa (kiintion seina), eika upload.html lataa tata
   tiedostoa. */

/* ===== AIKUISEN KOTI V1 (erä 3, 14.9.2026) — js/koti.js =====================
   Valkoinen kangas, rivit pyöristettyinä suorakaiteina (12 px), yksi täysvärinen
   teko. Putken nolla on himmeä eikä punainen: se on luku, ei tuomio.
   *Pelaa itse* on hiljainen rivi, koska se on aikuisen oma tekeminen
   (aloitusputki §23.3) — ja samalla mittaus siitä käytetäänkö sitä. */
/* ===== KODIN OMA YLALAITA (C158, Markon paatos 29.9.2026) ==================
   🔴 MITATTU VIKA JOTA TAMA KORJAA: Koti piilottaa pelin ylapalkin
   (`norsu-ylapalkki-piiloon`), jolloin `--norsu-ylapalkki-h` putoaa turva-alueen
   kokoiseksi — ja `.norsu-sivu` alkaa VASTA sen alta. Turva-alueen kaista jaa
   siis lapinakyvaksi, ja sen lapi nakyy ALLA oleva polkunakyma: Markon
   puhelinkuvassa Kodin ylapuolella luki *«Tervehdykset ja tutustuminen»*.
   Piilotus siirsi polun otsikon juuri siihen kaistaan.

   ⇒ Koti alkaa `top: 0`:sta ja maalaa turva-alueen itse. Palkki ei vieri:
   vieritys siirtyy kortille, joten saldo ja 👤 ovat aina ulottuvilla.
   ⚠️ ID-valitsin on tahallinen: se voittaa `.ranking-overlay.norsu-sivu`n
   TARKKUUDELLA eika jarjestyksella (sama kuvio kuin `#groupsOverlay`illa). */
#kotiOverlay.norsu-sivu {
  top: 0;
  display: flex; flex-direction: column;
  /* 🔴 `align-items: stretch` ON PAKKO, JA SEN PUUTTUMINEN OLI MITATTU VIKA
     (Marko 29.9.2026: *«kirja ja norsu ja norsukuvake ovat kaikki keskella
     ylhaalla samassa kasassa»*).

     `.ranking-overlay` asettaa `align-items: center`, koska se on TEHTY
     keskittamaan yksi kortti. Kun tama saanto vaihtoi suunnan sarakkeeksi,
     `center` jai voimaan — ja sarakkeessa se tarkoittaa VAAKAKESKITYSTA:
     ylapalkki kutistui sisaltonsa levyiseksi ja valui keskelle. Kortti ei
     kutistunut, koska silla on oma `width: 100%`.

     ⚠️ Yleistys: kun vaihdat `flex-direction`in, tarkista MYOS `align-items` —
     sen merkitys kaantyi mukana. Vanha arvo ei ollut vaarin, se tarkoitti eri
     asiaa. */
  align-items: stretch;
  overflow: hidden;
}
#kotiOverlay.norsu-sivu > .ranking-card {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.koti-ylapalkki {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px;
  padding-top: calc(var(--norsu-turva-yla, 0px) + 8px);
  background: #fff;
  border-bottom: 1px solid var(--raja-vaalea, #e0e0e0);
}
/* Nimi on OTSIKKO eika ovi: keskella, ei painike (Markon linjaus 29.9.2026).
   ⚠️ `flex: 1` + `text-align: center` pitaa sen ruudun keskella VAIN jos
   reunapainikkeet ovat yhta leveat. Ne eivat ole (kuvake vs. laatta), joten
   keskitys tehdaan absoluuttisesti — muuten otsikko liikkuu luvun pituuden
   mukaan, ja se nakyy heti kun saldo vaihtuu 9 → 10. */
.koti-ylapalkki { position: relative; justify-content: space-between; }
.koti-yp-nimi {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: calc(var(--norsu-turva-yla, 0px) + 8px);
  font-size: 17px; font-weight: 800; color: var(--brand-primary, #450693);
  letter-spacing: -.01em; line-height: 34px; pointer-events: none;
}
.koti-yp-kirjat { font-size: 20px; }
.koti-yp-nappi {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 34px; padding: 0 2px;
  border: 0; border-radius: 10px; background: transparent;
  font-family: inherit; font-size: 22px; line-height: 1; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.koti-yp-nappi:active { background: var(--surface, #f6f4fa); }

.koti-pyynnot { margin: 6px -12px 2px; }
.koti-lohko {
  margin: 14px 2px 8px; text-align: left;
  font-size: 12.5px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-faint, #8A8298);
}
.koti-lista { display: flex; flex-direction: column; gap: 8px; }
.koti-lapsi {
  display: flex; align-items: center; gap: 12px;
  width: 100%; box-sizing: border-box; padding: 12px 14px;
  border: 1px solid var(--raja-vaalea, #e0e0e0); border-radius: 12px; background: #fff;
  font-family: inherit; color: var(--ink); text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.koti-lapsi:active { border-color: var(--brand-primary); }
.koti-lapsi-kuva { font-size: 26px; flex: 0 0 auto; }
/* Laatta on kuva eika emoji: se saa oman korkeutensa `--laatta-h`:sta (C157). */
.koti-laatta { display: inline-flex; align-items: center; }
.koti-lapsi-teksti { flex: 1; min-width: 0; }
.koti-lapsi-nimi {
  display: block; font-size: 15.5px; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Pari «Harjoitellut N päivänä · Oppinut N uutta sanaa» (15.9.2026). Ei väriä:
   luvut ovat tietoa eivätkä tuomio (§10.4). Putki ja *pelasi tänään* poistuivat. */
.koti-lapsi-tila { display: block; margin-top: 2px; font-size: 13px; font-weight: 600; color: var(--teksti-vaimea, #888); }
/* ===== AIKAVALILEHDET LAPSEN SIVULLA (C158, 29.9.2026) ====================
   Nelja ikkunaa samasta `paivat`-kartasta. ⚠️ Suodatin on sen YLAPUOLELLA jota se
   rajaa (12.9. saanto): valilehdet ovat lukurivin ylla, kuten kielinauha on
   kirjojen yllapuolella. Muoto on sivun omien valilehtien pienempi sisar — ei
   uutta muotokielta, ja pyoristys 10 px (ei pilleri, CLAUDE.md periaate 4). */
.lauma-aikavalit {
  display: flex; gap: 6px; margin: 14px 0 6px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.lauma-aikavalit::-webkit-scrollbar { display: none; }
.lauma-aikavali {
  flex: 0 0 auto; padding: 6px 12px;
  border: 1px solid var(--raja-vaalea, #e0e0e0); border-radius: 10px; background: #fff;
  font-family: inherit; font-size: 13px; font-weight: 700; color: var(--teksti-vaimea, #888);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.lauma-aikavali.valittu {
  background: var(--brand-primary, #450693); border-color: var(--brand-primary, #450693);
  color: #fff;
}

/* Kodin aikavälilehdet — sama muoto kuin lapsen sivulla, ei uutta muotokieltä. */
.koti-aikavalit { display: flex; gap: 6px; margin: 0 0 8px; }
.koti-aikavali {
  flex: 0 0 auto; padding: 5px 12px;
  border: 1px solid var(--raja-vaalea, #e0e0e0); border-radius: 10px; background: #fff;
  font-family: inherit; font-size: 12.5px; font-weight: 700; color: var(--teksti-vaimea, #888);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.koti-aikavali.valittu {
  background: var(--brand-primary, #450693); border-color: var(--brand-primary, #450693);
  color: #fff;
}

/* ===== LAPSEN RIVIN NELJA LUKUA (C158, 29.9.2026) =========================
   Marko: *«liikaa tekstia… pitaisi olla harjoitellut paivat, aika, erien
   lukumaara ja opitut sanat»*. Kuvake ja luku vierekkain, nimi pienena alle —
   nimi on siella siksi etta kuvake yksin ei kerro MITA luku on, ja sisainen
   sana (`era`) ei saa vuotaa ruudulle. ⚠️ Rivi rullaa vaakaan kapealla
   naytolla eika katkea: nelja lukua ei mahdu 320 px:iin ilman kiristysta. */
.koti-luvut {
  display: flex; gap: 14px; margin-top: 6px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.koti-luvut::-webkit-scrollbar { display: none; }
.koti-luku { flex: 0 0 auto; display: flex; align-items: baseline; gap: 4px; }
.koti-luku-kuvake { font-size: 12px; opacity: .75; }
.koti-luku-arvo { font-size: 14px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.koti-luku-nimi { font-size: 11.5px; font-weight: 600; color: var(--teksti-vaimea, #888); }
.koti-luku-tyhja { font-size: 13px; font-weight: 600; color: var(--teksti-vaimea, #888); }
.koti-tyhja { font-size: 14px; color: var(--teksti-vaimea, #888); padding: 6px 2px; }
.koti-teot { display: flex; flex-direction: column; gap: 6px; margin-top: 20px; }
/* «Saanko kuvata itse?» (§27.4). Kortti eikä rivi: siinä on kysymys, perustelu ja
   kaksi vastausta, ja se on Kodin ainoa kohta jossa huoltaja PÄÄTTÄÄ jotain. */
.koti-lupa {
  margin: 10px 0 4px; padding: 12px 14px;
  border: 1px solid var(--raja-vaalea); border-radius: 12px; background: var(--surface);
  text-align: left;
}
.koti-lupa-kysymys { display: flex; align-items: center; gap: 10px; font-size: 15px; color: var(--ink); }
.koti-lupa-selite { margin-top: 8px; font-size: 13px; line-height: 1.5; color: var(--teksti-vaimea); }
.koti-lupa-napit { display: flex; gap: 8px; margin-top: 12px; }
.koti-lupa-napit > button { flex: 1; }
.koti-lupa-viesti { margin-top: 6px; font-size: 13px; color: var(--vaara); }

/* ===== HUOLTAJAN NAVI JA ➕-ARKKI (aikuisen-nakyma §27.3, 15.9.2026) =========
   Mitta: mallit/aikuisen-koti.html osio 3 (F · G · H · I).

   Sankari: 64 px, palkin yli pullistuva, valkoinen reunus leikkaa sen irti
   palkista, brändigradientti, valkoinen +. Sama rakenne kuin vanhalla ▶:lla,
   eri teko — ja siksi eri luokka (`.norsu-nav-play` ei palaa). Ei tekstiä alle:
   muillakaan paikoilla ei ole, ja ympyrä peittäisi sen (§20.4). */
.norsu-nav-anna {
  flex: 0 0 auto;
  width: 64px; height: 64px;
  margin: -24px 18px 0;
  padding: 0;
  border: 5px solid var(--surface);
  border-radius: 50%;
  background: var(--brand-gradient);
  color: var(--surface);
  font-family: inherit; font-size: 32px; font-weight: 400; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 16px rgba(69, 6, 147, 0.34);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.norsu-nav-anna:active { transform: translateY(2px); }
/* Sankari nousee palkin yli ja peittäisi sivun alimman rivin (Kodin
   «Kuvaa uusi kirja», §20.4). Tarkkuus (0,3,1) > `.ranking-overlay.norsu-sivu`
   (0,2,0), joten järjestys ei ratkaise. */
body.norsu-nav-huoltaja .ranking-overlay.norsu-sivu { padding-bottom: 30px; }
/* Sama syy arkissa: arkki päättyy navin yläreunaan, ja sankari nousee sen yli. */
body.norsu-nav-huoltaja .bm-arkki .bm-arkki-sisalto { padding-bottom: 24px; }

.norsu-anna-ots {
  padding: 4px 12px 0; text-align: left;
  font-size: 17px; font-weight: 700; color: var(--ink);
}
.norsu-anna-alle {
  display: flex; align-items: center; gap: 6px;
  padding: 2px 12px 8px; text-align: left;
  font-size: 13px; font-weight: 600; color: var(--teksti-vaimea);
}
.norsu-anna-vaihda {
  border: 0; background: none; padding: 0;
  font: inherit; color: var(--brand-primary); text-decoration: underline; cursor: pointer;
}
.norsu-anna-teksti { display: flex; flex-direction: column; min-width: 0; }
/* ⚠️ *Pelaa itse* -selite rivittyi 375–390 px leveydellä myös 12 px:llä (mitattu
   15.9.). Ratkaistu LYHENTÄMÄLLÄ teksti (*«Polku, seinä ja reppu»*, §28.3:4) —
   fontin pienentäminen olisi ollut mittauksen kiertäminen. */
.norsu-anna-teksti .selite {
  margin-top: 1px; font-size: 12.5px; font-weight: 500; color: var(--teksti-vaimea);
}
/* ===== ASENNUSRIVI (C67, 3.10.2026) — sama rivi Minä-arkissa ja Kodissa ==========
   Rivi on <div>, koska sen sisällä on kaksi nappia (Asenna ja ×): osoitin ei lupaa
   koko rivin painamista. Muotokieli on Norsun: pyöristetty suorakaide, ei pilleriä. */
.norsu-asennus { cursor: default; }
.norsu-asennus .norsu-anna-teksti,
.norsu-asennus .koti-lapsi-teksti { flex: 1; min-width: 0; }
.norsu-asennus-nappi {
  flex: 0 0 auto; padding: 7px 12px; border: none; border-radius: 10px;
  background: var(--brand-primary); color: #fff;
  font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
}
.norsu-asennus-torju {
  flex: 0 0 auto; width: 32px; height: 32px; border: none; border-radius: 10px;
  background: none; color: var(--teksti-vaimea, #888);
  font: inherit; font-size: 18px; line-height: 1; cursor: pointer;
}
/* Hallinto ja tilanvaihto erottuvat antamisesta ohuella viivalla (malli G, I). */
.bm-arkki-rivi.norsu-arkki-erotin {
  border-top: 1px solid var(--border); border-radius: 0; margin-top: 4px;
}

/* ===== VERBIN MUODOT (js/verbimuodot.js) ================================
   Imperfektiharjoitus opettajan lapun rivia varten (23.9.2026).

   ⚠️ TAMA LOHKO ON TARKOITUKSELLA LYHYT (25.9.2026). Ensimmainen versio
   maaritteli omat `.vm-prompt`-, `.vm-options`- ja `.vm-option`-tyylinsa,
   ja Markon tytar huomasi sen ruudulta: *«outo fontti … nakyi vain paljas
   sana»*. Mitattuna kysymyssana oli ainoa pelimuodossa joka ei ollut
   valkoisessa kortissa, ja `.vm-option` oli KOLMAS tapa piirtaa
   vastausnappi. Muoto tulee nyt `.finnish-word`- ja `.option-btn`-luokista
   kuten monivalinnassa; tanne jaa vain se mita muualla ei ole. */
.finnish-word .vm-fi {
  display: block;
  font-size: 16px;
  font-weight: 500;
  color: var(--teksti-himmea);
  margin-top: 6px;
}
.vm-feedback {
  min-height: 1.6em;
  text-align: center;
  margin: 0 16px 16px;
}
.vm-feedback .vm-ok { color: var(--onnistui); font-weight: 600; }
.vm-feedback .vm-ei { color: var(--vaara); }

/* ===== KAPPALEEN TEKSTIT JA VUOROPUHELUT (js/tekstit.js) ===============
   Opettajan ohje: «lue tekstikappale 2–3 kertaa niin että ymmärrät».
   Suomennos on piilossa kunnes riviä napautetaan — muuten silmä lukee sen
   eikä kohdekieltä, ja ymmärtämisestä tulee kääntämisen seuraamista. */
.tk-ohje {
  margin: 0 0 12px;
  font-size: .9rem;
  color: var(--teksti-himmea);
}
.tk-tyhja { color: var(--teksti-himmea); }
.tk-teksti {
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 10px 10px 6px;
  margin-bottom: 12px;
  background: #fff;
}
.tk-otsikko {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}
.tk-otsikko .tk-nimi {
  font-weight: 600;
  flex: 1;
  color: var(--teksti);
}
.tk-aani {
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #fff;
  padding: 4px 10px;
  cursor: pointer;
}
.tk-rivi {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  border: 0;
  border-radius: 10px;
  background: transparent;
  padding: 8px 8px;
  cursor: pointer;
  color: var(--teksti);
}
.tk-rivi + .tk-rivi { border-top: 1px solid var(--border); border-radius: 0; }
.tk-rivi .tk-kohde { display: block; }
.tk-rivi .tk-suomi {
  display: none;
  margin-top: 4px;
  font-size: .92rem;
  color: var(--teksti-himmea);
}
.tk-rivi.tk-auki .tk-suomi { display: block; }
.koe-kohta-luku {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: .92rem;
  margin: 4px 0 8px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #fff;
  color: var(--brand-primary);
  cursor: pointer;
}

/* Opettajan aihe nappina (js/koe.js, 23.9.2026): tämä on TEKO, ei valinta — siksi täysi
   brändiväri, toisin kuin viereinen «Lue teksti» joka on lisätieto. */
/* Sanamäärä napin oikeassa reunassa (Marko 26.9.2026): «▶ Tervehdykset  12».
   ⚠️ Luku kertoo jotain näkymätöntä — montako sanaa tämä harjoitus sisältää — joten se
   jää (UI-periaate 2). Numero on symbolin takana omana sirunaan, ei leipätekstinä. */
.koe-kohta-maara {
  float: right;
  font-size: .82rem;
  font-weight: 700;
  opacity: .78;
  padding-left: 10px;
}

.koe-kohta-aihe {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-weight: 600;
  margin: 6px 0 8px;
  padding: 12px 14px;
  border: 0;
  border-radius: 12px;
  background: var(--brand-primary);
  color: #fff;
  cursor: pointer;
}

/* «Merkitse nämä sanat» (js/koe.js, 23.9.2026): teko jota Norsu ehdottaa kun se ei
   tunnistanut riviä. Ääriviivat eikä täyttöä — opettajan oma aihe (.koe-kohta-aihe) on
   varmempi teko ja saa täyden brändivärin; tämä on ehdotus. */
.koe-kohta-merkitse {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: .92rem;
  font-weight: 600;
  margin: 6px 0 4px;
  padding: 11px 13px;
  border: 2px solid var(--brand-primary);
  border-radius: 12px;
  background: #fff;
  color: var(--brand-primary);
  cursor: pointer;
}

/* Yksi ovi merkintään (js/koe.js, 24.9.2026). Oma lohkonsa listan alla: opettaja voi
   nimetä tärkeimmät sanat monella tavalla, joten ovi ei voi olla yhden rivin varassa. */
.koe-merkinta-osa {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.koe-merkinta-otsikko {
  font-weight: 700;
  color: var(--teksti);
  margin-bottom: 2px;
}
.koe-merkinta-selite {
  font-size: .88rem;
  color: var(--teksti-himmea);
  margin-bottom: 8px;
  line-height: 1.45;
}

/* Miksi merkintanappia ei ole: haku kesken tai kirja ei ole aikuisen saatavilla.
   ⚠️ Rivi on olemassa siksi ettei tyhja kohta nayta samalta kuin rikkinainen nappi. */
.koe-merkinta-tila {
  font-size: .85rem;
  color: var(--teksti-himmea);
  background: var(--tausta-vaalea, rgba(0, 0, 0, .03));
  border-radius: 10px;
  padding: 8px 10px;
  line-height: 1.45;
}

/* ===== LAPSEN PIIRROS (C193, 1.10.2026) =====================================
   Pyöristetty suorakaide eikä pilleri (CLAUDE.md: painikkeet 10–12 px, kortit 12–16 px).
   Kangas on valkoinen; väri merkitsee jotain — tässä se ON se mitä lapsi valitsee. */
.piirto-overlay {
  position: fixed; inset: 0; z-index: 1500;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.piirto-kortti {
  background: var(--surface); border-radius: 16px; padding: 14px;
  width: min(100%, 440px); max-height: 100%; overflow: auto;
  display: flex; flex-direction: column; gap: 10px;
}
.piirto-sana { font-size: 20px; font-weight: 700; text-align: center; }
/* C197: tehtävänanto «kissa — a cat»: äidinkieli ensin, kohdekieli brändivärillä (opetettava sana). */
.piirto-kohde { color: var(--brand-primary); }
.piirto-canvas {
  width: 100%; aspect-ratio: 1 / 1; touch-action: none;
  border: 2px solid var(--raja-vaalea); border-radius: 12px; background: var(--surface);
}
.piirto-varit, .piirto-tyokalut, .piirto-napit, .piirto-leveydet { display: flex; gap: 8px; justify-content: center; }
/* C201: kymmenen tuoteväriä kahdessa viiden rivissä (36 px + 8 px väli = 212 px). */
.piirto-varit { flex-wrap: wrap; max-width: 212px; margin: 0 auto; }
.piirto-vari {
  width: 36px; height: 36px; border-radius: 10px; border: 3px solid transparent; cursor: pointer;
}
.piirto-leveys {
  width: 48px; height: 40px; border-radius: 10px; cursor: pointer; display: flex; align-items: center; justify-content: center;
  background: var(--surface); border: 2px solid var(--raja-vaalea);
}
.piirto-leveys span { display: block; border-radius: 50%; background: var(--ink, #222); }
.piirto-leveys.valittu { border-color: var(--brand-primary); }
.piirto-vari.valittu { border-color: var(--ink, #222); }
.piirto-tyokalu {
  min-width: 48px; height: 44px; border-radius: 10px; font-size: 22px; cursor: pointer;
  background: var(--surface); border: 2px solid var(--raja-vaalea);
}
.piirto-tyokalu.valittu { border-color: var(--brand-primary); }
.piirto-napit .button-primary, .piirto-napit .button-secondary { flex: 1; width: auto; }
.piirto-virhe { color: var(--vaara); font-size: 13px; min-height: 1em; text-align: center; }
.piirto-paneeli { display: flex; flex-direction: column; gap: 10px; }
/* C211: lapsen oma kuva sanan yllä (monivalinta, kirjoitus). Pieni: kysymys ja vaihtoehdot mahtuvat yhä. */
.sanan-kuva { display: flex; justify-content: center; margin: 0 0 8px; }
.sanan-kuva[hidden] { display: none; }
/* Pieni kuva putoavan kortin alussa (C211). ⚠️ `:not([hidden])` kantaa näkyvyyden — saman tarkkuuden
   `.sanan-kuva[hidden]` hävittäisi muuten järjestyksen mukaan. Negatiivinen pystymarginaali: kuva ei
   kasvata korttia, koska vaaraviiva luetaan kortin yläreunasta (mitattu 375 px: 29 px kuvalla ja ilman). */
.fw-word .sanan-kuva:not([hidden]) { display: inline-flex; vertical-align: middle; margin: -6px 8px -6px 0; }
.fw-word .sanan-kuva-kangas { width: 28px; height: 28px; border-radius: 6px; border-width: 1px; }
/* Parit: kuva KORVAA suomen sanan (Marko 2.10.2026) — kuva on kortin sisältö, ei sen koriste. */
.pairs-card.pairs-kuvakortti { padding: 6px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; }
.pairs-kuva-kangas { width: 72px; height: 72px; border-radius: 10px; display: block; }
.sanan-kuva-kangas {
  width: 88px; height: 88px; border-radius: 12px;
  border: 2px solid var(--raja-vaalea); background: var(--surface);
}
/* Tietokone ja vaakatabletti (Marko 2.10.2026): työkalut kankaan viereen, napit samalle ruudulle.
   Kangas mitoitetaan näkymän korkeudesta, jotta koko kortti mahtuu ilman vieritystä. */
@media (min-width: 720px) and (orientation: landscape) {
  .piirto-kortti {
    width: auto; max-width: 100%;
    display: grid; grid-template-columns: auto 232px; column-gap: 18px; row-gap: 10px; align-items: start;
  }
  .piirto-sana { grid-column: 1 / -1; }
  .piirto-canvas { width: min(60vw, calc(100vh - 130px)); }
  .piirto-paneeli { align-self: stretch; justify-content: flex-start; }
  .piirto-varit { margin: 0; }
  .piirto-tyokalut, .piirto-leveydet { justify-content: flex-start; flex-wrap: wrap; }
  .piirto-napit { flex-direction: column; margin-top: auto; }
}
/* C202: kuvatila — värit ja paksuudet eivät koske valokuvaa; vihje näkyy vain kuvalla. */
.piirto-kuvavihje { display: none; font-size: 13px; text-align: center; color: var(--teksti-himmea); }
.piirto-kuvatila .piirto-kuvavihje { display: block; }
.piirto-kuvatila .piirto-varit, .piirto-kuvatila .piirto-leveydet { visibility: hidden; }
.piirto-kuvatila [data-tyokalu] { opacity: .35; pointer-events: none; }
/* Galleria: omat kuvat ja huoltajan näkymä lapsen kuviin (Marko 2.10.2026).
   Kerros profiilin ja lapsisivun (1400) PÄÄLLÄ ja piirtoruudun (1500) ALLA: muokkaus avautuu galleriasta. */
.kg-overlay {
  position: fixed; inset: 0; z-index: 1450;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 16px; overflow-y: auto;
}
.kg-sisalto {
  background: var(--surface); border-radius: 16px; padding: 16px;
  width: 100%; max-width: 560px; margin: auto 0;
}
.kg-yla { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kg-otsikko { font-size: 18px; font-weight: 800; }
.kg-sulje { border: 0; background: none; font-size: 20px; padding: 4px 8px; cursor: pointer; }
.kg-ohje, .kg-tyhja { font-size: 13px; color: var(--teksti-himmea); margin: 8px 0 12px; }
.kg-tyhja { text-align: center; padding: 24px 8px; }
.kg-ruudukko { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.kg-kortti { border: 2px solid var(--border); border-radius: 12px; padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.kg-kortti.valittu { border-color: var(--brand-primary); grid-column: 1 / -1; }
.kg-kuva { border: 0; padding: 0; background: none; cursor: pointer; display: block; width: 100%; }
.kg-kangas { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; border-radius: 10px; }
.kg-kortti.valittu .kg-kangas { max-width: 320px; margin: 0 auto; }
.kg-sana { font-size: 14px; font-weight: 700; text-align: center; overflow-wrap: anywhere; }
.kg-teot { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; align-items: center; }
.kg-teot .button-primary, .kg-teot .button-secondary { width: auto; flex: 1 1 120px; }
.kg-kysymys { width: 100%; text-align: center; font-weight: 700; }
.kg-vaara { background: var(--vaara); border-color: var(--vaara); }
/* Muistisääntö (Marko 4.10.2026, js/muistisaanto.js): kevyt ja vapaaehtoinen. Kortissa teksti, kirjoituksen
   palautteessa nappi tai lapsen oma muistisääntö. Ikkuna galleriankin (1450) päällä. */
.kg-ms { font-size: 13px; color: var(--teksti); text-align: center; overflow-wrap: anywhere; }
.kg-kutsu { font-size: 13px; color: var(--teksti-himmea); text-align: center; }
.kg-vainms { aspect-ratio: 1 / 1; border-radius: 10px; background: var(--tint-yellow); font-size: 40px; }
.ms-palaute { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }
.ms-oma { font-size: 14px; color: var(--teksti); overflow-wrap: anywhere; text-align: center; }
.ms-nappi {
  border: 1px solid var(--border); background: var(--surface); color: var(--brand-primary);
  border-radius: 10px; padding: 6px 12px; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
}
.ms-overlay {
  position: fixed; inset: 0; z-index: 1500;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 16px; overflow-y: auto;
}
.ms-sisalto {
  background: var(--surface); border-radius: 16px; padding: 16px;
  width: 100%; max-width: 480px; margin: 40px 0 auto; display: flex; flex-direction: column; gap: 8px;
}
.ms-otsikko { font-size: 18px; font-weight: 800; }
.ms-sana { font-size: 16px; font-weight: 700; color: var(--brand-primary); overflow-wrap: anywhere; }
.ms-ohje { font-size: 13px; color: var(--teksti-himmea); }
.ms-kentta {
  width: 100%; box-sizing: border-box; border: 2px solid var(--border); border-radius: 12px;
  padding: 10px; font: inherit; font-size: 16px; resize: vertical;
}
.ms-virhe { font-size: 13px; color: var(--vaara); }
.ms-teot { display: flex; gap: 8px; }
.ms-teot .button-primary, .ms-teot .button-secondary { flex: 1 1 0; width: auto; }
.prof-kuvat-nappi, .lauma-kuvat-nappi { width: 100%; }
.piirra-tama-sana { padding: 0 12px 6px; font-size: 20px; font-weight: 700; }
.piirra-tama-napit { display: flex; gap: 8px; padding: 6px 12px 12px; }
.piirra-tama-napit .button-primary, .piirra-tama-napit .button-secondary { flex: 1; width: auto; }
.mk-kuvat { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; margin-bottom: 8px; }
.mk-kuvat canvas { width: 56px; height: 56px; border-radius: 10px; border: 1px solid var(--raja-vaalea); }
/* C194 Mikä piirtyy?: lapsen oma kuva piirtyy tähän. Valkoinen kangas, kortin pyöristys (16 px). */
.pt-kangas { display: block; width: min(64vw, 240px); height: auto; aspect-ratio: 1; margin: 4px auto 12px;
  border-radius: 16px; border: 1px solid var(--raja-vaalea); background: var(--surface); }
