/* Remppalaskuri — tyylit */

:root {
  --oranssi:        #E8730C;
  --oranssi-tumma:  #C25E06;
  --oranssi-vaalea: #FDF1E5;
  --teksti:         #1D2024;
  --teksti-himmea:  #6B7280;
  --viiva:          #C7CDD4;
  --viiva-tumma:    #98A1AB;
  --tausta:         #FFFFFF;
  --tausta-2:       #F7F8FA;
  --tausta-3:       #EDEFF3;
  --sininen:        #1F6FEB;
  /* Rakennerivin tausta: vaalea sininen 50 % lapinakyvyydella, jolloin
     valkoinen paistaa lapi ja rivi pysyy luettavana. Askon linjaus
     22.8.2026. */
  --sininen-rivi:   rgba(150, 190, 240, .5);
  --sininen-rivi-2: rgba(150, 190, 240, .72);
  --vihrea:         #12805C;
  /* Omasta kirjastosta tullut rivi: haalea vihrea 30 %. */
  --vihrea-oma:     rgba(120, 200, 160, .30);
  --vihrea-oma-2:   rgba(120, 200, 160, .45);
  --punainen:       #C0392B;
  --keltainen:      #B7791F;
  --varjo:          0 1px 3px rgba(20,25,35,.10), 0 6px 20px rgba(20,25,35,.08);
  --yla:            48px;
  --ala:            34px;
}

/* ------------------------------------------------------------- yonakyma

   Yksi luokka <html>-elementissa vaihtaa koko ohjelman. Se toimii vain
   siksi, etta varit tulevat naista muuttujista eivatka koodista — sama
   syy joka on kirjattu TYYLI.md:hen. Ainoa este oli 29 kovakoodattua
   `background: #fff`, jotka jaivat valkoisiksi luokasta riippumatta;
   ne osoittavat nyt muuttujaan.

   VALKOINEN TEKSTI ORANSSILLA JAA ENNALLEEN. `color: #fff` on 21
   paikassa, ja jokaisessa se on tekstia oranssilla tai tummalla
   pohjalla — se on oikea vari myos yolla. Vain tausta kaantyy.

   Merkitysvarit sailyttavat merkityksensa mutta kirkastuvat: tumma
   #C0392B ei erotu tummalta pohjalta, joten punainen, vihrea ja
   sininen saavat vaaleammat savyt. Oranssi kirkastuu vahan, koska
   #E8730C tummalla taustalla nayttaa ruskealta. */
html.yo {
  /* Yötilassa merkitysvärit ovat päivätilaa hillitymmät. Täysi neonoranssi
     levitti huomion koko pääryhmäriville, ja hover vaihtoi sen vielä eri
     sävyyn. Pehmeä meripihka erottaa toiminnon ilman silmien rasitusta. */
  --oranssi:        #D89045;
  --oranssi-tumma:  #BD7130;
  --oranssi-vaalea: #2A2119;
  --teksti:         #E6E9EE;
  --teksti-himmea:  #98A1AB;
  --viiva:          #2E343C;
  --viiva-tumma:    #454D57;
  --tausta:         #1A1E24;
  --tausta-2:       #121519;
  --tausta-3:       #242A32;
  --sininen:        #7EA7E5;
  --sininen-rivi:   rgba(60,110,190,.30);
  --sininen-rivi-2: rgba(60,110,190,.45);
  --vihrea:         #67B99B;
  --vihrea-oma:     rgba(40,140,100,.28);
  --vihrea-oma-2:   rgba(40,140,100,.42);
  --punainen:       #D9827B;
  --keltainen:      #D0AD69;
  --varjo:          0 1px 3px rgba(0,0,0,.50), 0 6px 20px rgba(0,0,0,.45);
}

/* Selaimen omat osat — vierityspalkit, kentat, valikot — seuraavat
   tata. Ilman sita kentat piirtyvat vaaleina keskelle tummaa sivua. */
html { color-scheme: light; }
html.yo { color-scheme: dark; }

/* RIVIEN SAVYT YOLLE.

   Muuttujat eivat riittaneet yksin. Ohjelmassa on parikymmenta vaaleaa
   rivitaustaa jotka on kirjoitettu suoraan heksana — valiotsikot,
   merkityt rivit — ja ne jaivat vaaleiksi kun teksti kaantyi
   vaaleaksi. Mitattu selaimesta: valiotsikkorivin tausta oli #E4E9F0 ja
   teksti #E6E9EE,
   eli rivi oli tayysin lukukelvoton.

   Savyt ovat lapinakyvia eivatka umpinaisia. Silloin sama sarja toimii
   kaikilla kolmella taustatasolla (--tausta, --tausta-2, --tausta-3)
   ilman etta jokaiselle pitaisi laskea oma heksa, ja rivin merkitys
   sailyy: sininen on yha sininen, vihrea vihrea. */
html.yo table.ruudukko tr.valiotsikko > td,
html.yo table.ruudukko tr.valiotsikko:hover > td { background: #232A33; }
html.yo table.ruudukko tr.valiotsikko.taso1 > td { background: #2A323C; }
html.yo table.ruudukko tr.valiotsikko.taso2 > td { background: #232A33; }
html.yo table.ruudukko tr.valiotsikko > td .himmea { color: #93A0AF; }

html.yo table.ruudukko tr:hover > td,
html.yo tr.lataus td { background: #202730; }

html.yo .taso.paketti { background: rgba(120,180,80,.20); color: #A9D98C; }
html.yo .taso.panos   { background: rgba(150,110,220,.22); color: #C6A9F2; }
html.yo tr.erotusrivi > td     { background: rgba(242,132,30,.15) !important; }

html.yo tr.valittu-rakenne > td       { background: rgba(63,191,143,.16) !important; }
html.yo tr.valittu-rakenne:hover > td { background: rgba(63,191,143,.24) !important; }

html.yo tr.merkitty.sininen   { background: rgba(92,156,255,.13); }
html.yo tr.merkitty.keltainen { background: rgba(224,169,58,.13); }
html.yo tr.merkitty.vihrea    { background: rgba(63,191,143,.13); }

html.yo td.rahasolu.kontti,
html.yo .aikohde.ilmainen { background: rgba(63,191,143,.12); }

html.yo table.tulostaulu tr.oletusrivi > td { background: rgba(92,156,255,.11); }

* { box-sizing: border-box; }

html, body {
  margin: 0; height: 100%; overflow: hidden;
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif;
  color: var(--teksti); background: var(--tausta-2);
  -webkit-font-smoothing: antialiased;
  cursor: default;
}

/* Teksti on maalattavissa. Aiemmin koko body oli user-select: none, jotta
   ohjelma tuntuisi työpöytäsovellukselta — mutta silloin rivin nimeä,
   hintaa tai koodia ei saanut kopioitua mihinkään, ja se maksoi enemmän
   kuin tuntuma. Maalaus estetään enää siellä missä se on oikeasti tiellä:
   raahauksen, painikkeiden ja valikkojen päällä. */
button, .painike, .ypainike, .kohta, .pikavalikko, .ratas, .nuoli,
.sarakekahva, .merkki {
  -webkit-user-select: none; user-select: none;
}

input, select, textarea, button { font: inherit; color: inherit; }
input, textarea { -webkit-user-select: text; user-select: text; }

/* ------------------------------------------------------------ yläpalkki */

/* YLÄPALKKI VALKOISEKSI 30.8.2026. Asko hyväksyi Archdesk-suuntaisen
   ilmemallin ("on parempi kuin meidän") 30 kilpailijan vertailun
   jälkeen; vertailu on ~/Desktop/Remppalaskuri-ilmevertailu.pdf.

   Syy ei ole maku vaan se, että oranssi teki palkissa kahta työtä
   kerralla: se oli sekä tunnus että tausta, jolloin sen päällä olevat
   painikkeet joutuivat erottumaan läpinäkyvillä valkoisilla sävyillä
   eivätkä erottuneet toisistaan. Palkissa oli neljä erinäköistä
   painiketta ja "Laskennat" oli oranssi oranssilla.

   Nyt oranssi tekee yhtä työtä: se on tunnus ja yksi päänappi. Muut
   ovat samaa ääriviivasarjaa.

   PÄÄRYHMÄRIVIN ORANSSI TÄYTTÖ EI MUUTU. Asko valitsi sen uudestaan
   30.8. kun hänelle näytettiin kolme vaihtoehtoa vierekkäin — ks.
   tr.laskelma-säännöt alempana. */
#yla {
  height: var(--yla); display: flex; align-items: center; gap: 10px;
  padding: 0 14px; background: var(--tausta); color: var(--teksti);
  border-bottom: 1px solid var(--viiva);
  position: relative; z-index: 20;
}
#yla .logo {
  font-weight: 700; letter-spacing: -.01em; font-size: 14px;
  white-space: nowrap;
}
#yla .logo-ai { color: var(--oranssi); }
#yla .nimi {
  font-size: 15px; font-weight: 600; margin-right: auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46vw;
}
#yla .nimi small { display: block; font-size: 12px; font-weight: 400;
                   color: var(--teksti-himmea); }

.ypainike {
  background: var(--tausta); border: 1px solid var(--viiva-tumma);
  color: var(--teksti); border-radius: 6px; padding: 6px 11px; cursor: pointer;
  white-space: nowrap; transition: background .12s;
}
.ypainike:hover { background: var(--tausta-2); }
/* Ylapalkin painikkeet jarjestellaan raahaamalla. Askon linjaus
   22.8.2026. Raahattava himmenee, ja kohde saa reunan siihen puoleen
   johon se pudotetaan — muuten kayttaja ei tieda kumpaan valiin nappi
   menee ennen kuin se on jo mennyt. */
/* -webkit-user-drag on PAKOLLINEN painikkeelle. Pelkka draggable=true
   ei riita: nappi nappaa mousedownin eika raahaus ala lainkaan, vaikka
   kasittelijat ovat paikallaan. Mitattu 22.8.2026 selaimesta —
   dragstart ja dragover toimivat ohjelmallisesti mutta hiirella ei
   tapahtunut mitaan. */
/* Osoitintapahtumat, ei HTML5-raahausta: mitattu ettei jalkimmainen
   toimi napilla lainkaan. touch-action: none estaa selainta ottamasta
   vetoa vieritykseksi kosketusnaytolla. */
.ypainike.raahattava { cursor: grab; touch-action: none; }
.ypainike.raahataan { opacity: .45; cursor: grabbing; }
.ypainike.palkkikohde { box-shadow: inset 0 0 0 2px var(--oranssi); }
.ypainike:active { background: var(--tausta-3); }
.ypainike.vahva { background: var(--oranssi); color: #fff; font-weight: 600;
                  border-color: var(--oranssi); }
.ypainike.vahva:hover { background: var(--oranssi-tumma); border-color: var(--oranssi-tumma); }

/* ------------------------------------------------------------- rakenne */

#runko { display: flex; height: calc(100% - var(--yla) - var(--ala)); }

/* Vasen sivupalkki poistettu 22.8.2026, Askon linjaus. Laskelmalista ja
   kirjastovalinta veivat 236 px leveydelta; laskelmiin paasee taulukosta
   ja kirjasto on ylapalkissa. Muuttuja --sivu jai pois samalla. */
/* SIVUN REUNUS. Asko 24.8.2026: "laitetaan reunoille marginaali. On
   kökkö kattoo iha reunasta, vaikka 2 cm."

   Reunus on VIERITTÄJÄSSÄ eikä sisällössä, jotta se koskee kaikkea
   kerralla — otsikkoa, laskentapuuta ja yhteenvetoa — eikä jokaista
   näkymää tarvitse muistaa erikseen. Sisempien lohkojen omat
   sivutäytteet nollataan samalla, muuten reunus olisi kaksinkertainen
   niissä ja yksinkertainen taulukossa.

   REUNUS ON PORTAINEN, ja syy on mitattu. Laskentapuu on Prismassa
   1 639 px leveä, ja se vieritti jo vaakasuunnassa 1 600 px:n ikkunassa
   ILMAN reunusta (39 px yli). Täysi 2 cm kummallakin puolella olisi
   vienyt ylityksen 190 pikseliin — reunus näyttäisi hyvältä ja
   taulukko olisi entistä hankalampi.

   Siksi 2 cm vasta kun ruudulla on sille tilaa (1 800 px), ja sitä
   kapeammalla sen verran että reuna erottuu. Sarakkeita voi vetää
   kapeammiksi, jolloin täysi reunus mahtuu pienemmällekin ruudulle. */
:root { --reunus: 10px; }
@media (min-width: 1100px) { :root { --reunus: 24px; } }
@media (min-width: 1800px) { :root { --reunus: 2cm; } }

/* RUUDUN POMPPIMINEN: SELAIN VIERITTI, EI OHJELMA.

   Asko 25.8.2026: "sivu pomppii kun painaa jonnekin nii
   pomppaa… ei pysty käyttämään kun pompii." Sama oire on TODO 30 ja 34.

   Syy mitattiin 25.8.2026 nauhoittamalla KUKA vierittää: scrollIntoView
   ja scrollTop-asetus otettiin kiinni kutsupinoineen, ja hyppyhetkellä
   lista oli TYHJÄ. Yksikään JavaScript-rivi ei vierittänyt. Sen teki
   selain.

   Kyse on scroll anchoringista. Kun piirra() kutsuu replaceChildren(),
   koko lista katoaa ja syntyy uudestaan. Chrome yrittää pitää sisällön
   paikallaan ja laskee korvauksen tyhjästä listasta — lopputulos on
   hyppy listan loppuun.

   MITATTU, kymmenen klikkausta 120 rivin näkymässä vierityksestä 400:

       overflow-anchor: auto   400 -> 3651 · 3713 · 3776 · 3838 · 3901
       overflow-anchor: none   400 ->  400 ·  400 ·  400 ·  400 ·  400

   Miksi piirra():n oma palautus ei auttanut: sen ehto on
   `alue.scrollTop !== vieritys`, ja replaceChildren():n jälkeen luku on
   YHÄ oikea — selain ei ole vielä ajanut asettelua. Ehto on epätosi,
   palautus jää tekemättä, ja anchoring siirtää vierityksen vasta sen
   jälkeen. Kaksi mekanismia korjasi samaa asiaa eri hetkellä ja ne
   taistelivat keskenään.

   ÄLÄ POISTA TÄTÄ SIISTIESSÄ. Rivi näyttää turhalta, koska anchoring on
   selaimen apu — mutta se apu on tarkoitettu sivulle joka ei palauta
   vieritystä itse. Tämä sovellus palauttaa. */
#sisalto { flex: 1; overflow: auto; background: var(--tausta); position: relative;
           overflow-anchor: none;
           padding-left: var(--reunus); padding-right: var(--reunus); }
/* Sama ikkunan omalle vieritykselle: pitkä laskenta vierittyy myös
   dokumentista, ja piirra() palauttaa senkin (window.scrollTo). */
html, body { overflow-anchor: none; }
#sisalto > .sivuotsikko { margin-left: calc(-1 * var(--reunus));
                          margin-right: calc(-1 * var(--reunus));
                          padding-left: var(--reunus); padding-right: var(--reunus); }
#sisalto > .padded { padding-left: 0; padding-right: 0; }

.osio { padding: 12px 12px 4px; font-size: 12px; font-weight: 700;
        letter-spacing: .07em; text-transform: uppercase; color: var(--teksti-himmea); }

.kohta {
  display: flex; align-items: center; gap: 6px; padding: 5px 12px;
  cursor: pointer; border-left: 3px solid transparent; font-size: 12.5px;
}
.kohta:hover { background: rgba(0,0,0,.045); }
.kohta.valittu { background: var(--tausta); border-left-color: var(--oranssi); font-weight: 600; }
.kohta .nimi { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kohta .summa { font-size: 12px; color: var(--teksti-himmea);
                font-variant-numeric: tabular-nums; }
.kohta.valittu .summa { color: var(--oranssi-tumma); }
.kohta .maara { font-size: 12px; color: var(--teksti-himmea);
                background: rgba(0,0,0,.06); border-radius: 8px; padding: 0 5px; }

/* -------------------------------------------------------------- alapalkki */

#ala {
  height: var(--ala); display: flex; align-items: center; gap: 18px;
  padding: 0 14px; background: var(--tausta-3);
  border-top: 1px solid var(--viiva-tumma); font-size: 12px;
}
#ala b { font-variant-numeric: tabular-nums; }
#ala .erotin { flex: 1; }
#ala .vihje { color: var(--teksti-himmea); font-size: 12px; }

/* ---------------------------------------------------------------- taulukko */

table.ruudukko { border-collapse: collapse; width: 100%; font-size: 13.5px; }
/* Leveydet kiinnittyvät vasta kun sarakkeita on kerran mitattu, jotta veto
   käyttäytyy ennakoitavasti kuten taulukkolaskennassa. */
table.ruudukko.mitattu { table-layout: fixed; }
table.ruudukko th {
  /* Kiinnitetyt osat ovat pinossa: sivuotsikko, suodatinpalkki, sarakeotsikot.
     Korkeudet mitataan piirron jalkeen muuttujiin --yla1 ja --yla2, jottei
     mikaan jaa toistensa alle kun nakymaa vierittaa. */
  position: sticky; top: var(--yla2, 0px); z-index: 5;
  background: var(--tausta-2);
  border-bottom: 1px solid var(--viiva-tumma); text-align: left;
  padding: 7px 8px; font-size: 12px; font-weight: 600; color: var(--teksti-himmea);
  white-space: nowrap;
}
table.ruudukko td {
  border-bottom: 1px solid var(--viiva); padding: 4px 8px; vertical-align: top;
}
/* Kun sarakeleveydet on kiinnitetty, ylipitkä sisältö katkaistaan sarakkeeseen
   eikä levitetä riviä. Nimisarake saa yhä rivittyä, siellä pituus on tietoa. */
table.ruudukko.mitattu td { overflow: hidden; }
table.ruudukko.mitattu td.oik { text-overflow: ellipsis; }
/* Solu joka rivittää sisältönsä ei katkaise sitä. Kolme pistettä on
   pahempi kuin korkeampi rivi: piste ei kerro mitä siitä jäi pois. */
table.ruudukko.mitattu td.rivittaa {
  overflow: visible; text-overflow: clip; white-space: normal;
}
/* Pystyviivat sarakkeiden väliin, jotta rivin lukemista on helpompi seurata. */
table.ruudukko th + th, table.ruudukko td + td { border-left: 1px solid var(--viiva); }
table.ruudukko tr:hover > td { background: #FAFBFC; }
table.ruudukko tr.valittu > td { background: var(--oranssi-vaalea); }
.oik { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.kesk { text-align: center; }
.himmea { color: var(--teksti-himmea); }
.pieni { font-size: 12px; }

tr.summarivi td {
  background: var(--tausta-2) !important; font-weight: 700;
  border-top: 2px solid var(--viiva-tumma);
}
tr.ryhma td {
  background: var(--tausta-3); font-weight: 600; font-size: 12.5px;
  letter-spacing: .02em; padding: 6px 8px; border-bottom: 1px solid var(--viiva-tumma);
}

/* Kustannuserä-sarake: nuoli, nimike ja ⓘ virtaavat samana tekstinä, jotta
   pitkä nimike rivittyy sanoittain eikä lohkona. */
td.nimikesolu { min-width: 260px; max-width: 460px; }
.solu.teksti.kasvava { display: inline; padding: 1px 4px; border-radius: 3px; }
.solu.teksti.kasvava:focus { box-shadow: 0 0 0 3px rgba(232,115,12,.15); }

/* muokattava solu */
.solu {
  display: block; width: 100%; border: 1px solid transparent; border-radius: 4px;
  padding: 2px 5px; background: transparent; text-align: right;
  font-variant-numeric: tabular-nums; outline: none; min-width: 62px;
}
.solu:hover { border-color: var(--viiva-tumma); background: var(--tausta); }
.solu:focus { border-color: var(--oranssi); background: var(--tausta);
              box-shadow: 0 0 0 3px rgba(232,115,12,.15); }
.solu.teksti { text-align: left; }

/* Summa ja sen alla muokattava yksikkohinta. Yksikkohinta on pienemmalla,
   jottei se kilpaile summan kanssa silmailtaessa, mutta laatikon reuna
   nakyy heti kun hiiri osuu — himmea luku ilman reunaa luetaan lukituksi. */
td.rahasolu { vertical-align: top; }
td.rahasolu .summa { font-weight: 600; }
td.rahasolu .yksikkohinta {
  font-size: 12px; color: var(--teksti-himmea); margin-top: 1px;
  border-color: var(--viiva); min-height: 17px;
}
td.rahasolu .yksikkohinta:hover, td.rahasolu .yksikkohinta:focus {
  color: var(--teksti);
}

.merkki {
  display: inline-block; min-width: 17px; text-align: center; border-radius: 3px;
  font-size: 12px; font-weight: 700; padding: 1px 3px; line-height: 14px;
}
/* Lahde on kaksiarvoinen: AI tai OMA. Askon linjaus 22.8.2026.
   Merkki on leveampi kuin vanhat yhden kirjaimen luokat, koska se on
   nyt luettava sana ja napsautettava valinta — ei lyhenne. */
.merkki.AI, .merkki.OMA {
  min-width: 44px; font-size: 12.5px; letter-spacing: .04em;
  padding: 2px 7px; cursor: pointer;
}
.merkki.AI  { background: #FBE9E7; color: var(--punainen); }
.merkki.OMA { background: #E4F5EE; color: var(--vihrea); }
.merkki.AI:hover, .merkki.OMA:hover { filter: brightness(.95); }
.merkki.R { background: #E6F0FB; color: var(--sininen); }
.merkki.T { background: #E4F5EE; color: var(--vihrea); }
.merkki.H { background: #E4F5EE; color: var(--vihrea); }
.merkki.A { background: #FDF0E4; color: var(--keltainen); }
.merkki.K { background: #FBE9E7; color: var(--punainen); }
.kasin { color: var(--keltainen); font-weight: 600; }

.t80 { display:inline-block; background: var(--tausta-3); border-radius: 3px;
       padding: 1px 5px; font-size: 12px; color: var(--teksti-himmea);
       font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- sivupohja */

.sivuotsikko {
  display: flex; align-items: baseline; gap: 12px; padding: 16px 18px 10px;
  border-bottom: 1px solid var(--viiva); position: sticky; top: 0;
  background: var(--tausta); z-index: 12;
}
.sivuotsikko h1 { font-size: 17px; margin: 0; font-weight: 650; }
.sivuotsikko .kuvaus { color: var(--teksti-himmea); font-size: 12px; margin-right: auto; }

/* Avaa/Sulje kaikki kuuluvat rivimaaran viereen, koska ne muuttavat juuri
   sita lukua. Tyontava auto-marginaali siirtyy silloin kuvaukselta talle
   parille — muuten pari lentaisi rivin oikeaan reunaan muiden mukana.
   :has rajaa muutoksen niihin otsikkoriveihin joilla pari on. */
.sivuotsikko .avausparit { display: flex; align-items: center; gap: 6px; margin-right: auto; }
.sivuotsikko:has(.avausparit) .kuvaus { margin-right: 0; }
.padded { padding: 14px 18px 40px; }

.painike {
  background: var(--tausta); border: 1px solid var(--viiva-tumma); border-radius: 6px;
  padding: 5px 11px; cursor: pointer; white-space: nowrap;
}
.painike:hover { background: var(--tausta-2); border-color: #A9B1BB; }
.painike.vahva { background: var(--oranssi); border-color: var(--oranssi);
                 color: #fff; font-weight: 600; }
.painike.vahva:hover { background: var(--oranssi-tumma); }
.painike.vaara { color: var(--punainen); }
.painike:disabled { opacity: .45; cursor: default; }
.painike.pikku { padding: 2px 7px; font-size: 12px; }

input[type=text], input[type=number], select, textarea {
  border: 1px solid var(--viiva-tumma); border-radius: 6px; padding: 6px 9px;
  background: var(--tausta); outline: none; width: 100%;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--oranssi); box-shadow: 0 0 0 3px rgba(232,115,12,.15);
}

/* ----------------------------------------------------------------- kortit */

.kortit { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px,1fr));
          gap: 12px; }
.kortti {
  border: 1px solid var(--viiva); border-radius: 9px; padding: 13px 14px;
  background: var(--tausta); cursor: pointer; transition: box-shadow .13s, border-color .13s;
}
.kortti:hover { border-color: var(--oranssi); box-shadow: var(--varjo); }
.kortti h3 { margin: 0 0 3px; font-size: 13.5px; font-weight: 620; }
.kortti .meta { color: var(--teksti-himmea); font-size: 12.5px; }
.kortti .luku { font-size: 17px; font-weight: 680; margin-top: 8px;
                font-variant-numeric: tabular-nums; }
.kortti .rivi { display: flex; gap: 8px; align-items: baseline; }
.kortti .toiminnot { margin-top: 9px; display: flex; gap: 6px; opacity: 0;
                     transition: opacity .13s; }
.kortti:hover .toiminnot { opacity: 1; }

/* YKSI KORTTI KAHDEN SIJASTA.

   Laskennassa oli `.mittari` (reunapyoristys 9, arvo 19 px, ei varjoa)
   ja hankinnassa `.kpikortti` (pyoristys 12, arvo 24 px, varjo, 4 px
   vasen reuna). Samaa tyota tekevat kortit nayttivat eri sivuilla eri
   ohjelmilta, ja se oli tuotteen selvin epayhtenaisyys.

   Korostus on nyt 3 pikselin raita YLAREUNASSA eika vasemmalla. Vasen
   reuna leikkasi otsakkeen ja arvon eri kohdasta kuin muut kortit,
   joten korostettu kortti oli myos eri levyinen sisalta. */
.mittarit {
  display: grid; gap: 10px; margin-bottom: 16px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

.mittari {
  position: relative; overflow: hidden;
  border: 1px solid var(--viiva); border-radius: 8px;
  padding: 13px 14px 12px; background: var(--tausta);
}
.mittari .otsake {
  font-size: 11px; font-weight: 650; text-transform: uppercase;
  letter-spacing: .06em; color: var(--teksti-himmea);
}
.mittari .arvo {
  font-size: 22px; font-weight: 660; line-height: 1.05; margin: 7px 0 0;
  font-variant-numeric: tabular-nums;
}
.mittari .lisa {
  font-size: 11px; margin-top: 6px; color: var(--teksti-himmea);
  font-variant-numeric: tabular-nums;
}
.mittari.paino .arvo { color: var(--oranssi); }

/* ---------------------------------------------------------------- dialogi */

#peite {
  position: fixed; inset: 0; background: rgba(20,25,35,.42); z-index: 100;
  display: none; align-items: center; justify-content: center; padding: 26px;
}
#peite.nakyy { display: flex; }
.dialogi {
  background: var(--tausta); border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,.35);
  width: min(1080px, 100%); max-height: 100%; display: flex; flex-direction: column;
  overflow: hidden;
  /* Kaikki ikkunat venyvät kulmasta ja siirtyvät otsikosta raahaamalla. */
  resize: both; min-width: 380px; min-height: 220px;
}
.dialogi > header { cursor: move; }
.dialogi > header h2 { cursor: move; user-select: none; }
.dialogi.kapea { width: min(560px, 100%); }
.dialogi.keski { width: min(780px, 100%); }
.dialogi > header {
  display: flex; align-items: center; gap: 10px; padding: 13px 16px;
  border-bottom: 1px solid var(--viiva); background: var(--tausta-2);
}
.dialogi > header h2 { margin: 0; font-size: 14.5px; font-weight: 650; flex: 1; }
.dialogi > .keho { padding: 14px 16px; overflow: auto; flex: 1; }
.dialogi > footer {
  display: flex; gap: 8px; justify-content: flex-end; align-items: center;
  padding: 11px 16px; border-top: 1px solid var(--viiva); background: var(--tausta-2);
}
.dialogi > footer .vasen { margin-right: auto; color: var(--teksti-himmea);
                           font-size: 12.5px; }
.sulje { background: none; border: none; font-size: 19px; cursor: pointer;
         color: var(--teksti-himmea); line-height: 1; padding: 0 4px; }
.sulje:hover { color: var(--teksti); }

.kentat { display: grid; grid-template-columns: 190px 1fr; gap: 9px 12px;
          align-items: center; }
.kentat label { color: var(--teksti-himmea); font-size: 12px; }
.kentat .vali { grid-column: 1 / -1; height: 1px; background: var(--viiva); margin: 5px 0; }
.kentat .selite { grid-column: 2; font-size: 12px; color: var(--teksti-himmea);
                  margin-top: -5px; }
.kentat h4 { grid-column: 1 / -1; margin: 8px 0 0; font-size: 12px;
             text-transform: uppercase; letter-spacing: .06em; color: var(--oranssi-tumma); }

/* ----------------------------------------------------------------- muuta */

.tyhja { text-align: center; color: var(--teksti-himmea); padding: 60px 20px; }
.tyhja h3 { font-weight: 600; color: var(--teksti); margin: 0 0 6px; }

/* Laskennan ensimmäinen näkymä: tyhjä tila on toiminnon alku, ei virhetila.
   Kolme vaihetta näyttävät mitä uusi laskenta tekee ennen ensimmäistä klikkiä. */
.laskenta-aloitus {
  max-width: 1060px; margin: 34px auto; padding: 34px;
  border: 1px solid var(--viiva); border-radius: 16px; background: var(--tausta);
  box-shadow: var(--varjo);
}
.aloitus-hero { max-width: 620px; }
.aloitus-merkki { margin: 0 0 12px; color: var(--oranssi-tumma); font-size: 11px; font-weight: 800; letter-spacing: .1em; }
.aloitus-hero h2 { margin: 0; font-size: clamp(27px, 3vw, 38px); line-height: 1.08; letter-spacing: -.035em; }
.aloitus-hero > p:not(.aloitus-merkki) { max-width: 520px; margin: 14px 0 22px; color: var(--teksti-himmea); line-height: 1.55; }
.aloitus-vaiheet { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 36px; }
.aloitus-vaiheet > div { min-height: 130px; padding: 16px; border-radius: 10px; background: var(--tausta-2); border: 1px solid var(--viiva); }
.aloitus-vaiheet span { display: block; margin-bottom: 22px; color: var(--oranssi-tumma); font-family: "IBM Plex Mono", monospace; font-size: 12px; font-weight: 700; }
.aloitus-vaiheet b { display: block; font-size: 15px; }
.aloitus-vaiheet p, .aloitus-jatka { margin: 6px 0 0; color: var(--teksti-himmea); font-size: 13px; line-height: 1.45; }
.aloitus-jatka { margin-top: 20px; }
.valiotsikko-maara { white-space: nowrap; }

/* Laskentaluettelo on samalla päänäkymä. Viimeisin kohde nostetaan
   jatkettavaksi työksi, jolloin yksittäinen rivi ei jää valtavaan tyhjään
   taulukkoon kellumaan. Varsinainen lista säilyy kokonaan sen alla. */
.koti-jatka {
  display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(360px, .8fr);
  gap: 28px; align-items: end; margin: 24px 0 30px; padding: 28px 30px;
  border: 1px solid var(--viiva); border-left: 4px solid var(--oranssi);
  border-radius: 13px; background: var(--tausta); box-shadow: var(--varjo);
}
.koti-eyebrow { display: block; margin-bottom: 10px; color: var(--oranssi-tumma); font-size: 11px; font-weight: 800; letter-spacing: .09em; }
.koti-jatka h2 { margin: 0; font-size: 27px; letter-spacing: -.03em; }
.koti-jatka p { margin: 7px 0 20px; color: var(--teksti-himmea); font-size: 13px; }
.koti-jatka-toiminnot { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.koti-jatka-luvut { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.koti-jatka-luvut > div { min-height: 82px; padding: 13px; border-radius: 9px; background: var(--tausta-2); border: 1px solid var(--viiva); }
.koti-jatka-luvut span { display: block; margin-bottom: 8px; color: var(--teksti-himmea); font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.koti-jatka-luvut b { display: block; font-size: 16px; letter-spacing: -.02em; }
.koti-taulukko { margin-bottom: 20px; }
@media (max-width: 820px) {
  .koti-jatka { grid-template-columns: 1fr; gap: 20px; padding: 22px; }
  .koti-jatka-luvut { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .koti-jatka-luvut > div { min-height: 66px; padding: 10px; }
  .koti-jatka-luvut b { font-size: 13px; }
}
@media (max-width: 700px) {
  .laskenta-aloitus { margin: 18px auto; padding: 22px; }
  .aloitus-vaiheet { grid-template-columns: 1fr; }
  .aloitus-vaiheet > div { min-height: 0; }
  .aloitus-vaiheet span { margin-bottom: 8px; }
}

#ilmoitus {
  position: fixed; bottom: calc(var(--ala) + 14px); left: 50%;
  transform: translateX(-50%) translateY(14px); background: #23272E; color: #fff;
  padding: 9px 16px; border-radius: 8px; box-shadow: var(--varjo); z-index: 200;
  opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s;
  max-width: 70vw;
}
#ilmoitus.nakyy { opacity: 1; transform: translateX(-50%) translateY(0); }
#ilmoitus.virhe { background: var(--punainen); }

.erittely td { background: var(--tausta-2) !important; padding: 0 !important;
               border-left: 3px solid var(--oranssi); }
.erittely table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.erittely table td { border-bottom: 1px solid var(--viiva); padding: 3px 8px;
                     background: transparent !important; }
.erittely .pak { font-weight: 600; }
.erittely .pan td:first-child { padding-left: 26px; color: var(--teksti-himmea); }

.nuoli { display: inline-block; width: 15px; color: var(--teksti-himmea);
         cursor: pointer; text-align: center; }
.nuoli:hover { color: var(--oranssi); }

.kuvausteksti { white-space: pre-wrap; font-size: 12px; line-height: 1.55;
                background: var(--tausta-2); border-radius: 8px; padding: 11px 13px;
                border: 1px solid var(--viiva); max-height: 300px; overflow: auto; }

.hakupalkki { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.hakupalkki input[type=text] { flex: 1; }
.hakupalkki select { width: auto; min-width: 130px; }

.valintaruutu { width: 15px; height: 15px; accent-color: var(--oranssi); cursor: pointer; }

/* ============================================ hankkeen puunäkymä ja lisäosat */

#yla .nimi { display: flex; flex-direction: column; justify-content: center; }
#yla .logo { cursor: pointer; }

.kohta.pois .nimi { text-decoration: line-through; opacity: .55; }

/* ------------------------------------------------------------ vaiheketju
   Missä laskenta menee. Askel on painike eikä merkki: vaihe asetetaan
   samasta paikasta jossa se luetaan, eikä sitä varten tarvitse etsiä
   asetusta hanketietojen takaa. */
/* Viiva askeleiden väliin kertoo että ne ovat ketju eikä valikko.
   Viimeisen jälkeen sitä ei ole: viiva johonkin joka ei ole askel
   lupaisi kuudennen vaiheen. */
/* Tehty on vihreä ja rastilla, nykyinen oranssi ja renkaalla. Kaksi eri
   asiaa, kaksi eri väriä: pelkkä väriero jättäisi arvattavaksi kumpi
   niistä on se jossa nyt ollaan. */

/* Kapealla ruudulla ketju rivittyy ja oikea reuna menee omalle
   rivilleen — se on tietoa eikä koriste, joten sitä ei piiloteta.

   Raja on sama 1000 px kuin sovellus.js:n KAPEA ja tämän tiedoston muut
   kapean näkymän lohkot. Kaksi eri rajaa tarkoittaisi sadan pikselin
   välin jossa ohjelma on puoliksi kummassakin tilassa. */
@media (max-width: 1000px) {
  /* Yhdysviiva pois: rivittyneessä ketjussa se osoittaa rivin lopussa
     tyhjään reunaan eli askeleeseen jota ei ole. Järjestys kertoo
     ketjun ilman viivaakin. */
}

/* hankepalkki: kertoimet + koko hanke */
.hankepalkki {
  display: flex; gap: 18px; align-items: flex-start; padding: 10px 18px 12px;
  border-bottom: 1px solid var(--viiva); background: var(--tausta-2); flex-wrap: wrap;
}
.kertoimet {
  display: grid; grid-template-columns: repeat(2, auto); gap: 2px 14px;
  font-size: 12.5px; cursor: pointer; border-radius: 7px; padding: 5px 8px;
  border: 1px solid transparent;
}
.kertoimet:hover { border-color: var(--viiva-tumma); background: var(--tausta); }
.kertoimet > div { display: contents; }
.kertoimet b { color: var(--teksti-himmea); font-weight: 500; }
.kertoimet span { font-variant-numeric: tabular-nums; }
/* TUNNUSLUKUKORTIT HANKEPALKISSA. Sama .mittari kuin Yhteenveto-näkymässä:
   kaksi eri korttityyliä samaan ohjelmaan olisi eriytynyt ensimmäisessä
   muutoksessa. Tässä ne saavat vain paikkansa palkissa ja ylälinjan,
   joka kertoo mikä luku on mitäkin lajia. */
.mittarit.tunnusluvut {
  flex: 1; min-width: 560px; margin-bottom: 0;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 8px;
}
.mittarit.tunnusluvut .mittari { padding: 7px 10px; }
.mittarit.tunnusluvut .arvo { font-size: 17px; }
.mittarit.tunnusluvut .otsake { font-size: 11px; font-weight: 650; }
.mittarit.tunnusluvut .lisa { font-size: 11px; white-space: nowrap;
                              overflow: hidden; text-overflow: ellipsis;
                              line-height: 1.35; }
/* Ylälinja kertoo lajin: oranssi on se luku joka lähtee ulos talosta,
   vihreä on se joka jää taloon. Muut ovat neutraaleja. */
.mittari { border-top: 3px solid var(--viiva); }
.mittari.paino { border-top-color: var(--oranssi); }
.mittari.hyva { border-top-color: var(--vihrea); }

/* puutaulukon rivityypit */
table.puu td { padding: 3px 7px; }
/* Paarivi on oranssi kuten ylapalkki. Askon linjaus 22.8.2026: se on
   luettelon jakaja, ja sen on erotuttava yhdella silmayksella eika
   vasta lukemalla. Sama var(--oranssi) kuin #yla:ssa. */
/* Taulukkoetuliite on pakollinen: yleinen "table.ruudukko tr:hover > td"
   on tarkempi kuin pelkka "tr.laskelma:hover > td", ja ilman etuliitetta
   se voittaisi — jolloin paarivi muuttuisi hiiren alla lahes valkoiseksi
   ja valkoinen teksti katoaisi siihen. Sama koskee valittu-tilaa. */
table.ruudukko tr.laskelma > td,
table.ruudukko tr.laskelma:hover > td,
table.ruudukko tr.laskelma.valittu > td {
  background: var(--oranssi); color: #fff; font-weight: 650;
  border-top: 1px solid var(--oranssi-tumma);
}
table.ruudukko tr.laskelma:hover > td { background: var(--oranssi-tumma); }
/* Ratas, nuoli, jarjestysnumero ja himmeat luvut oranssia vasten — myos
   hiiren alla, jossa .nuoli:hover muuten varjaisi nuolen oranssiksi
   oranssille taustalle. */
table.ruudukko tr.laskelma > td .ratas,
table.ruudukko tr.laskelma > td .nuoli,
table.ruudukko tr.laskelma > td .nuoli:hover,
table.ruudukko tr.laskelma > td .nuoli.avaus:hover,
table.ruudukko tr.laskelma > td .jno,
table.ruudukko tr.laskelma > td .himmea { color: #fff; }
table.ruudukko tr.laskelma > td .jno { border-color: rgba(255,255,255,.45); }
table.ruudukko tr.laskelma > td .ratas:hover {
  background: rgba(255,255,255,.18); color: #fff;
}
/* Nimikentta on .solu, ja yleinen ".solu:hover" maalaa sen VALKOISEKSI.
   Paarivilla teksti on valkoinen, joten hiiri tekstin paalla katosi
   valkoinen valkoiselle. Sama ansa kuin rivin taustassa mutta yhta
   tasoa syvemmalla: rivi oli jo korjattu, sen sisalla oleva kentta ei.
   Hiiren alla lapinakyva vaalennus, kirjoitettaessa valkoinen pohja ja
   tumma teksti jotta kirjoitettavan nakee. */
table.ruudukko tr.laskelma > td .solu:hover {
  background: rgba(255,255,255,.20); border-color: rgba(255,255,255,.55);
  color: #fff;
}
table.ruudukko tr.laskelma > td .solu:focus {
  background: var(--tausta); color: var(--teksti); border-color: var(--oranssi-tumma);
  box-shadow: 0 0 0 3px rgba(255,255,255,.35);
}
table.ruudukko tr.laskelma > td .nuoli.avaus:hover { color: #fff; }
table.ruudukko tr.laskelma > td .nuoli.avaus::before { border-left-color: #fff; }
table.ruudukko tr.laskelma > td .nuoli.avaus.auki::before {
  border-left-color: transparent; border-top-color: #fff;
}
table.ruudukko tr.laskelma > td .nuoli.avaus:hover::before { border-left-color: #fff; }
table.ruudukko tr.laskelma > td .nuoli.avaus.auki:hover::before {
  border-left-color: transparent; border-top-color: #fff;
}
/* Taulukkoetuliite on pakollinen, ks. paarivin sama korjaus: yleinen
   "table.ruudukko tr:hover > td" on tarkempi kuin "tr.rakenne > td". */
table.ruudukko tr.rakenne > td { background: var(--sininen-rivi); }
table.ruudukko tr.rakenne:hover > td { background: var(--sininen-rivi-2); }
/* Paketti- ja panosrivit ovat yhtä muokattavia kuin rakennerivit, joten ne
   kirjoitetaan samalla värillä. Vaalea tausta riittää erottamaan tason —
   himmeä teksti näytti siltä ettei rivejä saa muokata. */
tr.paketti > td { background: #FAFBFC; color: var(--teksti); }
tr.panos > td { background: #F4F6F8; color: var(--teksti); }
/* Omasta kirjastosta tullut rivi on haalean vihrea. Askon linjaus
   22.8.2026: vari kertoo etta kayttaja on itse hyvaksynyt luvun — ja
   samalla sen, etta rivi on ilmainen. Taulukkoetuliite pakollinen,
   muuten yleinen tr:hover voittaa ja vari katoaa hiiren alla. */
table.ruudukko tr.panos.oma > td,
table.ruudukko tr.paketti.oma > td { background: var(--vihrea-oma); }
table.ruudukko tr.panos.oma:hover > td,
table.ruudukko tr.paketti.oma:hover > td { background: var(--vihrea-oma-2); }
tr.pois > td { opacity: .5; }
tr.pois .solu.teksti { text-decoration: line-through; }
tr.lataus td { background: #FAFBFC; }

.jno {
  display: inline-block; min-width: 26px; text-align: center; border-radius: 4px;
  border: 1px solid var(--viiva-tumma); background: var(--tausta); padding: 0 3px;
  font-size: 12px; font-variant-numeric: tabular-nums; outline: none;
}
.jno:focus { border-color: var(--oranssi); box-shadow: 0 0 0 3px rgba(232,115,12,.15); }

.ratas {
  background: none; border: none; cursor: pointer; color: var(--teksti-himmea);
  font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 4px;
}
/* Rivin oma ratas on vasemmassa laidassa omassa sarakkeessaan ja
   kolminkertainen: se on rivin päävalikko, ei pikkukuvake muiden joukossa. */
.ratas.iso { font-size: 39px; padding: 0 4px; }
/* Ratas kertoo tasonsa koollaan: rakenne ja laskelma isolla, paketti ja
   panos pienemmalla — muuten panosrivit kasvaisivat kolminkertaisiksi. */
.ratas.keski { font-size: 24px; padding: 0 4px; }
.ratas.pieni { font-size: 13px; }
td.ratassolu { width: 46px; text-align: center; vertical-align: middle; }
.ratas:hover { background: rgba(0,0,0,.07); color: var(--teksti); }

.pikavalikko {
  position: fixed; z-index: 300; background: var(--tausta); border: 1px solid var(--viiva-tumma);
  border-radius: 8px; box-shadow: var(--varjo); padding: 4px; min-width: 190px;
}
.pikavalikko button {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 6px 10px; border-radius: 5px; cursor: pointer; font-size: 12.5px;
}
.pikavalikko button:hover { background: var(--tausta-2); }
.pikavalikko button.vaara { color: var(--punainen); }

.taso {
  display: inline-block; font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  border-radius: 3px; padding: 1px 4px; line-height: 14px;
}
.taso.rakenne { background: #E6F0FB; color: var(--sininen); }
.taso.paketti { background: #EEF3E9; color: #4B7A25; }
.taso.panos   { background: #F3EDF9; color: #6B41A8; }

tr.kansio > td {
  background: var(--tausta-3); font-weight: 650; font-size: 12px;
  border-top: 1px solid var(--viiva-tumma);
}
tr.hanke td:first-child { padding-left: 8px; }
tr.hanke { cursor: pointer; }

.radiot { display: flex; flex-direction: column; gap: 3px; }
label.rasti { display: flex; align-items: center; gap: 7px; cursor: pointer;
              font-size: 12.5px; color: var(--teksti); }
label.rasti input { accent-color: var(--oranssi); width: 15px; height: 15px; }

.dialogi.leve { width: min(1320px, 100%); }
.dialogi.taysi { width: min(1500px, 100%); height: 94vh; }
/* TAYSI POISTAA SISENNYKSEN, koska sen sisalto (esikatselukehys, rakenteen
   kaksi palstaa) tayttaa laatikon reunasta reunaan itse. */
.dialogi.taysi > .keho { padding: 0; display: flex; }
/* ISO ON SAMA KOKO MUTTA TAVALLISELLA SISENNYKSELLA. Asko 1.9.2026:
   "jätetään sarkaimet, ei saa lähteä tekstit heti reunasta."

   Kun kaikki laatikot suurennettiin, ne saivat myos `taysi`:n
   sisennyksen poiston — ja lomakelaatikoissa tekstit alkoivat suoraan
   reunasta. Koko ja reunaton sisalto ovat kaksi eri asiaa, ja ne
   erotettiin kahdeksi luokaksi. */
.dialogi.iso { width: min(1500px, 100%); height: 94vh; }
/* ARKKI: laatikon leveys on tulosteen leveys. Kaksi pikselia reunoille,
   jotta arkki ei jaa laatikon rajan alle. Suurennettuna (Koko nakyma)
   laatikko kasvaa, mutta arkki pysyy 210 mm:na keskella — sivu ei ala
   latoutua leveampana kuin se tulostuu. */
.dialogi.arkki { width: min(calc(210mm + 2px), 100%); height: 94vh; }
.dialogi.arkki > .keho { padding: 0; display: flex; }
/* Oletuskoko on `keski` (min(780px, 100%)) — Askon 1.9.2026 lahettaman
   kuvan mukaan mitattuna. Korkeus jaa sisallon mukaiseksi. */
.dialogi.keski > .keho { padding: 14px 16px; }
/* ESIKATSELU ON AINA A4:N LEVYINEN. Asko 3.9.2026: "tulosteen
   esikatselut aina A4 muodossa leveydeltaan."

   Kehys venyi laatikon levyiseksi (1500 px), ja sisalto latoutui sen
   mukaan: taulukot levisivat, rivit mahtuivat yhdelle riville ja
   sivunvaihdot osuivat eri kohtiin kuin PDF:ssa. Esikatselu naytti siis
   erilaiselta kuin se asiakirja jonka se lupaa nayttaa. 210 mm on
   arkin leveys, ja selain laskee millimetrit itse.

   max-width pitaa arkin ruudulla myos kapealla ikkunalla, jolloin se
   kutistuu eika mene laatikon reunan yli. */
.esikatselukehys {
  flex: 0 0 auto; width: 210mm; max-width: 100%;
  margin: 0 auto; border: 0; background: var(--tausta); display: block;
  box-shadow: 0 0 0 1px var(--viiva), 0 6px 22px rgba(20, 25, 35, .14);
}
/* Arkin ymparille poyta silloin kun laatikko on arkkia leveampi
   (Koko nakyma). Oletuskoossa arkki tayttaa laatikon eika poytaa nay. */
.keho:has(> .esikatselukehys) { background: var(--tausta-3); padding: 0; }

.osaotsikko { margin: 22px 0 8px; font-size: 12.5px; font-weight: 650; }
.solukentta {
  border: 1px solid transparent; border-radius: 4px; padding: 2px 5px; width: 100%;
  background: transparent; font-size: 12px;
  /* REUNAT JA TÄYTE LEVEYTEEN MUKAAN. Ilman tätä width:100% tarkoittaa
     sisältöä, ja kenttä on aina 12 px solua leveämpi — se työntää
     sarakkeen levenemään joka piirrossa. */
  box-sizing: border-box;
}
.solukentta:hover { border-color: var(--viiva-tumma); background: var(--tausta); }
.solukentta:focus { border-color: var(--oranssi); background: var(--tausta);
                    box-shadow: 0 0 0 3px rgba(232,115,12,.15); }

.sivuotsikko input[type=text] { max-width: 260px; }

.parikentta { display: flex; gap: 5px; }
.parikentta .solukentta:first-child { flex: 1 1 auto; }
.parikentta .solukentta:last-child { flex: 0 0 60px; }
.ikkunanapit { display: flex; gap: 7px; align-items: center; padding-left: 26px; }
.ikkunanapit .himmea { margin-right: auto; }
tr.erotusrivi > td { background: #FFF3E0 !important; font-style: italic; }
tr.panos .solukentta, tr.paketti .solukentta { padding: 1px 4px; }
tr.panos .luoti { color: var(--oranssi); margin-right: 5px; }
tr.panos td .pieni { line-height: 1.2; }
tr.paketti.pois > td, tr.panos.pois > td { opacity: .45; }
tr.paketti.pois .solukentta, tr.panos.pois .solukentta { text-decoration: line-through; }

/* panosrivin nimike: luoti ja kenttä samalle riville */
tr.panos td.panosnimi { display: table-cell; }
tr.panos td.panosnimi > div { display: flex; align-items: center; gap: 4px; }
tr.panos td.panosnimi .solukentta { flex: 1 1 auto; min-width: 120px; }
tr.panos select.solukentta { min-width: 94px; }
tr.paketti td.pakettinimi > div { display: flex; align-items: center; gap: 4px; }
tr.paketti td.pakettinimi .solukentta { flex: 1 1 auto; min-width: 140px; }

/* rakenteen ⓘ-paneeli */
tr.inforivi > td { background: #F7F8FA; border-bottom: 1px solid var(--viiva); }
.infopaneeli { padding: 6px 8px 8px 44px; }
.infosulje { margin-left: auto; }
.inforivit { display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
             margin-bottom: 5px; }
.infokentta { display: flex; align-items: center; gap: 7px; font-size: 12.5px;
              color: var(--teksti-himmea); }
/* 84 px riitti kolmelle merkille, mutta numerokentan omat nuolet vievat
   noin 20 px ja paikkamerkki katkesi puolesta valista. Askon havainto
   22.8.2026: "luvusta nakyy vaan puolet". */
.infokentta input { width: 108px; padding: 2px 6px; font-size: 12.5px; }
.infokuvaus { font-size: 12.5px; color: var(--teksti-himmea); white-space: pre-wrap;
              max-height: 84px; overflow: auto; padding: 5px 8px;
              border-left: 2px solid var(--viiva-tumma); background: var(--tausta);
              border-radius: 0 5px 5px 0; }

/* rakennevalitsin */
tr.valittu-rakenne > td { background: #E4F5EE !important; }
tr.valittu-rakenne:hover > td { background: #D6EFE3 !important; }
.sivupalkki { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.sivupalkki select { width: auto; padding: 2px 6px; }

/* rakenteiden suodatin */
.suodatinpalkki {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 18px; background: var(--tausta-2);
  border-bottom: 1px solid var(--viiva);
  position: sticky; top: var(--yla1, 0px); z-index: 10;
}
.suodatinpalkki .otsake {
  font-size: 12px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--teksti-himmea); margin-right: 2px;
}
.suodatinpalkki input, .suodatinpalkki select { width: auto; padding: 4px 8px; }

/* SUODATINRIVI SARAKEOTSIKOIDEN ALLA (työpöytä). Archdeskin malli: kenttä
   on sen sarakkeen alla jota se suodattaa, jolloin lukijan ei tarvitse
   arvata kumpi kenttä osui kumpaan sarakkeeseen.

   Kolmas kiinnityskorkeus --yla3 mitataan piirron jälkeen
   (paivitaKiinnitykset): sivuotsikko, mahdollinen palkki ja otsikkorivi
   yhteensä. Pienempi z-index kuin otsikoilla, jotta rivi liukuu niiden
   ALLE eikä päälle kun taulukkoa vieritetään. */
table.ruudukko tr.suodatinrivi th {
  top: var(--yla3, 0px); z-index: 4;
  padding: 4px 6px; font-weight: 400;
  background: var(--tausta-2);
  border-bottom: 1px solid var(--viiva-tumma);
}
table.ruudukko tr.suodatinrivi input {
  width: 100%; min-width: 0; padding: 3px 6px; font-size: 12px;
}
/* Kytkimet oikeaan reunaan: ne eivät kuulu millekään sarakkeelle, joten
   ne eivät saa näyttää siltä että kuuluisivat siihen jonka päällä ovat. */
table.ruudukko tr.suodatinrivi th.kytkimet {
  text-align: right; white-space: nowrap;
}
table.ruudukko tr.suodatinrivi th.kytkimet > * { margin-left: 4px; }
/* Otsikkorivin sarakekahva ei kuulu suodatinriviin: leveyttä vedetään
   otsikosta, ja kahva tässä olisi toinen tarttumapinta samalle asialle. */
table.ruudukko tr.suodatinrivi .sarakekahva { display: none; }

/* --------------------------------------------------- kumoa / tee uudelleen */

.kumousryhma { display: flex; gap: 2px; margin-right: 6px; flex: 0 0 auto; }

.ypainike.kumous {
  min-width: 30px; padding: 5px 0; font-size: 15px; line-height: 1;
  text-align: center;
}
.ypainike.kumous.pois { opacity: .3; cursor: default; }
.ypainike.kumous.pois:hover { background: var(--tausta); }

/* ----------------------------------------------- raporttivalintojen rivit */

/* Valinnat jotka eivät vaikuta valittuun pohjaan: näkyvissä mutta poissa
   käytöstä, jottei kukaan säädä asetusta joka ei näy tulosteessa. */
.raporttirivi { display: contents; }
.kentat .sopimaton { opacity: .35; }
.kentat .sopimaton, .kentat .sopimaton * { cursor: default; }

/* Merkitty rivi tunnistuu vilkaisulla myös silloin kun suodatin on pois. */
tr.merkitty td:first-child { border-left: 3px solid transparent; }
tr.merkitty.sininen   td:first-child { border-left-color: #1B6EF3; }
tr.merkitty.keltainen td:first-child { border-left-color: #E8A80C; }
tr.merkitty.vihrea    td:first-child { border-left-color: #12805C; }
tr.merkitty.sininen   { background: #F2F7FF; }
tr.merkitty.keltainen { background: #FFFAF0; }
tr.merkitty.vihrea    { background: #F1FAF6; }

/* Pitkä lähdetiedoston nimi ei saa venyttää riviä neljälle riville. */
td.katkaise { max-width: 150px; white-space: nowrap; overflow: hidden;
              text-overflow: ellipsis; }

/* Yksikkösolu laskelmapuussa: näyttää tekstiltä, mutta tarjoaa yksikkölistan. */
/* YKSIKKÖKENTTÄ MITOITETAAN PISIMMÄN YKSIKÖN MUKAAN, ei lyhimmän.
   Asko 24.8.2026: "nuo m2 laatikot ei mahdu teksti sinne."

   Mitattu selaimesta: clientWidth 52, scrollWidth 55 — sisältö oli
   kolme pikseliä laatikkoa leveämpi jo sanalla "m2", ja aineistossa on
   "m3ktr" ja "kpl/m2". Leveys ei ollut liian pieni vahingossa vaan
   siksi että se oli mitoitettu silmällä eikä sisällöllä. */
input.yksikkosolu { width: 72px; }
/* PUUSSA KENTTÄ ON SARAKKEEN LEVYINEN — MUTTEI PIENEMPI KUIN SISÄLTÖ.
   Asko 24.8.2026: "menee hassuks kun leventtää välejä tai kaventaa."

   Kentillä oli kiinteä pikselileveys, eivätkä ne seuranneet sarakkeen
   vetoa: leveä sarake jätti kentän pieneksi ja kapea leikkasi sen.

   Pelkkä width:100% ei riitä, ja se mitattiin: ilman alarajaa sarake
   kutistuu OTSIKON levyiseksi, koska kenttä ei enää työnnä sitä
   leveämmäksi. Yks.-sarake meni 48 pikseliin ja "m2" tarvitsee 51 —
   104 kenttää leikkasi. Kiinteät leveydet tekivät siis kaksi työtä:
   ne mitoittivat sarakkeen JA kentän, ja vain jälkimmäinen oli väärin.

   min-width mitoittaa sarakkeen, width:100% seuraa vetoa.

   ALARAJA ON PIENEMPI KUIN KAPEIN SARAKE. Ensimmäinen versio käytti
   68 pikseliä, ja selain_sarakkeet.py meni punaiseksi: Tunnit-sarake on
   66 px, joten alaraja työnsi kentän solun yli ja sisältö katkesi
   kolmeen pisteeseen. Sarake mitataan kerran eikä se veny alarajan
   mukaan sen jälkeen — siksi alarajan on mahduttava kapeimpaan
   sarakkeeseen, ei toisin päin. */
table.ruudukko.puu input.solukentta { width: 100%; min-width: 56px; }
table.ruudukko.puu input.yksikkosolu,
table.ruudukko.puu td:nth-child(6) input.solukentta { min-width: 58px; }
/* Nimikesarake saa olla kapeampikin: sen sisältö on vapaata tekstiä
   eikä siitä leikkaudu lukua. */
table.ruudukko.puu td.nimikesolu input.solukentta,
table.ruudukko.puu td.panosnimi input.solukentta,
table.ruudukko.puu td.pakettinimi input.solukentta { min-width: 0; }

/* KENTTÄSOLUN TÄYTE ON PIENEMPI KUIN TEKSTISOLUN. Solun täyte on 8 px
   kummallakin puolella, ja kapeimmassa sarakkeessa (Tunnit, 66 px) se
   vie kentältä 16 pikseliä — sisältö ei mahtunut ja katkesi kolmeen
   pisteeseen. Mitattu selain_sarakkeet.py:llä: solu 66, sisällön
   vähimmäisleveys 81.

   Kentällä on oma reunuksensa, joka erottaa sen solusta ilman täytettä.
   Tekstisoluihin tämä ei koske: niissä täyte on se mikä pitää luvut
   erillään toisistaan. */
table.ruudukko.puu td:has(> input.solukentta) { padding-left: 3px; padding-right: 3px; }

/* LUKUKENTTÄ EI SAA LEIKATA LUKUA. Mitattu selaimesta: paketin
   Tunnit-solu on 51 px ja "262,50" tarvitsee 53. Sarake on kapea eikä
   sitä voi leventää muiden kustannuksella, joten tila otetaan täytteestä
   — sitä oli 5 px kummallakin puolella eikä numero tarvitse niin paljon.

   Leikkautunut luku on väärä luku, ja se on pahempi kuin ahdas solu:
   "262,5" ja "262,50" ovat sama työmäärä, mutta "26" ei ole. */
table.ruudukko.puu input.solukentta[inputmode="decimal"],
table.ruudukko.puu td.oik input.solukentta { padding-left: 2px; padding-right: 2px; }

/* Rakenneikkuna: kentät vasemmalle, paketit ja panokset oikealle. */
.rakenneikkuna { display: grid; grid-template-columns: minmax(320px, 400px) 1fr;
                 gap: 20px; padding: 14px 16px; align-items: start; width: 100%; }
.rakenneikkuna > .kentat { align-content: start; }
.osaosio { min-width: 0; }
.rakenneikkuna > .osapuoli { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.nimirivi { display: grid; grid-template-columns: 130px 1fr; gap: 10px; align-items: center; }
.nimirivi label { color: var(--teksti-himmea); }
.nimirivi input { font-size: 15px; font-weight: 600; }
.osataulukko { margin-top: 8px; }
.nuoli.lisaa { opacity: .55; cursor: pointer; }

/* --------------------------------------------------- sarakkeiden veto */

/* Otsikkosolu on jo sticky, mikä riittää kahvan ankkuriksi. Erillinen
   position: relative kumosi kiinnityksen ja siirsi otsikon top-arvon verran
   alas rivien päälle — sticky ja relative eivät voi olla yhtä aikaa. */
.sarakekahva {
  position: absolute; top: 0; right: -3px; width: 7px; height: 100%;
  cursor: col-resize; z-index: 6;
}
table.ruudukko th:last-child .sarakekahva { right: 0; }
.sarakekahva:hover::after, .sarakekahva.vedossa::after {
  content: ''; position: absolute; left: 2px; top: 0; width: 2px; height: 100%;
  background: var(--oranssi);
}
body.saraketta-vedetaan { cursor: col-resize; }
body.saraketta-vedetaan * { cursor: col-resize !important; }

/* ------------------------------------------------------ avausnuolet

   Nuoli on tärkein ohjain koko näkymässä: siitä rivi aukeaa muokattavaksi.
   Siksi se on selvästi värillinen eikä harmaa vihje muiden joukossa. */
/* Kolmio piirretaan reunoilla eika kirjoiteta merkkina. Askon linjaus
   22.8.2026: nuolten on oltava selvat, etenkin alaspain. Merkki U+2304
   (chevron down) piirtyi ohuena viivana ja katosi rivin sekaan — ja
   tassa talossa on jo kerran kaynyt niin, etta jarjestelmasta puuttuva
   merkki jai ruudulle tyhjaksi laatikoksi. Reunakolmio nakyy aina
   samanlaisena eika ole minkaan fontin varassa. */
.nuoli.avaus {
  cursor: pointer; width: 18px; height: 18px; position: relative;
  vertical-align: -4px;
}
.nuoli.avaus::before {
  content: ''; position: absolute; top: 50%; left: 50%;
  border: 6px solid transparent;
  border-left-color: var(--oranssi-tumma);
  margin: -6px 0 0 -3px;                 /* oikealle osoittava */
  transition: transform .12s ease;
}
.nuoli.avaus.auki::before {
  border-left-color: transparent;
  border-top-color: var(--oranssi-tumma);
  margin: -3px 0 0 -6px;                 /* alaspain osoittava */
}
.nuoli.avaus:hover::before { border-left-color: var(--oranssi); }
.nuoli.avaus.auki:hover::before {
  border-left-color: transparent; border-top-color: var(--oranssi);
}
/* Paaryhmarivilla nuoli on isompi ja valkoinen oranssia vasten. */
table.ruudukko tr.laskelma .nuoli.avaus { width: 20px; height: 20px; }
table.ruudukko tr.laskelma .nuoli.avaus::before { border-width: 7px; }

/* Sama kirjasinkoko kaikkialla: erittelyrivit eivät enää kutistu. */
table.ruudukko td, table.ruudukko th,
table.ruudukko .solukentta, table.ruudukko select,
table.ruudukko .pieni, table.ruudukko .t80,
table.ruudukko .merkki { font-size: 13.5px; }

/* --------------------------------------------------------------- arviot

   Arvattu panosjako kirjoitetaan punaisella, ja rakennerivi punertuu jos
   yksikin sen panos on arvattu. Merkintä katoaa kun panosta muokataan tai
   arviot hyväksytään — silloin näkee mikä on tarkistettu ja mikä ei. */
tr.panos.arvio > td, tr.paketti.arvio > td { color: var(--punainen); }
tr.panos.arvio .solukentta, tr.paketti.arvio .solukentta { color: var(--punainen); }
tr.rakenne.arvio > td { color: var(--punainen); }
tr.rakenne.arvio .nimi::after {
  content: ' ●'; color: var(--punainen); font-size: 11px; vertical-align: 2px;
}

/* Nimi yhdelle riville: sarakkeen leveys ratkaisee kuinka paljon näkyy.
   Vetämällä leveämmäksi saa loput esiin ilman että rivi kasvaa. */
td.nimikesolu { white-space: nowrap; }
td.nimikesolu .nimi {
  display: inline-block; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; vertical-align: bottom; cursor: text;
}

/* --------------------------------------------------- pyöreä kuvakenappi

   Kuvake ilman tekstiä. Yläpalkin nappirivi on pitkä, ja tulostin kertoo
   raportista yhdellä silmäyksellä lyhyemmässä tilassa kuin sana. */
.ypainike.pyorea {
  width: 34px; height: 34px; min-width: 34px; padding: 0;
  border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center;
}
.ypainike.pyorea svg { display: block; }

/* ------------------------------------------------- lukukenttien nuolet

   Lomakkeiden lukukentistä enemmän/vähemmän-nuolet pois: ne vievät tilaa,
   osuvat vahingossa ja siirtävät lukua yhdellä silloin kun haluttiin
   kirjoittaa kokonaan uusi. Sääntö on "lomakkeissa ei, taulukoissa kyllä":
   panosrivin menekkiä hienosäädetään askeleella 0,1 ja siellä nuoli auttaa,
   joten taulukon soluja tämä ei koske. */
.dialogi .kentat input[type=number],
.dialogi .nimirivi input[type=number] {
  appearance: textfield; -moz-appearance: textfield;
}
.dialogi .kentat input[type=number]::-webkit-outer-spin-button,
.dialogi .kentat input[type=number]::-webkit-inner-spin-button,
.dialogi .nimirivi input[type=number]::-webkit-outer-spin-button,
.dialogi .nimirivi input[type=number]::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}

/* ============================================================ kapea näkymä

   TYYLI.md: puhelinnäkymä ei ole pienennetty työpöytänäkymä. Viisitoista
   saraketta ei mahdu 390 pikseliin millään suunnittelulla, joten rivistä
   näytetään nimi, määrä ja summa — loput avautuvat riviä napauttamalla.

   Raja on 1 000 px, sama luku kuin sovellus.js:n KAPEA-kysely. Se ei ole
   puhelimen ja tabletin välissä vaan siinä mihin taulukko mahtuu: 820
   pikselin tabletilla sivupalkki vei 236 px ja rahasarakkeet jäivät
   kokonaan ruudun ulkopuolelle — rivistä näkyi nimi eikä yhtään lukua. */

@media (max-width: 1000px) {

  /* Kosketuskoko 44 px. Muuttujia --yla ja --ala ei muuteta täällä: ne ovat
     sopimuksen mukaan pääikkunan, joten korkeudet annetaan suoraan. */
  #yla    { height: 52px; gap: 6px; padding: 0 8px; }
  #ala    { height: 56px; gap: 8px; padding: 0 10px; font-size: 11px;
            flex-wrap: wrap; align-content: center; line-height: 1.3; }
  #runko  { height: calc(100% - 52px - 56px); }
  #ala .erotin { display: none; }
  #ala-vasen, #ala-oikea { flex: 1 1 100%; }
  #ala-oikea { text-align: right; margin-top: -2px; }

  .ypainike { min-width: 44px; min-height: 44px; padding: 6px 10px; font-size: 15px; }
  #yla .nimi { font-size: 14px; max-width: none; }
  #yla .nimi small { font-size: 11px; }
  #yla .logo { display: none; }

  .kohta { min-height: 44px; font-size: 14px; padding: 6px 14px; }
  .osio  { font-size: 11.5px; padding: 14px 14px 4px; }

  /* ------------------------------------------------------- rivi korttina */

  table.ruudukko.puu, table.ruudukko.puu tbody { display: block; width: 100%; }
  table.ruudukko.puu thead { display: none; }
  table.ruudukko.puu colgroup { display: none; }

  table.ruudukko.puu tr {
    display: grid; grid-template-columns: 34px minmax(0, 1fr) auto;
    align-items: center; column-gap: 8px; row-gap: 1px;
    padding: 5px 10px 6px; min-height: 44px;
    border-bottom: 1px solid var(--viiva);
  }
  table.ruudukko.puu td { display: none; border: 0; padding: 1px 0; }
  table.ruudukko.puu td + td { border-left: 0; }

  /* Näkyvät solut: ⚙, nimi, summa, ja alarivillä määrä ja yksikkö. */
  table.ruudukko.puu td:nth-child(1)  { display: block; grid-area: 1 / 1 / 3 / 2; }
  table.ruudukko.puu td:nth-child(4)  { display: block; grid-area: 1 / 2 / 2 / 3;
                                        min-width: 0; white-space: normal;
                                        overflow-wrap: anywhere; }
  table.ruudukko.puu td:nth-child(11) { display: block; grid-area: 1 / 3 / 2 / 4;
                                        text-align: right; font-weight: 600;
                                        white-space: nowrap; }
  table.ruudukko.puu td:nth-child(5)  { display: block; grid-area: 2 / 2 / 3 / 3;
                                        justify-self: start; }
  table.ruudukko.puu td:nth-child(6)  { display: block; grid-area: 2 / 3 / 3 / 4;
                                        justify-self: end; }
  table.ruudukko.puu td:nth-child(5),
  table.ruudukko.puu td:nth-child(6)  { color: var(--teksti-himmea); font-size: 12.5px; }
  /* Nimi saa rivittyä kortissa: pituus on tietoa, ja työpöydän yhden rivin
     katkaisu jättäisi puhelimessa puolet nimestä näkymättä. Avausnuoli
     pysyy nimen vieressä eikä omalla rivillään. */
  table.ruudukko.puu td:nth-child(4) { display: flex; flex-wrap: nowrap;
                                       align-items: baseline; gap: 0 5px; }
  table.ruudukko.puu td:nth-child(4) > .nimi,
  table.ruudukko.puu td:nth-child(4) > .solu { min-width: 0; flex: 1 1 auto; }
  table.ruudukko.puu td.nimikesolu { white-space: normal; }
  table.ruudukko.puu td.nimikesolu .nimi { white-space: normal; max-width: none; }
  table.ruudukko.puu .nuoli.avaus { min-width: 14px; }
  /* Yksikkökenttä kapeaksi: se on valintalista, ei tekstikenttä. */
  table.ruudukko.puu input.yksikkosolu { width: 68px; }

  /* Tyhjä solu ei saa varata riviä: laskelmarivillä ei ole määrää. */
  table.ruudukko.puu td:empty { display: none; }

  /* Avattu rivi: loput sarakkeet nimikkeineen omille riveilleen. Nimike
     tulee muuttujasta, jotta sama sääntö kelpaa kaikille. */
  table.ruudukko.puu tr.avattu > td {
    display: flex; grid-column: 1 / -1; gap: 10px;
    align-items: baseline; min-height: 32px;
    border-top: 1px dashed var(--viiva);
  }
  table.ruudukko.puu tr.avattu > td:empty { display: none; }
  table.ruudukko.puu tr.avattu > td:nth-child(1),
  table.ruudukko.puu tr.avattu > td:nth-child(4),
  table.ruudukko.puu tr.avattu > td:nth-child(5),
  table.ruudukko.puu tr.avattu > td:nth-child(6),
  table.ruudukko.puu tr.avattu > td:nth-child(11) {
    display: block; border-top: 0;
  }
  table.ruudukko.puu tr.avattu > td:nth-child(1) { grid-area: 1 / 1 / 3 / 2; }
  table.ruudukko.puu tr.avattu > td:nth-child(4) { grid-area: 1 / 2 / 2 / 3; }
  table.ruudukko.puu tr.avattu > td:nth-child(11) { grid-area: 1 / 3 / 2 / 4; }
  table.ruudukko.puu tr.avattu > td:nth-child(5) { grid-area: 2 / 2 / 3 / 3; }
  table.ruudukko.puu tr.avattu > td:nth-child(6) { grid-area: 2 / 3 / 3 / 4; }

  table.ruudukko.puu tr.avattu > td::before {
    content: var(--otsikko, ''); color: var(--teksti-himmea);
    flex: 0 0 44%; font-size: 12.5px;
  }
  table.ruudukko.puu tr.avattu > td:nth-child(1)::before,
  table.ruudukko.puu tr.avattu > td:nth-child(4)::before,
  table.ruudukko.puu tr.avattu > td:nth-child(5)::before,
  table.ruudukko.puu tr.avattu > td:nth-child(6)::before,
  table.ruudukko.puu tr.avattu > td:nth-child(11)::before { content: none; }

  table.ruudukko.puu td:nth-child(2)  { --otsikko: 'Jno'; }
  table.ruudukko.puu td:nth-child(3)  { --otsikko: 'Littera'; }
  table.ruudukko.puu td:nth-child(7)  { --otsikko: 'Materiaalit'; }
  table.ruudukko.puu td:nth-child(8)  { --otsikko: 'Hankinnat'; }
  table.ruudukko.puu td:nth-child(9)  { --otsikko: 'Työt'; }
  table.ruudukko.puu td:nth-child(10) { --otsikko: 'Tunnit'; }
  table.ruudukko.puu td:nth-child(12) { --otsikko: '€/yks.'; }
  table.ruudukko.puu td:nth-child(13) { --otsikko: 'Lähde'; }
  table.ruudukko.puu td:nth-child(14) { --otsikko: 'Älä laske'; }
  table.ruudukko.puu td:nth-child(15) { --otsikko: 'Valitse'; }

  /* Summarivi on eri muotoinen: yhdistettyjä soluja, ei ruudukkoa. */
  table.ruudukko.puu tr.summarivi {
    display: flex; justify-content: space-between; gap: 10px; font-weight: 600;
  }
  table.ruudukko.puu tr.summarivi td { display: none; }
  table.ruudukko.puu tr.summarivi td:nth-child(1),
  table.ruudukko.puu tr.summarivi td:nth-child(6) { display: block; }

  /* --------------------------------------------------- muut näkymät */

  .sivuotsikko { flex-wrap: wrap; row-gap: 6px; padding: 8px 10px; }
  .sivuotsikko h1 { font-size: 17px; }
  .painike { min-height: 44px; }
  .painike.pikku { min-height: 36px; }
  .ratas { min-width: 34px; min-height: 34px; }
  .hakupalkki { flex-wrap: wrap; }
  .hakupalkki input, .hakupalkki select { min-height: 44px; }
  /* Suodatinpalkki mahtuu kahdelle riville kun kentät jakavat leveyden.
     Neljä riviä nappeja työnsi ensimmäisen laskelmarivin pois ruudulta. */
  .suodatinpalkki { padding: 6px 10px; gap: 6px; }
  .suodatinpalkki > input { flex: 1 1 100%; min-height: 44px; }
  .suodatinpalkki > select, .suodatinpalkki > .painike { flex: 1 1 44%; min-height: 44px; }
  .suodatinpalkki > label { font-size: 12px; }
  .suodatinpalkki .otsake { display: none; }
  .suodatinpalkki > .suodatinlisa { flex: 0 0 auto; }
  /* Suppea palkki: hakukenttä ja avausnappi, ei muuta. */
  .suodatinpalkki.suppea > * { display: none; }
  .suodatinpalkki.suppea > input:first-of-type,
  .suodatinpalkki.suppea > .suodatinlisa { display: block; }
  .suodatinpalkki.suppea > input:first-of-type { flex: 1 1 60%; max-width: none; }
  .padded { padding: 10px; }

  /* Laskennan kertoimet kahdelle palstalle ja tunnusluvut vieritettäväksi:
     kumpikaan ei kavennu 390 pikseliin, ja pakottaminen tekisi niistä
     lukukelvottomia. */
  .hankepalkki { display: block; padding: 8px 10px; }
  /* Kertoimien lapset ovat display:contents, joten yksi kenttä vie kaksi
     saraketta. Neljä saraketta = kaksi kenttää rinnakkain. */
  .hankepalkki .kertoimet { grid-template-columns: auto 1fr auto 1fr; gap: 3px 10px;
                            padding: 4px 0; }
  /* KORTTIEN MIN-WIDTH POIS PUHELIMESSA. Työpöydällä 560 px pitää viisi
     korttia yhdellä rivillä kertoimien vieressä; 390 pikselin ruudulla
     sama luku työntäisi koko sivun vaakavieritykseen — ja se on juuri se
     mitä `#sisalto { overflow-x: hidden }` alla estää, jolloin viides
     kortti jäisi ruudun ulkopuolelle ilman että sinne pääsee. */
  .mittarit.tunnusluvut {
    min-width: 0; margin-top: 8px;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  }
  .mittarit.tunnusluvut .lisa { white-space: normal; }

  /* Leveät taulukot muissa näkymissä vierivät itse, ei koko sivu. */
  #sisalto { overflow-x: hidden; }
  .padded > table.ruudukko { display: block; overflow-x: auto; }

  .dialogi { width: 96vw !important; max-width: 96vw; left: 2vw !important;
             top: 60px !important; max-height: calc(100% - 120px); }
}

/* ------------------------------------------------------- avattava teksti

   TYYLI.md: sininen teksti tarkoittaa tekstiä, jonka kaksoisnapsautus avaa
   oman ikkunansa. Katkoviiva alla kertoo että kohde on toiminto eikä
   pelkkä väritetty sana. */
.avattava {
  color: var(--sininen); cursor: pointer;
  text-decoration: underline dotted; text-underline-offset: 3px;
}
.avattava:hover { text-decoration: underline solid; }

/* Iso kirjoituskenttä irrotetussa ikkunassa: ikkuna venyy, kenttä venyy
   mukana. Kolme riviä riitti kahdelle lauseelle, ei muistiinpanoille. */
.isokentta-kehys { display: flex; padding: 14px 16px; min-height: 320px; }
textarea.isokentta {
  flex: 1; min-height: 300px; resize: none; padding: 10px 12px;
  border: 1px solid var(--viiva); border-radius: 7px; background: var(--tausta);
  font: 13.5px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
}
textarea.isokentta:focus { border-color: var(--oranssi); outline: none; }

/* Säädettävä prosentti tarjousketjussa: pieni kenttä tekstin perässä. */
input.prosenttisolu {
  width: 62px; text-align: right; margin: 0 2px 0 6px;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------- välilehdet

   Kirjaston kolme lajia. Valittu välilehti erottuu oranssilla alaviivalla
   eikä värillisellä taustalla: taulukko alkaa heti alta, ja täytetty
   painike näyttäisi siltä että se on osa taulukkoa. */
.valilehdet {
  display: flex; gap: 2px; padding: 0 16px;
  border-bottom: 1px solid var(--viiva-tumma); background: var(--tausta-2);
}
.valilehti {
  background: none; border: 0; border-bottom: 2px solid transparent;
  padding: 9px 14px; cursor: pointer; font-size: 13.5px;
  color: var(--teksti-himmea); margin-bottom: -1px;
}
.valilehti:hover { color: var(--teksti); background: rgba(0,0,0,.035); }
.valilehti.valittu {
  color: var(--oranssi-tumma); font-weight: 600;
  border-bottom-color: var(--oranssi);
}
input.kirjastohaku { min-width: 260px; flex: 1 1 260px; }

/* Estetty valikkokohta: harmaa eikä hiljainen. Kohta joka näyttää
   aktiiviselta mutta ei tee mitään saa käyttäjän päättelemään ohjelman
   olevan rikki — juuri niin kävi rivin siirrolle ryhmänsä reunassa. */
.pikavalikko button:disabled {
  color: var(--teksti-himmea); cursor: default; opacity: .55;
}
.pikavalikko button:disabled:hover { background: none; }

/* --------------------------------------------------- ammattien hinnasto

   Yksi tuntihinta laskentaa kohti korvautui kolmentoista ammatin
   hinnastolla. Rivit ovat kapeita ja tasattuja, jotta kolmetoista lukua
   mahtuu ikkunaan ilman vieritystä. */
/* KAHTEEN SARAKKEESEEN. Asko 26.8.2026: "tuntihinnat kahteen riviin."
   Kolmentoista ammatin hinnasto yhdessä sarakkeessa vei Tiedot-ikkunan
   niin korkeaksi, että Muistiinpanot jäi näkymän alapuolelle — ja se on
   ikkunan viimeinen rivi juuri siksi että se luetaan. Puhelimessa
   sarakkeita on yksi: kaksi 92 pikselin lukukenttää rinnakkain ei mahdu
   390 pikseliin. */
.hinnasto { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 18px; }
@media (max-width: 560px) { .hinnasto { grid-template-columns: 1fr; } }
.hinnastorivi {
  display: grid; grid-template-columns: 1fr 92px 30px;
  align-items: center; gap: 8px;
}
.hinnastorivi .nimi { font-size: 13px; }
.hinnastorivi input { text-align: right; font-variant-numeric: tabular-nums; }
.hinnastorivi .yks { font-size: 12px; }
/* Oma hinta erottuu oletuksesta: muuten ei näe mitkä on itse annettu. */
.hinnastorivi.oma .nimi { font-weight: 600; }
/* Oletushinta on hinnaston tärkein rivi: se koskee kaikkea työtä jolla
   ei ole ammattia, eli käytännössä koko laskentaa. */
.hinnastorivi.oletushinta { padding-bottom: 4px; margin-bottom: 2px;
  border-bottom: 1px solid var(--viiva); }
.hinnastorivi.oletushinta .nimi { font-weight: 650; }
.hinnastorivi.oma input { border-color: var(--oranssi); }
/* Pitkän kentän otsikko rivin alkuun: kolmentoista rivin hinnasto vei
   otsikon ruudukossa alas listan loppuun. */
.kentat > label { align-self: start; padding-top: 7px; }

/* ---------------------------------------------------- tuodun alkuperä

   Hyväksyntämerkinnät säilyvät tuonnissa, mutta ne ovat tekijän eivätkä
   vastaanottajan. Ilman tätä palkkia vastaanottaja näkisi mustia lukuja
   ja olettaisi tarkistaneensa ne itse. Sininen on tiedotus, ei virhe:
   laskennassa ei ole mitään vikaa, se on vain jonkun toisen tekemä. */
.alkupera {
  display: flex; align-items: flex-start; gap: 14px;
  margin: 10px 16px 0; padding: 10px 14px;
  background: #F2F7FF; border-left: 3px solid var(--sininen);
  border-radius: 0 7px 7px 0; font-size: 13px;
}
.alkupera > div:first-child { flex: 1; }
.alkupera .pieni { color: var(--teksti-himmea); font-size: 12.5px; margin-top: 3px; }

/* Pyöreä kuvakepainike myös vaalealla pohjalla, ei vain yläpalkissa. */
.painike.pyorea {
  width: 34px; height: 34px; min-width: 34px; padding: 0;
  border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center;
}
.painike.pyorea svg { display: block; }

/* ------------------------------------------------------- hyväksyntä

   Punainen luku on arvio, jonka joku on vielä katsottava. Kytkin on
   rivillä eikä valikossa: sadan rivin läpikäynti yksi ratasvalikko
   kerrallaan ei tapahdu, ja silloin arviot jäisivät hyväksymättä. */
/* Merkki eikä teksti: nimisarake on kiinteänlevyinen, ja tekstipilleri
   leikkautui sarakkeen reunaan puolesta sanasta lukukelvottomaksi. */
button.hyvaksy {
  border: 1px solid var(--punainen); background: #FDF1F0; color: var(--punainen);
  border-radius: 999px; width: 22px; height: 18px; padding: 0;
  font-size: 11.5px; font-weight: 700; cursor: pointer; margin-left: 6px;
  line-height: 1; vertical-align: 1px;
}
button.hyvaksy:hover { background: var(--punainen); color: #fff; }
/* Rivin päällä ollessa kytkin erottuu; muuten se ei vie huomiota nimeltä. */
tr.rakenne button.hyvaksy { opacity: .75; }
tr.rakenne:hover button.hyvaksy { opacity: 1; }

/* ---------------------------------------------------------- menekkihaku */
button.taytto {
  border: 1px solid var(--viiva-tumma); background: var(--tausta-2);
  color: var(--teksti-himmea); border-radius: 999px;
  width: 22px; height: 18px; padding: 0; font-size: 12px; cursor: pointer;
  margin-left: 6px; line-height: 1; opacity: .7; vertical-align: 1px;
}
tr.rakenne:hover button.taytto { opacity: 1; }
button.taytto:hover { border-color: var(--oranssi); color: var(--oranssi-tumma); }

/* Rajaus on yhtä tärkeä kuin luku: sama työ on kaksi eri lukua sen mukaan
   mitä siihen sisältyy. Siksi se on oma lohkonsa taulukon yläpuolella
   eikä alaviite sen alla. */
.menekkitulos .rajaus {
  background: var(--oranssi-vaalea); border-left: 3px solid var(--oranssi);
  padding: 8px 12px; border-radius: 0 6px 6px 0; margin-bottom: 12px;
  font-size: 13px;
}
.menekkitulos .rajaus b { display: block; font-size: 11.5px; letter-spacing: .06em;
                          text-transform: uppercase; color: var(--oranssi-tumma); }
.menekkitulos table { margin-top: 4px; }

/* Kirjautunut käyttäjä yläpalkissa. Kaksi tiliä näyttävät muuten
   täsmälleen samalta, koska koetili on kopio omasta kannasta — ja
   väärälle tilille tehty muutos näyttää siltä että työ katosi. */
.ypainike.kayttaja {
  background: var(--tausta-3); border-color: var(--viiva-tumma);
  font-size: 12px; max-width: 160px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.ypainike.kayttaja:hover { background: var(--viiva); }

/* ------------------------------------------------------- hinnan haku */
.hakurivi { display: flex; gap: 8px; padding: 14px 16px 0; }
.hakurivi input { flex: 1; }
textarea.liitoskentta { min-height: 130px; margin: 10px 16px; width: calc(100% - 32px); }
.hintatulos { padding: 0 16px 4px; }
.hintatulos .loydetty {
  background: var(--tausta-2); border: 1px solid var(--viiva);
  border-radius: 7px; padding: 9px 12px; margin-bottom: 10px; font-size: 13px;
}
.hintatulos .loydetty .pieni { color: var(--teksti-himmea); font-size: 12px; }
.hintatulos .katkaise { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Löytymättä jääminen on oikea tulos eikä virhe: sininen tiedotus, ei
   punainen varoitus. Punainen tarkoittaa tässä ohjelmassa arviota. */
.hintatulos .eiloytynyt {
  background: #F2F7FF; border-left: 3px solid var(--sininen);
  border-radius: 0 7px 7px 0; padding: 10px 14px; font-size: 13px;
}
.hintatulos .eiloytynyt .pieni { color: var(--teksti-himmea); font-size: 12.5px;
                                 margin-top: 3px; }

/* Hinnan lähde. Liitetty voittaa haetun, ja käyttäjän on nähtävä kumpaa
   hän katsoo — kaksi hintaa näyttää muuten ruudulla samalta. Ei punaista:
   haettu hinta ei ole arvio vaan lähteellinen luku, joka on vain
   heikompi kuin itse liitetty. */
.lahdemerkki {
  display: inline-block; font-size: 11px; font-weight: 600;
  letter-spacing: .04em; padding: 1px 7px; border-radius: 999px;
  background: var(--tausta-3); color: var(--teksti-himmea);
}
.lahdemerkki.vihrea { background: #E7F5EF; color: var(--vihrea); }
.lahdemerkki.sininen { background: #EAF1FE; color: var(--sininen); }

/* ----------------------------------------------------- puuttuva hinta

   Rivi jolta puuttuu yhden materiaalin hinta ei näytä puutteelliselta —
   se näyttää halvalta. Summa on olemassa ja pienempi kuin pitäisi, eikä
   mikään kerro miksi. Merkki on summan vieressä, koska juuri siinä
   kohdassa väärä luku luetaan.

   Keltainen eikä punainen: punainen tarkoittaa tässä ohjelmassa arviota
   jota ei ole hyväksytty, ja rivi voi olla molempia yhtä aikaa. Jos
   epätäydellinen luku olisi myös punainen, kumpikaan merkitys ei kestäisi.
   Muuttuja --keltainen on jo :root-lohkossa; TYYLI.md:n väriluetteloon
   pitää lisätä rivi "keltainen = epätäydellinen luku". */
.puuttuu {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  border: 1px solid var(--keltainen); border-radius: 999px;
  background: #FFFAF0; color: var(--keltainen);
  font-size: 10.5px; font-weight: 600; white-space: nowrap; cursor: help;
}
.puuttuvat {
  border-left: 3px solid var(--keltainen); background: #FFFAF0;
  padding: 8px 12px; border-radius: 0 6px 6px 0; font-size: 13px;
}
.puuttuvat .pieni { font-size: 12.5px; margin-top: 2px; }
td.rahasolu.kontti { background: #F1FAF6; }
td.rahasolu.kontti .summa { font-weight: 700; }
/* Johdettu yksikköhinta: sitä ei syötetä, joten se ei näytä kentältä. */
.johdettu { font-size: 10.5px; color: var(--teksti-himmea);
            font-variant-numeric: tabular-nums; margin-top: 1px; }

/* Kolme kokoa. Puhelimessa alirivi on kortti kolmella rivillä:

       AU                                    1 500,00 €
       Räystään purku · Kattoliike Oy
       [1 500,00]                            [könttä]

   Ylin rivi kertoo mikä ja paljonko, ja se riittää selaamiseen. Hinta ja
   muoto ovat alimmalla rivillä yhdessä, koska ne ovat sama tieto: 1 500 ei
   tarkoita mitään ennen kuin tietää onko se könttä vai yksikköhinta.

   Yksikkö jää pois: se lukee päärivillä yllä, ja kolmanteen sarakkeeseen
   ahdettuna se leikkautui puoliksi. Leikkautunut yksikkö on pahempi kuin
   puuttuva — 'j' luetaan miksi tahansa mitä lukija odottaa.

   Ruudukko annetaan kokonaan, ei yhtä solua kerrallaan. Yleissääntö vie
   ratasruudun alueelle 1/1/3/2, ja siihen päälle sijoitettu AU-merkki
   jäi rattaan alle — kaksi sääntöä samasta ruudusta on tässä sama vika
   kuin kaksi hintaa samasta rivistä. */

.hvaroitus { margin: 10px 16px 0; padding: 9px 12px; font-size: 12.5px;
             background: #FDF6E7; border-left: 3px solid var(--keltainen);
             border-radius: 0 5px 5px 0; line-height: 1.45; }

@media (max-width: 1000px) {
  .hvaroitus { margin: 8px 10px 0; }
}

/* --------------------------------------------- juuri lisätty rivi

   Askon vaiva: "nyt sitä ei löydä mihin se meni. ajattele jos iso
   laskenta ja joutuu etsimään mihin se rivi meni."

   Vieritys tuo rivin ruudulle, merkintä kertoo mikä niistä se on. Kun
   rakenteita lisätään kerralla kahdeksan, vieritys osuu yhteen ja loput
   löytyvät merkinnästä.

   Merkintä katoaa itsestään kuuden sekunnin jälkeen. Pysyvänä se olisi
   rivin ominaisuus eikä tapahtuma, ja seuraavan lisäyksen jälkeen
   merkittyjä olisi kaksi sarjaa ilman että kumpikaan kertoo kumpi oli
   uusi.

   Taulukkoetuliite on pakollinen: yleinen "table.ruudukko tr:hover > td"
   (0,2,3) voittaisi ilman sitä ja merkintä katoaisi hiiren alla — juuri
   silloin kun käyttäjä on menossa sen päälle. */
table.ruudukko tr.rakenne.uusi > td { background: var(--oranssi-vaalea); }
table.ruudukko tr.rakenne.uusi > td:first-child {
  box-shadow: inset 3px 0 0 var(--oranssi); }
/* Vilkku kertoo että jotain tapahtui juuri nyt. Se ajetaan kerran eikä
   toistu: toistuva liike ruudulla on häiriö eikä tieto. */
@media (prefers-reduced-motion: no-preference) {
  table.ruudukko tr.rakenne.uusi > td { animation: uusirivi .9s ease-out 1; }
}
@keyframes uusirivi {
  0%   { background: #FBD9B4; }
  100% { background: var(--oranssi-vaalea); }
}

/* ================================================== AI-haku

   Kirjasto on nimiluettelo; sisältö haetaan ja se maksaa. Siksi tämän
   näkymän painavin osa ei ole tulos vaan HINTA ENNEN PAINALLUSTA. */
.ainappi { padding: 2px 8px; font-size: 11px; font-weight: 700;
           letter-spacing: .3px; border-radius: 4px; cursor: pointer;
           border: 1px solid rgba(255,255,255,.55);
           background: rgba(255,255,255,.16); color: #fff; }
.ainappi:hover { background: var(--tausta); color: var(--oranssi-tumma); }
/* Gemini on näkyvä toiminto, ei pelkkä koristekuvake. Sama painike on
   pää- ja alalitteralla, jolloin käyttäjä oppii yhdellä silmäyksellä mistä
   kysymyslaatikko avautuu. Varsinainen haku- ja liittämisreitti on ennallaan. */
.ainappi.gemini {
  min-width: 68px; padding: 4px 8px; border-color: var(--sininen);
  background: var(--sininen); color: #fff; font-size: 11px;
  box-shadow: 0 1px 2px rgba(20, 25, 35, .16);
}
.ainappi.gemini:hover,
.ainappi.gemini:focus-visible {
  background: var(--tausta); color: var(--sininen); border-color: var(--sininen);
  outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--sininen) 22%, transparent);
}
.ainappi.gemini.pieni { min-width: 62px; padding: 3px 6px; }
/* Rakennerivin AI-nappi on kapeampi kuin paaryhman: sarake on kapea ja
   nappeja on rivilla monta. Sama vari ja sama toiminto. */
.ainappi.pieni { padding: 1px 5px; font-size: 10.5px; letter-spacing: .02em; }
/* AI-SARAKE ON YKSI PYSTYLINJA. Asko 24.8.2026: "logo pitää olla
   pystylinjassa kaikkien kanssa samassa linjassa."

   Kolme tasoa käyttää samaa saraketta — pääryhmän "AI-haku", rakenteen
   tähti ja panoksen pienempi tähti — ja ne olivat kolmessa eri
   vaakapaikassa, koska panosrivin solu oli keskitetty ja muut eivät.
   Keskitys tehdään tässä yhdessä paikassa, jolloin nappien keskilinja
   on sama riippumatta siitä kuinka leveä nappi on. */
td.aisolu { white-space: nowrap; text-align: center; }

/* AI-TÄHTI ALALITTERALLA eli rakennerivillä. Asko 24.8.2026.

   KOKO ON SIDOTTU RATTAASEEN. Asko: "näihin hitusen pienempi kuin
   vasen reuna logo." Rivin vasen ratas on 39 px:n kirjasinkoossa ja
   sen näkyvä kuvake on noin 27 px; tähti on 24 px eli hitusen
   pienempi. Jos rattaan kokoa muutetaan, tämä on se luku joka
   muutetaan sen mukana — muuten rivin kaksi kuvaketta alkavat
   kilpailla keskenään.

   Valkoinen tausta ei ole koriste vaan luettavuus: siniset ja vihreät
   sakarat katoavat väritetylle riville, ja juuri ne erottavat tämän
   napin muista. Sama syy kuin arviomerkin punaisella — väri on se
   tieto, joten sen on näyttävä.

   Nappi on neliö eikä pilleri: kuvakkeen ympärille jäävä tyhjä
   vaakatila näyttäisi katkenneelta tekstiltä. */
.ainappi.tahti { display: inline-flex; align-items: center;
                 justify-content: center; width: 30px; height: 30px;
                 padding: 0; border-radius: 7px;
                 background: var(--tausta); border: 1px solid rgba(0,0,0,.10);
                 /* MARGIN NOLLATAAN TÄSSÄ, ei peritä. Panosrivin nappi
                    peri margin-left: 6px vanhalta säännöltä siltä ajalta
                    kun se oli nimen perässä, ja se siirsi sen kolme
                    pikseliä pois muiden linjasta. Sarakkeen suoruus on
                    se mitä tässä mitataan, joten se ei saa riippua
                    siitä mitä nappi on joskus perinyt. */
                 margin: 0; }
.ainappi.tahti:hover { background: var(--tausta); transform: scale(1.1); }
.ainappi.tahti:active { transform: scale(.96); }
.aitahti { width: 24px; height: 24px; display: block; pointer-events: none; }

/* Gemini-logo on kuvakepainike myös pääryhmällä: tekstipainikkeen sininen
   pilleri peittäisi logon rauhallisen taustan ja rikkoisi AI-sarakkeen rytmin. */
.ainappi.gemini.tahti { min-width: 0; width: 30px; height: 30px; padding: 0;
                        background: var(--tausta); color: inherit;
                        border-color: rgba(0,0,0,.10); box-shadow: none; }
.ainappi.gemini.tahti:hover,
.ainappi.gemini.tahti:focus-visible { background: var(--tausta); color: inherit;
                                      border-color: rgba(0,0,0,.10);
                                      outline: none;
                                      box-shadow: 0 0 0 3px color-mix(in srgb, var(--sininen) 22%, transparent); }

/* ODOTTAVA TÄHTI. Asko 24.8.2026 lähetti Gemini-tyylisen animaation
   (kierto + syke + häivytys) ja kysyi miten se istuu tähän koodiin.

   Se istuu odotuslohkoon, jossa oli ennestään pyörivä kehrä. Kolme
   asiaa on toisin kuin ehdotuksessa, ja jokainen on syystä:

   1. LIUKUVÄRI ON NELJÄNÄ SAKARANA, ei yhtenä diagonaalina.
      Ehdotuksessa oli yksi kulmasta kulmaan kulkeva liukuväri — sama
      jonka mittasin 24.8. ja joka ei toimi koveralla nelisakaraisella:
      yläsakara ja vasen sakara osuvat akselilla samaan kohtaan ja
      saavat saman värin, ja päätevärit jäävät tyhjiin kulmiin. Tähti
      olisi ollut kaksivärinen.

   2. SYKE ON PIENEMPI. Ehdotuksen scale(0.8 → 1.2) on 50 % kasvu, ja
      se hyppii tekstirivin vieressä. 0.9 → 1.08 riittää kertomaan
      että jotain tapahtuu.

   3. KIERTO PYSÄHTYY, JOS KÄYTTÄJÄ NIIN PYYTÄÄ. Ehdotus jätti
      prefers-reduced-motionin huomiotta. Loputon pyörivä ja sykkivä
      kuvake on osalle käyttäjistä oikea haitta, ei tyyliseikka. */
@keyframes aitahti-miettii {
  0%   { transform: rotate(0deg)   scale(.9);   opacity: .55; }
  50%  { transform: rotate(180deg) scale(1.08); opacity: 1; }
  100% { transform: rotate(360deg) scale(.9);   opacity: .55; }
}
.aitahti.miettii { animation: aitahti-miettii 1.8s infinite ease-in-out; }
.aitahti.miettii.pieni { width: 15px; height: 15px;
                         display: inline-block; vertical-align: -3px; }
.odotus .aitahti.miettii { width: 44px; height: 44px;
                           margin: 0 auto 4px; }
@media (prefers-reduced-motion: reduce) {
  /* Ei kiertoa eikä sykettä — pelkkä hidas häivytys kertoo yhä että
     jotain on kesken. */
  @keyframes aitahti-miettii {
    0%, 100% { opacity: .5; }
    50%      { opacity: 1; }
  }
  .aitahti.miettii { animation-duration: 2.4s; }
}

/* PANOSRIVIN TÄHTI: KOLMANNES PIENEMPI JA PYKÄLÄ OIKEALLE.
   Asko 24.8.2026: "siirretään vihreet geminit pykälän oikealle — kun
   esim. maalaus littera sisältö avataan, tulevat ne silloin näkyviin.
   Logot 1/3 osaa pienempi kuin päälitteran gemini logo."

   Rakennerivin tähti on 24 px, joten kolmannes pienempi on 16 px.
   Siirto on 18 px eli sama pykälä jolla rivin sisältö sisentyy
   (panosnimi 44 px, pakettinimi 26 px). Silloin sarake kertoo tason
   samalla tavalla kuin nimisarake. */
/* PANOSRIVIN TÄHTI ON NIMEN EDESSÄ, ei AI-sarakkeessa. Asko
   24.8.2026 osoitti nuolella: "laitetaan nuo tuohon."

   Sijainti ratkaisi myös pystylinjan, joka oli aiemmin päivällä oma
   pyyntönsä: panosnimen sisennys on sama joka rivillä, joten nimen
   EDESSÄ tähdet ovat samassa kohdassa. Nimen PERÄSSÄ ne olisivat
   ragged, koska nimi on eri pituinen. Sarake ei siis ollut se mikä
   linjan teki — se oli kummalla puolen tekstiä nappi on. */
.ainappi.tahti.pieni { width: 22px; height: 22px; border-radius: 5px;
                       margin-right: 6px; vertical-align: -6px; }
.ainappi.tahti.pieni .aitahti { width: 16px; height: 16px; }

.aikohteet { max-height: 240px; overflow-y: auto; margin-top: 10px;
             border: 1px solid var(--viiva); border-radius: 6px; }
.aikohde { display: flex; gap: 10px; align-items: baseline;
           padding: 5px 10px; font-size: 12.5px;
           border-bottom: 1px solid var(--viiva); }
.aikohde:last-child { border-bottom: 0; }
.aikohde .nimi { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.aikohde .puuttuu { color: var(--teksti-himmea); white-space: nowrap; }
.aikohde.ilmainen { background: #F1FAF6; }
.ilmaismerkki { background: var(--vihrea); color: #fff; border-radius: 3px;
                padding: 0 5px; font-size: 11px; font-weight: 600; }
/* Tiimalasi pyorii niin kauan kuin haku on kaynnissa. Merkki U+29D7 on
   piirtomerkki eika kuva, joten se ei tarvitse tiedostoa — ja jos se
   puuttuisi fontista, rivilla lukisi yha "haetaan…" eli tieto ei
   katoaisi merkin mukana. */
.tiimalasi {
  display: inline-block; font-size: 13px;
  animation: tiimalasi 1.4s linear infinite;
}
@keyframes tiimalasi {
  0%   { transform: rotate(0deg); }
  55%  { transform: rotate(180deg); }
  100% { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  /* Liike pois jos jarjestelma sita pyytaa; merkki jaa nakyviin. */
  .tiimalasi { animation: none; }
}

.aipuuttuvat { margin-top: 12px; padding: 9px 12px; font-size: 12.5px;
               background: #FDF6E7; border-left: 3px solid var(--keltainen);
               border-radius: 0 5px 5px 0; }
.aipuuttuvat ul { margin: 5px 0 0; padding-left: 18px; }
.aitulos { margin-top: 12px; padding: 10px 12px; font-size: 13px;
           background: #F1FAF6; border-left: 3px solid var(--vihrea);
           border-radius: 0 5px 5px 0; }

@media (max-width: 1000px) {
  .ainappi { min-height: 34px; padding: 6px 12px; font-size: 12.5px; }
  .ainappi.tahti { min-height: 36px; width: 36px; height: 36px; padding: 0; }
  .ainappi.tahti .aitahti { width: 28px; height: 28px; }
  .aikohteet { max-height: 200px; }
  .aikohde { flex-wrap: wrap; min-height: 36px; }
}

/* Hylätyt: malli antoi luvun, lähteestä ei löytynyt, jätettiin pois.
   Ei tulos vaan todiste ehdon toimimisesta — siksi neutraali eikä
   varoitus. Varoituksen väri tekisi tästä ongelman, ja se on
   päinvastoin se kohta jossa ohjelma toimii oikein. */
.aihylatyt { margin-top: 10px; padding: 7px 10px; font-size: 12px;
             color: var(--teksti-himmea); background: var(--tausta-2);
             border-radius: 5px; }

/* ------------------------------------------- irrotettu ikkuna

   Kahden näytön työskentely: laskenta toisella näytöllä, kirjasto
   toisella. Irrotettu ikkuna on sama ohjelma samalla istunnolla, mutta
   se näyttää yhden näkymän eikä muuta.

   Merkki yläpalkissa kertoo kumpi ikkuna on kyseessä. Ilman sitä kaksi
   samannäköistä ikkunaa kahdella näytöllä on arvoitus, ja väärään
   ikkunaan kirjoittaminen huomataan vasta kun luku on väärässä
   paikassa. */
.irrotettumerkki { margin-left: auto; padding: 3px 9px; font-size: 11px;
                   font-weight: 700; letter-spacing: .4px;
                   border-radius: 4px; white-space: nowrap;
                   border: 1px solid rgba(255,255,255,.55);
                   background: rgba(255,255,255,.16); color: #fff; }
/* Alapalkkia ei ole: laskennan summat ovat päälaskennan luku, ja
   kahdessa paikassa näytettynä niistä tulee kaksi lukua jotka voivat
   erota hetkellisesti toisistaan. */
body.irrotettu #ala { display: none; }
body.irrotettu #runko { height: calc(100% - var(--yla)); }
body.irrotettu #sivu, body.irrotettu #sivupeite { display: none; }

/* ------------------------------------------- irrallinen ikkuna

   Rakenteita lisätään monta peräkkäin ja jokaisen jälkeen halutaan nähdä
   mitä laskennalle tapahtui. Himmennetty tausta tekee siitä mahdotonta:
   laskenta on lukossa ikkunan takana, ja ainoa tapa katsoa on sulkea
   ikkuna ja avata se uudestaan.

   Peite jää paikalleen sijoittajaksi mutta lakkaa olemasta este:
   pointer-events none päästää napsautukset läpi, ikkuna itse ottaa ne
   auto:lla takaisin. Himmennys pois, varjo jää — varjo on se joka kertoo
   että ikkuna on taustan päällä eikä osa sitä. */
#peite.irrallaan {
  background: none; pointer-events: none;
  /* Keskelle eikä oikeaan reunaan.

     REUNUS SAMA KUIN TAVALLISELLA PEITTEELLÄ (26px). Se oli 14px, ja
     koska laatikon leveys on 100 % peitteen sisätilasta, irrallinen
     laatikko oli 24 px leveämpi kuin muut: mitattu 1.9.2026 1 412 px
     vastaan 1 388 px. Ero ei näy silmällä mutta rikkoo saman koon. */
  align-items: flex-start; justify-content: center; padding: 26px;
}
#peite.irrallaan > .dialogi {
  pointer-events: auto;
  /* KORKEUS SAMA KUIN MUILLA. Oli calc(100% - 28px), ja se leikkasi
     irrallisen laatikon 26 px muita matalammaksi: mitattu 1.9.2026
     820 px vastaan 846 px. Ks. reunuksen perustelu ylempänä. */
  max-height: 100%;
  box-shadow: 0 10px 44px rgba(20,25,35,.32), 0 0 0 1px var(--viiva);
  /* OMA LEVEYS POISTETTU 1.9.2026. Asko: "laitoin aiemmin kuvan että
     miltä oletuslaatikot koonsa puolesta näyttää, ja se on tuo -->
     mittaa ja kaikkiin sama."

     TÄMÄ KUMOAA 31.8.2026 ANNETUN OHJEEN, ja se sanotaan ääneen: silloin
     Asko merkitsi ruutukuvaan "kun painaa Lisää rakenteita syntyy tämän
     kokoinen laatikko (mittaa)", ja irralliselle ikkunalle annettiin oma
     leveys min(1320px, 76vw). Mitattu 1.9.2026 selaimessa 1 440 px:n
     ikkunassa: viisi laatikkoa avautui kokoon 1388 x 846 ja tämä yksi
     kokoon 1094 x 844 — juuri sen oman leveyden takia.

     Irrallisuus on nyt vain käytöstä (ei himmennystä, napsautukset
     läpi) eikä kokoa. Koko tulee `.dialogi.iso`:sta kuten kaikilla
     muillakin, ja käyttäjän oma venytys voittaa sen edelleen. */
}
/* Otsikko kertoo että ikkuna on irrallinen. Ilman merkintää kaksi
   samannäköistä ikkunaa eroaa vain siitä toimiiko tausta, ja sen huomaa
   vasta kun sitä yrittää. */
#peite.irrallaan > .dialogi > header::after {
  content: 'laskenta käytettävissä'; font-size: 11px; font-weight: 600;
  color: var(--vihrea); border: 1px solid var(--vihrea);
  border-radius: 4px; padding: 1px 6px; white-space: nowrap;
}
@media (max-width: 1000px) {
  /* Puhelimessa ei ole tilaa vierekkäin, joten irrallinen on modaali.
     Kaventaminen tekisi molemmista käyttökelvottomia. */
  #peite.irrallaan { background: rgba(20,25,35,.42); pointer-events: auto;
                     align-items: center; justify-content: center; }
  #peite.irrallaan > .dialogi > header::after { content: none; }
}

/* ------------------------------------------- panostulos

   Sama näkymä liitetylle ja haetulle. Kolme lukemaa erikseen, koska
   "3 riviä" ei kerro saiko käyttäjä menekit vai muunnoksia — ja
   ensimmäinen mitattu rivi 22.8.2026 oli juuri muunnos. */
.tuloslukemat { display: flex; flex-wrap: wrap; gap: 6px 10px;
                align-items: baseline; margin: 4px 0 10px;
                font-size: 12.5px; }
.lukema { padding: 1px 8px; border-radius: 10px; font-weight: 600;
          border: 1px solid; white-space: nowrap; }
/* LUETTU on vihreä: käyttäjä haki sivun itse, ohjelma vain luki sen.
   TYYLI.md antaa vihreän vahvistetulle. */
.lukema.luettu   { color: var(--vihrea);    border-color: var(--vihrea); }
/* LASKETTU on sininen: lähteellinen mutta johdettu — ei sama kuin
   sivulta luettu, eikä arvio. */
.lukema.laskettu { color: var(--sininen);   border-color: var(--sininen); }
/* TYHJÄ on keltainen: puuttuva luku, ei väärä luku. */
.lukema.tyhja    { color: var(--keltainen); border-color: var(--keltainen); }
.lukema.hylatty  { color: var(--teksti-himmea); border-color: var(--viiva); }

table.tulostaulu { width: 100%; margin-top: 4px; }
table.tulostaulu tr.oletusrivi > td { background: #F3F7FE; }
.hakurivi { display: flex; gap: 8px; align-items: center; }
.hakurivi input { flex: 1 1 auto; }
/* Tiimalasi pyörii kun palvelin lukee. Ilman sitä liittäminen näyttää
   siltä ettei mitään tapahtunut, ja käyttäjä liittää uudestaan. */
.tiimalasi { display: inline-block; animation: tiimalasi 1.1s linear infinite; }
@keyframes tiimalasi { to { transform: rotate(360deg); } }

/* Haun ulkopuolelle jääneet. Syyt eritellään: "on jo sisältö" kehottaa
   tyhjentämään rivin, "oman kirjaston rakenne" sanoo ettei sitä pidä
   hakea lainkaan. Yksi luku ei erottaisi niitä. */
.aiohitetut { margin-top: 10px; padding: 8px 11px; font-size: 12.5px;
              background: var(--tausta-2); border-radius: 5px;
              color: var(--teksti-himmea); line-height: 1.45; }
.aiohitetut ul { margin: 4px 0 0; padding-left: 18px; }

/* ------------------------------------- kirjaston päälitterarivit

   Askon linjaus 22.8.2026: "Rakennekirjastoon laitetaan mukaan
   päälitterarivit, jonka alla on aina siihen kuuluvat. katselu on näin
   selvempää."

   Tasainen luettelo 1 690 rakenteesta on luettelo jossa ei ole paikkaa:
   rivit ovat litterajärjestyksessä mutta mikään ei kerro missä yksi
   ryhmä loppuu ja seuraava alkaa. */
table tr.litteraotsikko > td {
  background: var(--tausta-3); border-top: 1px solid var(--viiva);
  padding-top: 7px; padding-bottom: 7px; cursor: pointer;
  position: sticky; top: 0; z-index: 2;
}
table tr.litteraotsikko:hover > td { background: var(--oranssi-vaalea); }
table tr.litteraotsikko .nuoli.avaus { color: var(--oranssi);
                                                font-weight: 700; }
/* Kiinni oleva ryhmä ei näytä tyhjältä vaan suljetulta: nuoli kääntyy ja
   otsikko jää paikalleen. Ilman eroa suljettu ryhmä näyttää siltä että
   siinä ei ole mitään. */
table tr.litteraotsikko.kiinni > td { background: var(--tausta-2); }
table tr.kirjastorivi > td:first-child { padding-left: 18px; }

/* --------------------------------------------------- hakuosumat

   Sivut joille ohjelma ei päässyt mutta käyttäjä pääsee. Ei
   lohdutuspalkinto: mitattu 22.8.2026 että rajattu haku antaa 9/10
   asennusohjetta joilla luvut oikeasti ovat. Vihreä, koska seuraava
   askel on käyttäjän ja se onnistuu. */
.osumat { margin-top: 12px; padding: 10px 12px; font-size: 12.5px;
          background: #F1FAF6; border-left: 3px solid var(--vihrea);
          border-radius: 0 5px 5px 0; line-height: 1.45; }
.osumalista { margin: 8px 0; padding-left: 20px; }
.osumalista li { margin-bottom: 6px; }
.osumalista a { color: var(--sininen); }
.osumalista .katkaise { overflow-wrap: anywhere; font-size: 11px; }
.osumat .liitoskentta { width: 100%; margin: 6px 0 0; }

/* Kuvana oleva sivu on PARAS osuma eikä epäonnistuminen: mitattu kaksi
   asennusohjetta joissa tekstinpoiminta antoi tyhjän mutta luvut olivat
   sivulla luettavina. Siksi sama vihreä kuin muillakin osumilla, ja
   ylimpänä listassa. */
.osumat.kuvana { border-left-color: var(--vihrea); background: #ECF8F2; }

/* Osuman laji merkkinä rivin alussa. Käyttäjä selaa listaa merkeistä ja
   lukee ohjeen vasta siitä rivistä jonka aikoo avata. */
.osumalaji { display: inline-block; margin-right: 6px; padding: 0 6px;
             border-radius: 9px; font-size: 10.5px; font-weight: 700;
             border: 1px solid; white-space: nowrap; vertical-align: 1px; }
/* Kuvana on paras osuma, ei epäonnistuminen: sivu on oikea ja luvut
   ovat siellä. Siksi vihreä, sama väri kuin vahvistetulla. */
.osumalaji.kuvana { color: var(--vihrea); border-color: var(--vihrea); }
.osumalaji.muuri  { color: var(--sininen); border-color: var(--sininen); }
.osumalaji.tyhja  { color: var(--teksti-himmea); border-color: var(--viiva); }

/* Valmiit hakukysymykset. Asko 22.8.2026: laatikossa on useita valmiita
   kysymyksiä, joista valitaan mitä haetaan — ja kysymys on NÄKYVISSÄ
   eikä piilossa, jotta sen voi korjata ennen hakua. */
.kysymysvalinnat {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 10px 0 8px;
}
.kysymysvalinta {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 10px 4px 6px; border: 1px solid var(--viiva);
  border-radius: 3px; cursor: pointer; min-height: 32px;
}
.kysymysvalinta:hover { background: var(--pinta2, rgba(0,0,0,.03)); }
.kysymysvalinta input { min-width: 16px; min-height: 16px; margin: 0; }
.kysymyskentta {
  width: 100%; font: inherit; font-size: 13px; line-height: 1.45;
  padding: 8px 10px; border: 1px solid var(--viiva); border-radius: 3px;
  resize: vertical; box-sizing: border-box;
}

/* AI-nappi panosrivillä. Asko 22.8.2026: "näyttäs olevan jotain nappeja
   mutta ei selkeästi."

   .ainappi on maalattu oranssille ja siniselle riville: valkoinen reunus
   ja läpikuultava valkoinen tausta. Panosrivi on valkoinen, joten nappi
   oli valkoista valkoisella — se näkyi vain haaleana ympyränä nimen
   perässä eikä siitä nähnyt mitä se on. */
.panosnimi .ainappi:not(.tahti) {
  border-color: var(--oranssi, #d2691e);
  background: var(--oranssi, #d2691e);
  color: #fff;
  padding: 1px 6px;
  margin-left: 8px;
  vertical-align: middle;
  opacity: .85;
}
.panosnimi .ainappi:not(.tahti):hover {
  opacity: 1;
  background: var(--oranssi-tumma, #a8501a);
  border-color: var(--oranssi-tumma, #a8501a);
  color: #fff;
}
tr.panos:not(:hover) .panosnimi .ainappi:not(.tahti) { opacity: .55; }

/* Kertoimen palautusnappi. Asko 22.8.2026: "vaikeutta ei saa pois
   mistään napista." Tyhjä perii laskelmalta, annettu luku lukitsee —
   ja ilman nappia rivi jää lukituksi vahingossa. */
.infokentta .kenttarivi { display: flex; align-items: center; gap: 4px; }
.infokentta .kenttarivi input { flex: 1 1 auto; min-width: 0; }
.tyhjenna {
  flex: 0 0 auto; width: 24px; height: 24px; min-height: 24px;
  padding: 0; line-height: 1; font-size: 15px;
  border: 1px solid var(--viiva); border-radius: 3px;
  background: transparent; color: var(--himmea); cursor: pointer;
}
.tyhjenna:hover:not(:disabled) {
  background: var(--oranssi-vaalea); border-color: var(--oranssi);
  color: var(--oranssi-tumma);
}
.tyhjenna:disabled { opacity: .3; cursor: default; }

/* AI-nappi muokkausikkunan työkalurivillä. Oranssi kuten muutkin
   AI-napit, mutta painikkeen muotoinen — se on rivissä painikkeiden
   kanssa eikä nimen perässä. */
.painike.ainappi-ikkuna {
  border-color: var(--oranssi); background: var(--oranssi); color: #fff;
  font-weight: 600;
}
.painike.ainappi-ikkuna:hover {
  background: var(--oranssi-tumma); border-color: var(--oranssi-tumma);
}

/* Iso tiimalasi ja sekuntilaskuri. Asko 23.8.2026: "tiimalasi
   suureksi 10 kertaa suuremmaksi ja sekuntilaskuri alapuolelle."

   Kutsu kestää 30–60 sekuntia. Pieni merkki ei kerro että jotain
   tapahtuu — se on sama vikaluokka kuin vaiettu pysäytys, ja siitä
   syntyy se tunne että ohjelma sahaa. */
.odotus { text-align: center; padding: 22px 10px; }
/* .sekunnit ja .selite poistuivat 24.8.2026: "ei tekstejä, esim
   gemini hakee. Pelkkä logo animointi." Säännöt jäivät pois samalla,
   koska tyhjä sääntö näyttää unohtuneelta eikä päätökseltä. */

/* NAPIN VÄRI TILANA — POISTETTU 24.8.2026, ei unohtunut.

   Asko 23.8.2026: "AI-napit punaisella, sitten kun on onnistuneesti
   haettu tai tehty oma rivi, muuttuu vihreäksi." Sääntö oli hyvä
   tekstinapille: kymmenen samanväristä nappia on sama kuin ei mitään
   tietoa.

   Asko 24.8.2026, kun napista tuli tähti: "kaikki logot saman
   näköisiä, en pyytänyt vihreää logoa." Se kumoaa edellisen, ja syy
   on eri kuin ensimmäisellä kerralla — tunnus ei ole mittari.

   ÄLÄ PALAUTA TÄTÄ näkemättä SOPIMUS.md:n riviä. Tämä on ollut
   punaisena, vihreänä, renkaana ja täyttönä saman päivän aikana, ja
   jokainen versio näytti puuttuvalta ominaisuudelta seuraavalle
   lukijalle. */
.aiempi {
  background: var(--tausta-2, #f4f5f7); border: 1px solid var(--viiva);
  border-radius: 4px; padding: 8px 10px; margin-bottom: 10px;
  font-size: 12.5px;
}
.aiempi .pieni { color: var(--himmea); margin-top: 3px; word-break: break-word; }

/* Montako riviä sai hinnan. Asko 23.8.2026: yhdeksällä kymmenestä oli
   hinta eikä se näkynyt mistään — poiminta oli tehnyt työn, näkymä ei
   kertonut siitä. */
.lukema.hinnoiteltu {
  background: var(--vihrea-oma, rgba(120,200,160,.30));
  color: var(--vihrea, #12805C);
}

/* ALV-sääntö hakulaatikossa. Vähennys on oletus ja se sanotaan
   ääneen; poikkeus on yksi ruutu, ei kysymys joka kerta. */
.alvsaanto {
  margin: 8px 0; padding: 9px 11px; border-radius: 6px;
  border: 1px solid var(--reuna); background: var(--pohja2);
  font-size: 13px; line-height: 1.45;
  display: flex; flex-direction: column; gap: 7px;
}
.alvpoikkeus {
  display: flex; align-items: center; gap: 6px; cursor: pointer;
}
.alvpoikkeus:has(input:checked) { font-weight: 600; }

/* Gemini-haun laatikko etenee työvaiheina. Tämä koskee vain näkymää:
   kysymyksen muodostus, Googleen avautuminen ja poiminta eivät muutu. */
.liitamenekit-dialogi { display: grid; gap: 12px; }
.liitamenekit-dialogi .hvaroitus { margin: 0; }
.ai-vaihepolku {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;
  margin: 0; padding: 0; list-style: none;
}
.ai-vaihepolku li {
  display: flex; gap: 8px; align-items: center; min-height: 52px; padding: 8px 10px;
  border: 1px solid var(--viiva); border-radius: 8px; background: var(--tausta-2);
  color: var(--teksti-himmea); font-size: 11px; line-height: 1.25;
}
.ai-vaihepolku b { display: block; margin-bottom: 2px; color: var(--teksti); font-size: 12px; }
.ai-vaihenumero {
  display: grid; flex: 0 0 23px; place-items: center; width: 23px; height: 23px;
  border-radius: 50%; color: #fff; font-size: 11px; font-weight: 800;
}
.ai-vaihepolku .valitse { border-color: color-mix(in srgb, var(--sininen) 30%, var(--viiva)); }
.ai-vaihepolku .valitse .ai-vaihenumero { background: var(--sininen); }
.ai-vaihepolku .hae { border-color: color-mix(in srgb, var(--oranssi) 30%, var(--viiva)); }
.ai-vaihepolku .hae .ai-vaihenumero { background: var(--oranssi); }
.ai-vaihepolku .poimi { border-color: color-mix(in srgb, var(--vihrea) 30%, var(--viiva)); }
.ai-vaihepolku .poimi .ai-vaihenumero { background: var(--vihrea); }
.ai-osio { padding: 12px; border: 1px solid var(--viiva); border-radius: 8px;
            background: var(--tausta); box-shadow: 0 1px 2px rgba(20,25,35,.04); }
.ai-osio h3 { margin: 0; font-size: 13px; letter-spacing: -.01em; }
.ai-lajiosio {
  border-left: 3px solid var(--sininen);
  border-color: color-mix(in srgb, var(--sininen) 32%, var(--viiva));
  border-left-color: var(--sininen);
  background: color-mix(in srgb, var(--sininen) 4%, var(--tausta));
}
.ai-lajiosio h3 { color: var(--sininen); }
.ai-hakuosio {
  border-left: 3px solid var(--oranssi);
  border-color: color-mix(in srgb, var(--oranssi) 32%, var(--viiva));
  border-left-color: var(--oranssi);
  background: color-mix(in srgb, var(--oranssi-vaalea) 58%, var(--tausta));
}
.ai-hakuosio h3 { color: var(--oranssi-tumma); }
.ai-vastausosio {
  border-left: 3px solid var(--vihrea);
  border-color: color-mix(in srgb, var(--vihrea) 32%, var(--viiva));
  border-left-color: var(--vihrea);
  background: color-mix(in srgb, var(--vihrea) 4%, var(--tausta));
}
.ai-vastausosio h3 { color: var(--vihrea); }
.ai-vastausosio { padding-top: 14px; }
.ai-vastausosio .ai-kenttaotsikko > span { color: var(--vihrea); }
.ai-osioselite { margin: 4px 0 9px; font-size: 12.5px; color: var(--teksti-himmea); }
.ai-osio .kysymysvalinnat { margin: 0 0 10px; }
.ai-kenttaotsikko { display: grid; gap: 5px; font-size: 12px; font-weight: 650;
                     color: var(--teksti-himmea); }
.ai-kenttaotsikko textarea { color: var(--teksti); font-weight: 400; }
.ai-hakuapu { margin-top: 7px; font-size: 12px; color: var(--teksti-himmea); }
.liitamenekit-dialogi .hakurivi { padding: 9px 0 0; }
.liitamenekit-dialogi textarea.liitoskentta { width: 100%; margin: 0;
                                               min-height: 150px; }
.ai-lahderivi { margin: 10px 0 0; }
.ai-vastausosio .alvsaanto { margin: 10px 0 0; }
.ai-poimintarivi { border-top: 1px solid var(--viiva); }
.liitamenekit-dialogi .hintatulos { padding: 0; }

/* Työpöydällä haku ei saa muuttua pitkäksi pystysuoraksi seinäksi. Leveä
   ikkuna käyttää tilan sivusuunnassa: aineistolaji ja kysymys ovat vierekkäin,
   vastausvaihe niiden alla. Tällöin myös lähde, ALV-poikkeus ja poiminta
   näkyvät ilman että ne jäävät alatunnisteen alle. */
@media (min-width: 900px) {
  #dialogi[data-avain="liitamenekit"] {
    width: min(1080px, calc(100vw - 52px));
    height: min(880px, calc(100dvh - 52px));
  }
  .liitamenekit-dialogi {
    grid-template-columns: minmax(230px, .62fr) minmax(0, 1.38fr);
    gap: 9px;
  }
  .liitamenekit-dialogi .hvaroitus,
  .liitamenekit-dialogi .ai-vaihepolku,
  .liitamenekit-dialogi .ai-vastausosio { grid-column: 1 / -1; }
  .ai-lajiosio { grid-column: 1; }
  .ai-hakuosio { grid-column: 2; }
  .ai-vastausosio .ai-osioselite { display: none; }
  .ai-osio { padding: 10px; }
  .ai-osio .kysymysvalinnat { margin-bottom: 7px; }
  .liitamenekit-dialogi textarea.liitoskentta { min-height: 96px; }
  .ai-vastausosio .alvsaanto { padding: 7px 9px; font-size: 12px; }
}

@media (max-width: 620px) {
  .ai-vaihepolku { grid-template-columns: 1fr; }
  .ai-vaihepolku li { min-height: 42px; }
}

/* Hinnan kaksi tilaa: haku verkosta tai oma hinta. Vain toinen näkyy
   kerrallaan — puoliksi näkyvä haku omahintatilassa houkuttelisi
   liittämään vastauksen kenttään joka ei sitä lue. */
.piilossa { display: none !important; }
.tilavalinta { display: flex; gap: 6px; margin-bottom: 10px; }
.tilanappi {
  padding: 6px 14px; border: 1px solid var(--reuna); border-radius: 6px;
  background: var(--pohja2); cursor: pointer; font-size: 13px;
  font-family: inherit; color: inherit;
}
.tilanappi:hover { border-color: var(--korostus); }
.tilanappi.nyt {
  border-color: var(--korostus); background: var(--korostus-vaalea);
  font-weight: 600;
}
.omahinta { display: flex; flex-direction: column; gap: 8px; }
.omarivi { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.omahintakentta {
  width: 130px; text-align: right; font-variant-numeric: tabular-nums;
}
.omayksikko { color: var(--himmea, #7a7a7a); min-width: 44px; }

/* Yksikkö hinnan vieressä. Panosrivin hinta osuu "Työt"-otsikon alle,
   koska panosrivi käyttää samoja soluja eri merkityksessä kuin
   summarivi. Yksikkö kertoo mistä on kyse ilman otsikkoa. */
/* Yksikkö OMALLE RIVILLEEN hinnan alle. Asko 23.8.2026: "jotta noita
   pisteitä ei tulisi, voisi ohjelma muuttaa rivileveyden automaattisesti,
   jos pitkä niin sitten rivitys."

   Taulukko on table-layout: fixed sen jälkeen kun sarakkeet on kerran
   mitattu, ja ylipitkä sisältö katkaistiin kolmeen pisteeseen. Vieressä
   ollut "€/m" ei mahtunut hintakentän rinnalle, joten näytöllä luki
   "0,8800 …" eikä yksikköä näkynyt lainkaan — juuri se tieto katosi
   jonka takia yksikkö sinne lisättiin.

   Sarakkeen leventäminen ei ole ratkaisu: se veisi tilaa naapureilta
   joka rivillä, vaikka pitkä sisältö on harvinainen. Rivitys vie tilaa
   vain siltä riviltä joka sitä tarvitsee. */
.hintayksikko {
  display: block; text-align: right; margin-left: 0; margin-top: 1px;
  font-size: 11px; color: var(--himmea, #7a7a7a); white-space: nowrap;
}

/* Kustannuslajin valitsin panoksen nimen vieressä. Pieni ja himmeä:
   se on harvoin vaihdettava asetus, ei rivin sisältöä.

   EI SAA KASVAA. Mitattu 25.8.2026: valitsin peri `.solukentta`-luokan
   `flex: 1 1 auto`:n ja venyi 289 pikseliin näyttääkseen sanan
   "Materiaalit". Nimelle jäi 74 px, ja siihen ei mahdu
   "Kiinnitysruuvit" — ruudulla luki "Kiinnitysr / uuvit".

   Asko kysyi kahdesti miten kapean laatikon sisällön näkee. Vika ei
   ollut nimessä eikä sananjaossa vaan siinä, että naapuri vei tilan
   jota se ei tarvinnut. */
.lajivalinta {
  margin-left: 8px; font-size: 11px; padding: 1px 3px;
  color: var(--himmea, #7a7a7a);
  flex: 0 0 auto; width: auto; min-width: 0; max-width: 96px;
}

/* Kasvava ikkuna kiinnitetään yläreunaan. Pystykeskitys siirtää ikkunaa
   joka kerta kun sisällön korkeus muuttuu, ja hakulaatikossa se muuttuu
   kolmesti yhden haun aikana: kysymys → tiimalasi → tulostaulukko.
   Asko 23.8.2026: "kuvaruutu pomppii haun aikana." */
.dialogi.kasvaa { align-self: flex-start; }

/* Odotuslohko varaa oman tilansa, jottei sekuntilukema hyppää. */
.odotus { min-height: 150px; box-sizing: border-box; }

/* Poimintanappi liitoskentän alla. Poiminta ei lähde liittämisestä
   vaan napista: liitetty teksti on usein puolikas, kutsu maksaa ja
   kestää, eikä tekstiä ehtinyt korjata ennen kuin se lähti. */
.poimintanappi { min-width: 190px; }

/* ALV-merkki hakutuloksessa: luku ei kerro veroa, teksti kertoo. */
.alvmerkki {
  font-size: 11px; padding: 2px 7px; border-radius: 10px;
  background: var(--pohja2, #f2f4f7); color: var(--himmea, #7a7a7a);
  border: 1px solid var(--reuna, #d9dde3);
}

/* Panoksen hinta rivin omassa yksikössä, hintakentän alla. Johdettu
   luku (menekki × yksikköhinta), joten himmeämpi kuin syötetty. */
.perhinta {
  display: block; text-align: right; margin-top: 1px;
  font-size: 11px; color: var(--himmea, #7a7a7a); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Palkki joka kertoo että sivu ajaa vanhaa koodia. Alareunassa, koska
   yläreuna on täynnä työkaluja — ja koska tämä ei ole kiireellinen:
   käyttäjä lataa uudelleen kun hänelle sopii. */
.paivityspalkki {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px;
  z-index: 400; display: flex; align-items: center; gap: 12px;
  padding: 10px 16px; border-radius: 10px; font-size: 13px;
  background: var(--pohja2, #fff); color: var(--teksti, #222);
  border: 1px solid var(--korostus, #d2601a);
  box-shadow: 0 8px 28px rgba(0,0,0,.22);
  max-width: min(680px, 92vw);
}
/* Valittava rivi luettelossa: koko rivi on napsautettava. */
tr.valintarivi { cursor: pointer; }
tr.valintarivi:hover > td { background: #FAFBFC; }
tr.valintarivi.valittu > td { background: var(--korostus-vaalea, #FDF1E7); }
html.yo tr.valintarivi:hover > td { background: #202730; }
/* Ostetun tuotteen valinta hakutuloksessa: rivin nimike vaihtuu siksi
   mitä sivulla luki, ellei käyttäjä käännä rastia. */
.tuotevalinta {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  padding: 7px 10px; border-radius: 6px; font-size: 13px;
  background: var(--pohja2, #f2f4f7); border: 1px solid var(--reuna);
  flex-wrap: wrap;
}
.tuoterasti { display: flex; align-items: center; gap: 7px; cursor: pointer;
  white-space: nowrap; }
/* Nimi vie tilan, koodi on lyhyt ja tunnistettava numerosarja. */
.tuotevalinta .tuotenimi { flex: 1; min-width: 190px; }
.tuotevalinta .tuotekoodi { width: 130px; font-variant-numeric: tabular-nums; }

.mtilamerkki {
  display: inline-block; padding: 2px 8px; border-radius: 3px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--viiva); background: var(--tausta);
  color: var(--teksti-himmea);
}
/* Hankinnan tila on työjonon tärkein tieto. Värit eivät yksin kanna
   merkitystä: teksti kertoo vaiheen, väri tekee seuraavan toiminnon
   löydettäväksi pitkällä taulukolla. */

/* ERITTELEMÄTÖN OSUUS. Näyttää panosriviltä mutta ei ole panos: se on
   paketin oma yksikköhinta jota panokset eivät kata, ja pakettitaso ei
   enää näy (24.8.2026). Omalla luokalla siksi, ettei koodi joka laskee
   panosrivejä laskisi tätä mukaan. */
tr.jaannosrivi { background: var(--pohja2, #f7f8fa); }
tr.jaannosrivi td { color: var(--himmea); font-style: italic; }

/* Keneltä-kenttä ja sen oikotie rekisteriin samaan riviin. Nappi ei
   saa työntää kenttää kapeaksi: kenttä venyy, nappi on vakiolevyinen. */
.kenelta { display: flex; gap: 6px; align-items: center; }
.kenelta > input { flex: 1; min-width: 0; }

/* Liitoslajin valinta ja tarjoustulos, ks. palvelin/tarjousliitos.py. */
.liitoslajit { border-bottom: 1px solid var(--viiva); padding-bottom: 8px;
               margin-bottom: 6px; }
.liitoslajiselite { margin-top: 0; margin-bottom: 10px; }

/* YKSIKKÖKENTTÄ JA SEN VALIKKONUOLI. Asko 25.8.2026: "ei toimi, huom
   pitää olla myös erä vaihtoehto."

   Kenttä oli HTML-datalist, ja selain suodattaa sen ehdotukset sen
   mukaan mitä kentässä jo lukee: "m2" näytti vain m2/brm2/htm2/hum2,
   eikä "erä" voinut näkyä koskaan. Nyt nuoli avaa ohjelman oman
   valikon, joka näyttää aina kaikki yksiköt.

   Nuoli on kentän sisällä oikeassa reunassa, jottei sarake levene. */
.yksikkokentta { position: relative; display: block; }
.yksikkokentta > input { width: 100%; padding-right: 20px; }
/* OSUMAKOHTA ON ISOMPI KUIN MERKKI. Mitattu 25.8.2026: ensimmäinen
   versio oli 9 x 10 px, ja sitä osuu hiirellä huonosti kun taulukossa
   on 94 riviä. Merkki pysyy pienenä jottei sarake näytä sekavalta;
   painallusalue ulottuu kentän koko korkeudelle. */
.yksikkonuoli {
  position: absolute; right: 0; top: 0; bottom: 0; width: 20px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer; padding: 0;
  font-size: 10px; line-height: 1; color: var(--teksti-himmea);
}
.yksikkonuoli::after { content: '▾'; }
.yksikkonuoli:hover { color: var(--oranssi); background: var(--tausta-2); }
/* Nykyinen yksikkö erottuu valikossa. */
.pikavalikko button.valittu { font-weight: 700; color: var(--oranssi-tumma); }

/* VÄLIVAIHE: yksi littera tarkistettavaksi poiminnan jälkeen.
   Asko 25.8.2026: "jos on paljon litteroita ei löydä heti mihin se
   meni. tehdään välivaihe --> aukeaa vaan ko. littera."

   Palkki on kiinni rivistön yläpuolella, koska rivejä korjatessa
   vieritetään — ja hyväksyminen on se teko johon palataan. */
.tarkistuspalkki {
  position: sticky; top: calc(var(--yla) + 8px); z-index: 12;
  display: flex; gap: 16px; align-items: center;
  justify-content: space-between; flex-wrap: wrap;
  margin: 12px 0 10px;
  padding: 11px 14px;
  background: linear-gradient(90deg, var(--oranssi-vaalea), var(--tausta) 72%);
  border: 1px solid color-mix(in srgb, var(--oranssi) 36%, var(--viiva));
  border-left: 4px solid var(--oranssi);
  border-radius: 10px;
  box-shadow: 0 5px 14px rgba(20, 28, 38, .12);
}
.tarkistuskuvaus { min-width: 0; flex: 1 1 420px; }
.tarkistustila {
  display: inline-block; margin: 0 0 5px; padding: 3px 8px;
  border-radius: 999px; background: var(--oranssi); color: #fff;
  font-size: 11px; font-weight: 800; letter-spacing: .035em; text-transform: uppercase;
}
.tarkistusotsikko { display: flex; align-items: baseline; gap: 2px; color: var(--teksti); font-size: 14px; line-height: 1.35; }
.tarkistusotsikko .t80 { color: var(--oranssi-tumma); font-weight: 750; }
.tarkistusviesti { margin-top: 7px; padding: 6px 8px; border-radius: 6px; background: color-mix(in srgb, var(--oranssi-vaalea) 72%, transparent); color: var(--teksti-himmea); font-size: 12px; line-height: 1.4; }
.tarkistusviesti b { color: var(--oranssi-tumma); }
.tarkistustoiminnot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.tarkistustoiminnot .painike { margin: 0; white-space: nowrap; }

@media (max-width: 900px) {
  .tarkistuspalkki { top: calc(var(--yla) + 4px); }
  .tarkistustoiminnot { width: 100%; justify-content: flex-start; }
}

/* PANOKSEN NIMI TEKSTINÄ, EI KENTTÄNÄ — jotta se rivittyy.

   Asko 25.8.2026 kysyi kahdesti miten kapean laatikon sisällön näkee.
   `<input>` ei rivity koskaan, joten "Muurauksen aputyö" ja
   "Muuraustyö" näyttivät molemmat samalta: "Muurau…". Selite hiiren
   alla ei ollut vastaus — se peitti naapuririvin nimen.

   Teksti saa rivittyä ja katkaista pitkän sanan, jottei se työnnä
   saraketta leveämmäksi. Reunus näkyy vasta hiiren alla: rivillä on
   kymmenen kenttää, ja kymmenen laatikkoa kerralla on melua. */
.nimiteksti {
  display: block; flex: 1 1 auto; min-width: 0;
  /* break-word EIKA anywhere. `anywhere` pienentaa elementin
     min-content-leveyden yhteen merkkiin, jolloin flex kutistaa sen
     lahes nollaan ja teksti katkeaa keskelta sanaa: mitattu
     25.8.2026, ruudulla luki "asennus / styö" ja "Kiinnitysr /
     uuvit". break-word katkaisee vain sanan joka ei mahdu
     omalle rivilleen. */
  overflow-wrap: break-word; white-space: normal;
  padding: 3px 6px; border: 1px solid transparent; border-radius: 6px;
  cursor: text; line-height: 1.25;
}
.nimiteksti:hover { border-color: var(--viiva); background: var(--tausta-2); }
.nimiteksti:focus-visible { outline: 2px solid var(--oranssi); outline-offset: 0; }
/* Rivi saa kasvaa nimen mukaan. */
tr.panos td.panosnimi > div { align-items: flex-start; }

/* VETOKAHVA NIMEN JA KUSTANNUSLAJIN VÄLIIN. Asko 25.8.2026 piirsi
   viivan juuri tähän kohtaan: "viivan kohdalle laatikko jako niin vois
   levennellä".

   Nimi ja laji ovat samassa solussa, joten taulukon oma sarakekahva ei
   ylety tähän. Veto vasempaan levittää nimeä, oikeaan valitsinta —
   sama suunta kuin sarakkeilla.

   Kahva on kapea muttei näkymätön: hiiren alla se tummuu, jotta sen
   löytää ilman että se on rivillä koko ajan viivana. */
.lajikahva {
  flex: 0 0 auto; width: 7px; align-self: stretch; min-height: 18px;
  cursor: col-resize; border-radius: 2px;
  background: linear-gradient(var(--viiva), var(--viiva)) center / 1px 70%
              no-repeat;
}
.lajikahva:hover, .lajikahva.vedossa {
  background: linear-gradient(var(--oranssi), var(--oranssi)) center / 2px 90%
              no-repeat;
}
/* Leveys tulee muuttujasta, jotta veto koskee kaikkia rivejä kerralla.

   VALITSIN TARVITSEE TARKEMMAN VALITSIMEN. Mitattu 25.8.2026: sääntö
   `tr.panos td.panosnimi .solukentta` on tarkempi kuin `.lajivalinta`
   ja pakotti valitsimelle `flex: 1 1 auto` ja `min-width: 120px` —
   siksi se venyi nimen kustannuksella eikä veto vaikuttanut mihinkään
   vaikka luku talletettiin oikein. */
tr.panos td.panosnimi select.lajivalinta {
  flex: 0 0 auto;
  width: var(--lajileveys, 96px);
  min-width: 0; max-width: none;
}

/* IRROTUSNAPPI IKKUNAN OTSIKOSSA. Asko 25.8.2026: "voiko nuo ikkunat
   irrotaa niin että voi viedä vaikka toiselle näytölle?"

   Sulkunapin vieressä, samankokoisena mutta himmeämpänä: irrotus on
   harvinaisempi teko kuin sulkeminen, eikä se saa kilpailla siitä
   mihin käsi menee. */
.dialogi > header .irrotanappi {
  border: 0; background: none; cursor: pointer;
  font-size: 16px; line-height: 1; padding: 2px 6px;
  color: var(--teksti-himmea); border-radius: 6px;
}
.dialogi > header .irrotanappi:hover {
  color: var(--oranssi); background: var(--tausta-2);
}
/* Irrotetussa ikkunassa laatikko täyttää ikkunan: siellä ei ole muuta.

   MYÖS KORKEUS. Sovi hinta -laatikolle on `height: min(88vh, 940px)`,
   jotta päälaskennasta näkyy reuna takana. Irrotetussa ikkunassa takana
   ei ole mitään, joten 12 %:n reuna ja 940 px:n katto ovat pelkkää
   hukkaa juuri siinä ikkunassa joka avattiin tilan takia. */
body.irrotettu .dialogi { width: min(100%, 100vw); max-height: 100vh; }
body.irrotettu #peite { padding: 0; }
body.irrotettu #peite { background: var(--tausta); }

/* KENELTÄ HINTA ON KYSYTTY, panosrivin littera-sarakkeessa.
   Asko 25.8.2026: "jos niihen laittaisi toimittajan… tietäisin heti
   että hinta on kysytty."

   Sarake on kapea, joten nimi katkeaa kolmeen pisteeseen ja koko nimi
   on selitteessä. Vihreä on sama merkitys kuin muualla: tällä on
   lähde. */
.panostoimittaja {
  display: block; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; padding: 1px 5px; border-radius: 5px;
  background: var(--vihrea-oma); color: #14532D;
}

/* --------------------------------------------------------- VALIOTSIKOT

   Litterasta johdetut ryhmaotsikot laskelman ja rakenteiden valissa.
   Asko 25.8.2026: "tulee muuten liian vaikealukuinen laskenta."

   ETULIITE `table.ruudukko` ON PAKOLLINEN, samasta syysta kuin
   tr.laskelma-saannoissa yllä: `table.ruudukko tr:hover > td` on
   tarkempi kuin `tr.valiotsikko > td`, ja ilman etuliitetta hiiri
   pyyhkisi otsikon taustan pois. Sama vika oli aiemmin toisen taulukon
   vareissa 25.8. */
table.ruudukko tr.valiotsikko > td,
table.ruudukko tr.valiotsikko:hover > td {
  background: var(--valiotsikko, #eef1f5);
  border-top: 1px solid #c9d2dd;
  font-weight: 600;
}
table.ruudukko tr.valiotsikko.taso1 > td { background: #e4e9f0; }
table.ruudukko tr.valiotsikko.taso2 > td { background: #eef1f5; }
/* Toisen tason otsikko sisennetaan, jotta kolme tasoa erottuu
   toisistaan ilman etta niita tarvitsee lukea. */
table.ruudukko tr.valiotsikko.taso2 > td:nth-child(4) { padding-left: 22px; }
table.ruudukko tr.valiotsikko > td .himmea { color: #5a6b7d; font-weight: 400; }
table.ruudukko tr.valiotsikko > td .t80 { opacity: .85; }

/* Rivin toimittajamerkki: kenelta hinta on kysytty, nakyvissa riviä
   avaamatta. Asko 25.8.2026: "paalitteroilla etusivunakymassa ei ole
   toimittajan tietoja missaan."

   Vaaleansininen eika vihrea: vihrea tarkoittaa tassa ohjelmassa omaa
   lukua, ja kysytty hinta on eri asia. Kaksi merkitysta samalla varilla olisi juuri se vika
   jota vastaan koko varijarjestelma on. */
.rivitoimittaja {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  border-radius: 9px; font-size: 11px; font-weight: 600;
  background: #dbe7f5; color: #24486e; white-space: nowrap;
  vertical-align: middle;
}
table.ruudukko tr.laskelma > td .rivitoimittaja,
table.ruudukko tr.valiotsikko > td .rivitoimittaja {
  background: rgba(255,255,255,.85); color: #24486e;
}

/* Vaikeuskerroin valiotsikkorivilla. Kapea kentta joka nayttaa tyhjalta
   kunnes siihen kirjoitetaan — tyhja tarkoittaa etta rivit perivat
   arvon laskelmalta, eika sita saa naytella luvulta. */
.ryhmakerroin {
  width: 52px; text-align: center; font-size: 12px; padding: 1px 2px;
  border: 1px solid #c9d2dd; border-radius: 4px; background: var(--tausta);
}
.ryhmakerroin::placeholder { color: #9aa7b4; }/* ================================================ vasen puu-sivupalkki
   Asko 29.8.2026: "siirretään yläpalkissa leijuva sekava painikerivi
   selkeäksi vasemman reunan puu-sivupalkiksi."

   Leveys on 210 px eikä 236: aiempi sarake poistettiin liian leveänä,
   ja navigaatio mahtuu kapeampaan kuin luettelo. */
#sivu {
  flex: 0 0 210px; background: var(--tausta-2);
  border-right: 1px solid var(--viiva); overflow-y: auto;
  padding: 8px 0 14px; user-select: none;
}
#sivu .ryhma { padding: 10px 12px 4px; font-size: 11px; font-weight: 700;
               letter-spacing: .06em; color: var(--teksti-himmea);
               text-transform: uppercase; }
#sivu .kohta {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 12px 7px 20px; border: 0; background: transparent;
  font: inherit; color: var(--teksti); cursor: pointer; text-align: left;
}
#sivu .kohta:hover { background: var(--tausta-3); }
#sivu .kohta.valittu {
  background: var(--oranssi-vaalea); color: var(--oranssi-tumma);
  font-weight: 600; box-shadow: inset 3px 0 0 var(--oranssi);
}
#sivu .kohta .nimi { flex: 1; min-width: 0; overflow: hidden;
                    text-overflow: ellipsis; white-space: nowrap; }
#sivu .kohta .merkki-piste { margin-left: auto; font-size: 11px; }

/* KUVAKE ON HIMMEÄ, TEKSTI EI. Emojissa väri tuli fontista eikä
   totellut mitään; viivakuvake perii `currentColor`:n, joten se
   pitää himmentää erikseen — muuten neljätoista mustaa kuvaketta
   painaa enemmän kuin niiden nimet.

   `flex: none` on pakollinen: ilman sitä pitkä nimi (Automaattilaskenta)
   kutistaa kuvakkeen soikeaksi sen sijaan että nimi katkeaisi. */
#sivu .kohta svg { width: 16px; height: 16px; flex: none;
                   color: var(--teksti-himmea); }
#sivu .kohta:hover svg { color: var(--teksti); }
#sivu .kohta.valittu svg { color: var(--oranssi); }/* Kapealla ruudulla palkki katoaa: navigaatio ei saa viedä puolta
   leveydestä silloin kun taulukko tarvitsee sen. */
@media (max-width: 900px) { #sivu { display: none; } }

/* Korostus muuttujista, ei heksoista: --oranssi-vaalea on paivalla
   #FDF1E5 ja yolla #2A2119, joten sama saanto kelpaa molemmissa. */
.painike.puunappi {
  background: var(--oranssi-vaalea); border-color: transparent;
  color: var(--oranssi-tumma); font-weight: 600;
}
.painike.puunappi:hover { background: var(--oranssi-vaalea); border-color: var(--oranssi); }
html.yo .painike.puunappi { color: var(--oranssi); }

/* ---------------------------------------------------- koko näkymä

   Asko 29.8.2026: "mistä ikkuna aukeaa suuremmaksi niin voisi laittaa
   ison laatikon avaa koko näkymä tms. selkeää."

   Nappi on TEKSTI eikä kuvake. Otsikkorivillä on jo kaksi kuvaketta (⧉
   ja ×), ja kolmas olisi arvattava; juuri arvaamisen välttäminen oli
   pyynnön koko sisältö. Kuvake on tekstin edessä tunnisteena.

   Sääntö on tiedoston lopussa tarkoituksella: `.dialogi.suuri` on yhtä
   tarkka kuin edeltävä korkeussääntö, joten järjestys ratkaisee
   kumpi voittaa korkeuden. */
.dialogi.suuri {
  position: fixed; inset: 0; width: 100vw; height: 100vh;
  max-width: none; max-height: none; border-radius: 0;
}
#peite:has(> .dialogi.suuri) { padding: 0; }
.kokonappi {
  border: 1px solid var(--viiva); background: var(--tausta);
  color: var(--teksti-himmea); border-radius: 6px; cursor: pointer;
  font-size: 12px; padding: 4px 10px; white-space: nowrap;
}
.kokonappi:hover { color: var(--oranssi); border-color: var(--oranssi); }
/* Referaatti on proosaa eikä tarkistettu lauseittain — omat kehykset,
   jotta lukija näkee kummalla puolella tarkistusrajaa hän on. */
.referaatti {
  background: var(--oranssi-vaalea); border-left: 3px solid var(--oranssi);
  border-radius: 0 8px 8px 0; padding: 12px 14px; margin: 8px 0 14px;
  font-size: 14px; line-height: 1.55;
}
.referaatti ul { margin: 0; padding-left: 20px; }
.referaatti li { margin: 1px 0; }
.hylatyt { margin-top: 10px; font-size: 12.5px; }
.hylatyt summary { cursor: pointer; color: var(--teksti-himmea); }
.hylatyt ul { margin: 6px 0 0; padding-left: 20px; }
.hylatyt li { margin: 5px 0; }

/* --------------------------------------------------------- vaihenauha */
.vaihenauha { display: flex; gap: 6px; flex-wrap: wrap; margin: -2px 0 14px; }
.vaihenauha .vaihe {
  display: flex; align-items: center; gap: 7px;
  padding: 5px 12px 5px 8px; border-radius: 999px;
  border: 1px solid var(--viiva); background: var(--tausta-2);
  font-size: 12.5px; color: var(--teksti-himmea);
}
.vaihenauha .vaihe.tehty { border-color: #A8D9C4; background: #E4F5EE; color: #14532D; }
.vaihenauha .vaihe.nyt {
  border-color: var(--oranssi); background: var(--oranssi-vaalea);
  color: var(--teksti); font-weight: 600;
}
@keyframes kiilto { from { background-position: 100% 0; }
                    to   { background-position: -140% 0; } }
.hinnoittelu { margin-top: 18px; }

/* ==================================================================
   ILMEEN KOHOTUS 30.8.2026 — Archdesk-suunta

   Asko hyväksyi mallin sanoin "on parempi kuin meidän". Malli on
   ~/Desktop/remppalaskuri-ilme/ilme-malli.html, tausta 30 kilpailijan
   pisteytys ~/Desktop/Remppalaskuri-ilmevertailu.pdf (Archdesk 91/100,
   Remppalaskuri 59/100).

   MITÄ EI MUUTETA: pääryhmärivin oranssi täyttö (tr.laskelma, ylempänä
   tässä tiedostossa) ja rakennerivin sininen. Molemmat ovat Askon omia
   linjauksia, ja hän valitsi ne uudestaan 30.8. kun vaihtoehdot
   näytettiin vierekkäin.

   OMAT MUUTTUJAT UUSILLE OSILLE. --oranssi on rivien TÄYTTÖväri, ei
   korostusväri; sen kierrättäminen täytti kerran koko taulukon. Uudet
   osat käyttävät omia muuttujiaan, jolloin kumpikaan ei liikuta
   toista.
   ================================================================== */

:root {
  --korostus-08: rgba(232,115,12,.08);   /* valittu kohta sivupalkissa */
  --korostus-14: rgba(232,115,12,.14);   /* hehku ja kevyt rajaus */
  --lampo:       232,115,12;             /* Yhteensä-sarakkeen sävy, rgb */
}

/* ---------------------------------------------- 1. sarakeotsikot oikealle

   Sama vika kuin table.kokohanke:ssa 23.8.2026, samassa taulukossa eri
   paikassa: "table.ruudukko th" (0,1,1) voitti .oik:n (0,1,0), joten
   Materiaalit, Hankinnat, Työt, Tunnit, Yhteensä ja €/yks. olivat
   vasemmassa reunassa ja niiden luvut oikeassa. Silmä ei saa
   sarakkeesta otetta kun otsikko ja luku ovat eri reunoissa. */
table.ruudukko th.oik { text-align: right; }
table.ruudukko th.kesk { text-align: center; }

/* ------------------------------------------- 2. ratas näkyviin hiiren alle

   Ratas on joka rivillä, ja 103 rivin listassa se on pystysuora
   kohinasarake joka kilpailee lukujen kanssa. Archdesk ja Buildern
   piilottavat rivin toiminnot; Buildxact ei, ja ero näkyy.

   KOSKETUSNÄYTÖLLÄ EI OLE HIIRTÄ. Ilman hover-vartiota ratas katoaisi
   puhelimesta kokonaan eikä rivin valikkoon pääsisi mitenkään.
   Näppäimistökäyttö säilyy :focus-within-ehdon kautta. */
@media (hover: hover) and (pointer: fine) {
  table.ruudukko tbody .ratas { opacity: 0; transition: opacity .1s; }
  table.ruudukko tbody tr:hover .ratas,
  table.ruudukko tbody tr:focus-within .ratas,
  table.ruudukko tbody .ratas:focus-visible { opacity: 1; }
}

/* ------------------------------------------------ 3. lähdemerkki pilleriksi

   Muoto muuttuu, väri ei: .merkki.H on vihreä ja .merkki.A keltainen
   kuten ennenkin. Pyöreä reunus
   erottaa merkin luvusta nopeammin kuin suorakulmio, koska taulukossa
   ei ole muita pyöreitä muotoja. */
.merkki { border-radius: 10px; padding: 1px 7px; }
.merkki.AI, .merkki.OMA { border-radius: 11px; }

/* --------------------------------------------------- 4. lämpösävy summaan

   Yhteensä-sarakkeen tausta on samaa oranssia, läpinäkyvyys suhteessa
   rivin osuuteen suurimmasta. Suurin erä löytyy silmäilemällä eikä
   lukemalla. Ei uutta väriä — --lampo on sama sävy kuin --oranssi,
   omana muuttujanaan jotta täyttöväriä voi muuttaa ilman että sävy
   seuraa perässä.

   Katto on 14 %: sitä tummempi tausta alkaa syödä luvun kontrastia,
   ja luku on tässä tärkeämpi kuin sävy. JS asettaa --o riviä
   piirrettäessä; ilman sitä sävyä ei ole eikä mikään hajoa. */
table.ruudukko td.lampo { position: relative; }
table.ruudukko td.lampo::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: rgb(var(--lampo)); opacity: var(--o, 0);
}
table.ruudukko td.lampo > * { position: relative; }
/* Pääryhmärivillä tausta on jo oranssi. Toinen sävy sen päällä ei
   kertoisi mitään ja tummentaisi valkoisen tekstin alta. */
table.ruudukko tr.laskelma > td.lampo::before { display: none; }

/* Kirjaston hakupalkin laatikot saman levyisiksi. Asko 31.8.2026:
   "kaikki yhtä leveitä, saman levyisiä kuin Kaikki pääryhmät laatikko."
   Mitattu ennen: 273, 815 ja 138 px — hakukenttä venyi koko jäljelle
   jääneen tilan yli, jolloin rivi näytti sattumanvaraiselta.

   Kiinteä leveys eikä `flex: 1`: yhtä leveä tarkoittaa yhtä leveää myös
   silloin kun laatikoita on kaksi (Materiaalit) tai neljä (Tyhjennä
   mukana). Tyhjennä-nappi jää omaan kokoonsa — se on toiminto, ei
   suodatin. */
.hakupalkki.tasaleveat { flex-wrap: nowrap; }
/* YHTA LEVEAT JA YHDELLE RIVILLE. Kiinteä leveys teki ensin juuri sen
   mitä sen piti estää: kirjastonäkymässä (1 242 px) kolme laatikkoa
   mahtui, mutta Lisää rakenteita -ikkunassa hakupalkki on 728 px ja
   neljä 273 pikselin laatikkoa vaatii 1 116 px — ne kiertyivät kahdelle
   riville. Mitattu 31.8.2026: Littera ja pääryhmä y=82, haku ja
   omistaja y=125.

   `flex: 1 1 0` antaa jokaiselle saman osuuden rivistä riippumatta
   siitä montako niitä on ja kuinka leveä palkki on. Se on "yhtä
   leveitä" myös silloin kun ikkunan koko muuttuu — kiinteä pikseliluku
   on sitä vain yhdessä leveydessä. */
.hakupalkki.tasaleveat > select,
.hakupalkki.tasaleveat > input { flex: 1 1 0; min-width: 0; width: auto; }
.hakupalkki.tasaleveat > button { flex: 0 0 auto; }

/* PUOLIKKAAT: yhtä leveät mutta puolet siitä minkä ne täyttäisivät.
   Asko 31.8.2026: "laatikot 1/2 osaa pienemmät, ne ei tarvitse olla
   päästä päähän." Rivi ei ole mitta-asteikko: suodatin ei parane siitä
   että se venyy ruudun reunaan, ja kolme kapeaa laatikkoa vasemmalla
   luetaan yhdellä silmäyksellä.

   `flex-grow: 0` estää venymisen ja `flex-basis` antaa puolet siitä
   osuudesta jonka ne muuten saisivat — laskettuna palkin leveydestä,
   jotta suhde säilyy ikkunan koon muuttuessa. */
.hakupalkki.tasaleveat.puolikkaat > select,
.hakupalkki.tasaleveat.puolikkaat > input {
  flex: 0 1 calc((100% - 16px) / 6);
}

/* ==================================================================
   ILMEEN UUDISTUS — työpöytä eikä taulukko joka on levitetty ruutuun

   Tavoite on rauhoittaa pitkä laskenta ilman että yksikään luku, sarake
   tai toiminto häviää. Oranssi pysyy ensisijaisena toimintavärinä,
   valkoinen työpintana ja vihreä/punainen vain taloudellisina merkityksinä.
   ================================================================== */
:root {
  --viiva: #DCE2E8;
  --viiva-tumma: #B9C3CF;
  --tausta-2: #F6F8FA;
  --tausta-3: #EEF2F6;
  --varjo: 0 1px 2px rgba(20, 28, 40, .05), 0 10px 28px rgba(20, 28, 40, .07);
  --yla: 60px;
  --ala: 32px;
}

html.yo {
  --viiva: #323B46;
  --viiva-tumma: #465260;
  --tausta-2: #151A20;
  --tausta-3: #222A34;
}

/* Tausta erottaa työalueen navigaatiosta ja korteista. Hyvin vaalea
   lämpö estää valkoisten taulukoiden sulautumisen yhdeksi pinnaksi. */
html, body { background: #F3F5F8; }
html.yo, html.yo body { background: #11161C; }
#sisalto {
  background: linear-gradient(135deg, #FBFCFD 0%, #F5F7FA 100%);
  padding-top: 12px;
}
html.yo #sisalto { background: linear-gradient(135deg, #1A2028, #12171D); }
#sisalto > .padded { max-width: 1760px; margin-left: auto; margin-right: auto; }

/* Yläpalkki toimii nyt tuotteen otsikkona: projekti on ensimmäinen asia,
   ei lista irrallisia painikkeita. */
#yla { padding: 0 20px; gap: 12px; box-shadow: 0 1px 0 rgba(20,28,40,.04); }
#yla .nimi { font-size: 17px; font-weight: 700; letter-spacing: -.015em; }
#yla .nimi small { margin-top: 1px; }
.ypainike { min-height: 36px; border-radius: 9px; padding: 7px 12px; }

/* Navigaatio on oma, hillitty työkalupintansa. Valittu kohta saa pillin,
   ei vain kapeaa viivaa ruudun reunaan. */
#sivu { background: rgba(255,255,255,.78); padding: 14px 10px 18px; }
html.yo #sivu { background: #12171D; }
#sivu .ryhma { padding: 16px 12px 6px; font-size: 10px; }
#sivu .kohta { border-radius: 9px; margin: 2px 0; padding: 9px 10px; }
#sivu .kohta:hover { background: var(--tausta-3); }
#sivu .kohta.valittu {
  background: var(--oranssi-vaalea); box-shadow: none; color: var(--oranssi-tumma);
}
#sivu .kohta.valittu::before {
  content: ''; width: 3px; height: 18px; margin-left: -10px;
  border-radius: 0 3px 3px 0; background: var(--oranssi);
}

/* Sivun otsikko, toimintopainikkeet ja kentät jakavat yhden pehmeän
   mittakaavan. Tämä vaikuttaa kaikkiin näkymiin. */
.sivuotsikko {
  top: 0; min-height: 66px; padding: 14px 22px; gap: 10px;
  border: 1px solid var(--viiva); border-radius: 14px; box-shadow: var(--varjo);
}
#sisalto > .sivuotsikko {
  margin-left: 0; margin-right: 0; padding-left: 22px; padding-right: 22px;
}
.sivuotsikko h1 { font-size: 20px; letter-spacing: -.02em; }
.sivuotsikko .kuvaus { font-size: 13px; }
.painike {
  min-height: 34px; border-radius: 8px; padding: 6px 12px;
  transition: transform .12s ease, background .12s ease, border-color .12s ease,
              box-shadow .12s ease;
}
.painike:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 3px 8px rgba(20,28,40,.08); }
.painike.vahva { box-shadow: 0 3px 9px rgba(232,115,12,.20); }
input[type=text], input[type=number], select, textarea { border-color: var(--viiva); border-radius: 8px; }

/* Kortit ovat nyt selkeitä päätöspintoja. Taulukkonumerot jäävät tiiviiksi,
   mutta yhteenveto ja lomakkeet saavat enemmän hengitystilaa. */
.kortti,
.mittari {
  border-color: var(--viiva); border-radius: 14px; box-shadow: var(--varjo);
}
.kortti { padding: 16px; }
.mittari { padding: 16px 18px 15px; }
.mittari .arvo { font-size: 25px; letter-spacing: -.025em; }
.mittarit { gap: 14px; }

/* Taulukko pysyy laskentatyökaluna, mutta ruudukko ei saa näyttää
   ruutupaperilta. Pystyviivat ovat nyt vain kevyt kohdistusapu. */
table.ruudukko th { background: var(--tausta-2); padding: 9px 10px; }
table.ruudukko td { padding: 6px 10px; border-bottom-color: var(--viiva); }
table.ruudukko th + th, table.ruudukko td + td { border-left-color: rgba(185,195,207,.45); }
table.ruudukko tr:hover > td { background: rgba(232,115,12,.035); }
table.ruudukko tr.valittu > td { background: var(--oranssi-vaalea); }
table.puu td { padding-top: 5px; padding-bottom: 5px; }
.mtilamerkki { min-height: 28px; border-radius: 999px; padding: 4px 10px; }

/* Pienellä näytöllä otsikko saa rivittyä eikä painikkeita typistetä
   kuvakkeiksi: toiminnon nimi on tärkeämpi kuin muutama pikseli. */
@media (max-width: 900px) {
  :root { --yla: 54px; }
  #sisalto { padding-top: 8px; }
  .sivuotsikko, #sisalto > .sivuotsikko {
    position: static; min-height: 0; padding: 14px; border-radius: 12px;
    align-items: center; flex-wrap: wrap;
  }
  .sivuotsikko h1 { font-size: 18px; }
  .sivuotsikko .kuvaus { flex-basis: 100%; margin-right: 0; order: 3; }
  .mittari .arvo { font-size: 22px; }
}

/* ------------------------------------------------------ väri-identiteetti
   Ilmeelle oma tunniste ilman uutta "värimaailmaa":
   oranssi = tee päätös, sininen = tieto, keltainen =
   odottaa päätöstä, vihreä = valmis. Väri ei koskaan ole ainoa merkki —
   jokaisella on aina myös luettava teksti. */
:root {
  --sininen-info: #2563B8;
  --sininen-vaalea: #EAF2FF;
  --keltainen-vaalea: #FFF5DE;
  --vihrea-vaalea: #EAF7F1;
}

/* Hyvin kevyt tekninen ruudukko tekee taustasta työkalun, ei markkinointia.
   Se näkyy vain tyhjissä väleissä ja katoaa täysin taulukkojen ja korttien
   alle, joten luvut säilyvät aina ensisijaisina. */
#sisalto {
  background-color: #F5F8FC;
  background-image:
    radial-gradient(circle at 92% 3%, rgba(232,115,12,.12), transparent 25rem),
    radial-gradient(circle at 10% 0%, rgba(37,99,184,.075), transparent 23rem),
    linear-gradient(rgba(130,150,175,.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(130,150,175,.07) 1px, transparent 1px);
  background-size: auto, auto, 24px 24px, 24px 24px;
}
html.yo #sisalto {
  background-color: #141B23;
  background-image:
    radial-gradient(circle at 92% 3%, rgba(242,132,30,.13), transparent 24rem),
    linear-gradient(rgba(130,150,175,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(130,150,175,.045) 1px, transparent 1px);
  background-size: auto, 24px 24px, 24px 24px;
}

/* Otsikko on sovelluksen oma "komentokeskus": valo ja väriviiva kertovat
   missä näkymässä ollaan, mutta painikkeet ja teksti jäävät täysin luettaviksi. */
.sivuotsikko {
  isolation: isolate; overflow: hidden;
  background-color: rgba(255,255,255,.92);
}
.sivuotsikko::before {
  content: ''; position: absolute; inset: 0 auto 0 0; width: 5px;
  background: linear-gradient(180deg, var(--oranssi), #F3A52B 55%, var(--sininen-info));
}
.sivuotsikko::after {
  content: ''; position: absolute; width: 220px; height: 220px; right: -108px; top: -155px;
  border-radius: 50%; background: radial-gradient(circle, rgba(232,115,12,.16), transparent 68%);
  pointer-events: none; z-index: 0;
}
html.yo .sivuotsikko { background-color: rgba(26,30,36,.94); }
.sivuotsikko > * { position: relative; z-index: 1; }

#yla .ypainike.vahva, .painike.vahva {
  background-image: linear-gradient(135deg, #F78324, var(--oranssi));
}

/* Pienet tietotunnisteet: sininen viittaa aina tietoon. */
.merkki.AI { background: var(--sininen-vaalea); color: var(--sininen-info); }
.t80 { background: #EDF4FC; color: #53708F; }
.rivitoimittaja { background: var(--sininen-vaalea); border-color: #BDD2EE; color: #215887; }

/* Aktiivinen sivu saa hienovaraisen lämpimän hehkun. Se ei ole uusi tila,
   vaan pelkkä orientaatiovihje pitkän työpäivän aikana. */
#sivu .kohta.valittu { box-shadow: 0 4px 12px rgba(232,115,12,.10); }
#sivu .kohta.valittu svg { color: var(--oranssi-tumma); }

/* -------------------------------------------------- laskennan rauhoitus
   Laskenta on työpöytä, ei markkinointinäkymä. Taustaruudukko ja kokonaan
   oranssit pääryhmät veivät huomion juuri niiltä luvuilta joita käyttäjä
   vertailee. Oranssi jää toimintaan ja valintaan; ryhmän erottaa nyt
   vasemman reunan merkki, paino ja hillitty oma pinta. */
#sisalto,
html.yo #sisalto {
  background-image: none;
}

table.ruudukko tr.laskelma > td,
table.ruudukko tr.laskelma:hover > td {
  background: var(--tausta-2);
  color: var(--teksti);
  border-top-color: var(--viiva-tumma);
  border-bottom-color: var(--viiva);
}
/* Vanha yötilan yleinen hover on tarkempi html-etuliitteensä vuoksi.
   Tämä rajattu poikkeus varmistaa, ettei se maalaa pääryhmän omaa tilaa
   yli hiiren tullessa riville. */
table.ruudukko tr.laskelma:hover > td { background: var(--tausta-3) !important; }
table.ruudukko tr.laskelma > td:first-child {
  box-shadow: inset 4px 0 0 var(--oranssi);
}
table.ruudukko tr.laskelma.valittu > td {
  background: var(--oranssi-vaalea);
  color: var(--teksti);
}
/* Valittu meripihkarivi säilyttää tilansa myös hiiren alla. Hover saa
   kertoa että rivi on painettava, ei hävittää käyttäjän valitsemaa väriä. */
table.ruudukko tr.laskelma.valittu:hover > td {
  background: var(--oranssi-vaalea) !important;
}
table.ruudukko tr.laskelma > td .ratas,
table.ruudukko tr.laskelma > td .nuoli,
table.ruudukko tr.laskelma > td .nuoli:hover,
table.ruudukko tr.laskelma > td .nuoli.avaus:hover,
table.ruudukko tr.laskelma > td .jno { color: var(--teksti); }
table.ruudukko tr.laskelma > td .himmea { color: var(--teksti-himmea); }
table.ruudukko tr.laskelma > td .jno { border-color: var(--viiva-tumma); }
table.ruudukko tr.laskelma > td .ratas:hover {
  background: var(--oranssi-vaalea); color: var(--oranssi-tumma);
}
table.ruudukko tr.laskelma > td .solu:hover {
  background: var(--tausta); border-color: var(--viiva-tumma); color: var(--teksti);
}
table.ruudukko tr.laskelma > td .solu:focus {
  background: var(--tausta); color: var(--teksti); border-color: var(--oranssi);
  box-shadow: 0 0 0 3px rgba(232,115,12,.15);
}
table.ruudukko tr.laskelma > td .nuoli.avaus::before,
table.ruudukko tr.laskelma > td .nuoli.avaus:hover::before { border-left-color: var(--teksti); }
table.ruudukko tr.laskelma > td .nuoli.avaus.auki::before,
table.ruudukko tr.laskelma > td .nuoli.avaus.auki:hover::before {
  border-left-color: transparent; border-top-color: var(--teksti);
}

/* YÖTILA — rauhallinen laskentapöytä, ei väriefektien kokoelma.
   Korostusväri kertoo vain toiminnon tai valinnan. Hover vaihtaa ainoastaan
   pintaa hyvin vähän, jotta hiiren liikuttelu ei saa taulukkoa välkkymään. */
html.yo {
  --oranssi: #C48750;
  --oranssi-tumma: #D6A36F;
  --oranssi-vaalea: #30271F;
  --tausta: #2B323A;
  --tausta-2: #242A31;
  --tausta-3: #373F49;
  --viiva: #4C5662;
  --viiva-tumma: #65717E;
  --teksti: #E4E7EA;
  --teksti-himmea: #A1A8B0;
  --sininen: #93A9C4;
  --vihrea: #85B6A0;
  --punainen: #CD8D87;
  --keltainen: #C5AA78;
  --sininen-info: #9BAFCA;
  --sininen-vaalea: #202A35;
  --keltainen-vaalea: #2A271E;
  --vihrea-vaalea: #1F2B27;
  --varjo: 0 1px 0 rgba(255,255,255,.035), 0 10px 24px rgba(0,0,0,.18);
}

html.yo .sivuotsikko::before { background: var(--oranssi); }
html.yo .sivuotsikko::after { display: none; }
html.yo,
html.yo body { background: var(--tausta-2); }
html.yo #sisalto { background-color: var(--tausta-2); }
html.yo #sivu { background: #292F37; }
html.yo .sivuotsikko { background-color: #2B323A; }
html.yo #yla .ypainike.vahva,
html.yo .painike.vahva { background-image: none; background-color: var(--oranssi); }
html.yo #sivu .kohta.valittu { box-shadow: none; }
html.yo .aitahti { filter: saturate(.58) brightness(.86); }
html.yo table.ruudukko th + th,
html.yo table.ruudukko td + td { border-left-color: rgba(255,255,255,.065); }
html.yo table.ruudukko tr:hover > td,
html.yo tr.lataus td { background: #3D4651; }
html.yo .t80 { background: #3D4651; color: #D0D7DF; }

/* Varoitus- ja tilalaatikot olivat päivätilan vaaleita pintoja, mutta
   perivät yötilassa vaalean tekstin. Siksi sisältö katosi juuri siellä
   missä käyttäjän pitäisi lukea rajaus ja haun ulkopuolelle jääneet rivit. */
html.yo .hvaroitus,
html.yo .aipuuttuvat,
html.yo .puuttuvat { background: #302C23; color: #E8DFC9; }
html.yo .puuttuu { background: #302C23; color: var(--keltainen); }
html.yo .aitulos { background: #22332B; color: #D9E9E0; }

/* Pilkonnan jälkeiset panokset erotetaan kustannuslajin mukaan, mutta
   vain vasemmalla merkkinä ja lajin omassa valitsimessa. Koko rivin
   värjääminen tekisi kymmenestä panoksesta jälleen levottoman raidaston. */
:root {
  --panos-materiaali-tausta: #EAF2FF;
  --panos-materiaali-teksti: #215887;
  --panos-tyo-tausta: #FFF2E8;
  --panos-tyo-teksti: #99541D;
  --panos-hankinta-tausta: #EAF7F1;
  --panos-hankinta-teksti: #236548;
}
table.ruudukko tr.panos.laji-materiaali > td:first-child { box-shadow: inset 3px 0 0 var(--sininen); }
table.ruudukko tr.panos.laji-tyo > td:first-child { box-shadow: inset 3px 0 0 var(--oranssi); }
table.ruudukko tr.panos.laji-hankinta > td:first-child { box-shadow: inset 3px 0 0 var(--vihrea); }
tr.panos .lajivalinta.laji-materiaali { background: var(--panos-materiaali-tausta); color: var(--panos-materiaali-teksti); }
tr.panos .lajivalinta.laji-tyo { background: var(--panos-tyo-tausta); color: var(--panos-tyo-teksti); }
tr.panos .lajivalinta.laji-hankinta { background: var(--panos-hankinta-tausta); color: var(--panos-hankinta-teksti); }
html.yo {
  --panos-materiaali-tausta: #253648;
  --panos-materiaali-teksti: #B7D0EB;
  --panos-tyo-tausta: #3A3026;
  --panos-tyo-teksti: #E0BA94;
  --panos-hankinta-tausta: #29382F;
  --panos-hankinta-teksti: #B7D6C2;
}
/* Poiminta tehdään usein kaukana listan yläreunasta. Valittu kori kelluu
   siksi oikealla eikä katoa sen vuoksi, että käyttäjä katsoo seuraavaa
   materiaalia tai työvaihetta. */

/* ------------------------------------------- tarjoussumman muodostuminen

   Asko 3.9.2026 kuvan kanssa: "vaikee lukuinen, laitetaan paljon
   suppeemmaksi."

   Taulukko peri `table.ruudukko`:n `width: 100%`, joten se venyi koko
   ruudun leveydelle. Nimike oli vasemmassa laidassa ja euro oikeassa,
   ja niiden valissa oli tuhat pikselia tyhjaa — silma ei yhdista rivia
   ja lukua sellaisen matkan yli. Kolme saraketta ei tarvitse enempaa
   tilaa kuin sisaltonsa.

   Rivit ovat myos matalammat: ketju luetaan yhtena kokonaisuutena
   ylhaalta alas, ei rivi kerrallaan kuten laskentataulukko. */
table.ruudukko.ketju {
  width: auto; min-width: 460px; max-width: 620px; font-size: 13px;
}
table.ruudukko.ketju td { padding: 3px 10px; }
table.ruudukko.ketju .saadinsolu { width: 104px; white-space: nowrap; }
table.ruudukko.ketju input.prosenttisolu { width: 52px; }
/* Nollalla oleva varaus ei kilpaile huomiosta niiden rivien kanssa
   joissa on rahaa. Kentta pysyy taysin kaytettavana. */
table.ruudukko.ketju tr.nolla td { color: var(--teksti-himmea); }
table.ruudukko.ketju tr.nolla td b { font-weight: 400; }
/* Yhteenveto luetaan ylhaalta alas, joten sen leveys on tekstipalstan
   leveys eika ikkunan. Kortit rivittyvat kahteen jonoon kapeammalla. */
/* Valitsimessa on #sisalto mukana, koska rivin 4188 sailio-saanto
   `#sisalto > .padded` voittaisi yhden luokan. */
#sisalto > .padded.yhteenvetosivu { max-width: 1040px; }

/* TYOMAAN TILA laskennat-taulukossa: laskennassa/tarjottu/ohi/kiinni.
   Tuotu tyopoytaversiosta 11.9.2026 sellaisenaan — sama tieto ei saa
   nayttaa kahdessa ohjelmassa erilaiselta. Vari ei kanna merkitysta
   yksin: teksti kertoo vaiheen, vari tekee sen loydettavaksi pitkalta
   listalta. */
.mtilamerkki.laskennassa {
  border-color: var(--viiva); color: var(--teksti-himmea); background: var(--tausta);
}
.mtilamerkki.tarjottu {
  border-color: #E3C58A; color: #8A6114; background: #FDF8EE;
}
.mtilamerkki.ohi {
  border-color: #E3A6A0; color: var(--punainen); background: #FCEEED;
}
.mtilamerkki.kiinni {
  border-color: var(--vihrea); color: var(--vihrea); background: #F1FAF6;
}
.mtilamerkki.kiinni::before { content: '\2713 '; }
button.mtilamerkki.tila { cursor: pointer; }
button.mtilamerkki.tila:hover { filter: brightness(.97); }

/* Tilasuodatin laskentalistan ylla. Valittu erottuu taytolla, muut ovat
   reunuksellisia — sama malli kuin ohjelman muissa valinnoissa. */
.tilasuodatin { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.tilasuodatin button {
  padding: 5px 11px; border-radius: 999px; font-size: 12px; font-weight: 600;
  border: 1px solid var(--viiva); background: var(--tausta);
  color: var(--teksti-himmea); cursor: pointer;
}
.tilasuodatin button.valittu {
  border-color: var(--oranssi); background: var(--oranssi); color: #fff;
}

/* TYOTILAN YRITYS ylapalkissa. Ei painike vaan merkki: se kertoo missa
   ollaan, eika sita klikkaamalla tapahdu mitaan. */
#yla .yritysmerkki {
  font-size: 12px; font-weight: 600; color: var(--oranssi);
  border: 1px solid var(--oranssi); border-radius: 999px;
  padding: 3px 10px; white-space: nowrap; margin-right: auto;
}
@media (max-width: 900px) { #yla .yritysmerkki { display: none; } }

/* ======================================================= KEMIQR-KEHYS
   Kustannuslaskin KemiQR:n työmaan alla (luku 785, 19.–20.9.2026). Asko:
   *"tämä upotetaan kemiqr sivuun heti aluesuunnitelman alle"*, *"muutenkin
   tärkeää että säilytetään kemiqr ilme"*, *"kaikki otsakkeet pitää
   muistaa muuttaa"*, *"yhteenveto ilmeessä numerot ei katkea laatikoissa"*,
   *"muutosta nuoliin mistä kumotaan ja mennään eteen"*.

   KAIKKI SÄÄNNÖT OVAT html.kemiqr:n ALLA. Luokan asettaa kaynnistys.js vain
   kun osoitteessa on ?upotettu=1; ilman sitä laskin näyttää ja toimii
   täsmälleen kuten ennen, myös laskuri.sttsaneeraus.fi. Tumma teema on
   html.kemiqr.yo (portaalin oletus), vaalea html.kemiqr ilman yo-luokkaa.

   Tokenit ovat KemiQR Pro-ilmeen design systemistä (tokens.json 17.9.2026):
   pinta/korotettu/reuna/teksti/himmea molemmissa teemoissa, oranssi #F47920
   ja sen päällä AINA tumma teksti (#141A22, 6,3:1) - valkoinen oranssilla
   on 2,8:1 eikä kelpaa. Merkitysvärit kartoittuvat KemiQR:n tilaväreihin:
   kirjastosta = tieto (sininen), oma = hyvä (vihreä), arvio = vaara
   (punainen), epätäydellinen = varo (keltainen), lukittu = himmeä. */
html:is(.kemiqr,.kemiqr-ilme) {
  --oranssi:        #f47920;
  --oranssi-tumma:  #c95f0a;
  --oranssi-vaalea: #ffe9db;
  --teksti:         #141a22;
  --teksti-himmea:  #5a6472;
  --viiva:          #e3ddd1;
  --viiva-tumma:    #cfc6b6;
  --tausta:         #ffffff;
  --tausta-2:       #f5f2ec;
  --tausta-3:       #ede8df;
  --sininen:        #1c3d5a;
  --sininen-rivi:   #e4edf5;
  --sininen-rivi-2: #d6e3f0;
  --vihrea:         #17704a;
  --vihrea-oma:     #dff3e8;
  --vihrea-oma-2:   #cdeadb;
  --punainen:       #b02e24;
  --keltainen:      #8a6300;
  --varjo:          0 1px 2px rgba(20,26,34,.05), 0 12px 32px -18px rgba(20,26,34,.22);
  --kemiqr-pinta:          #ffffff;
  --kemiqr-korotettu:      #ede8df;
  --kemiqr-reuna:          #e3ddd1;
  --kemiqr-reunaL:         #cfc6b6;
  --kemiqr-sivu:           #f5f2ec;
  --kemiqr-oranssi-teksti: #ad4e08;
  --kemiqr-oranssi-paalla: #141a22;
  --kemiqr-oranssi-pehmea: #ffe9db;
  --kemiqr-varo-tausta:    #fff3cc;
  --kemiqr-varo-teksti:    #8a6300;
  --kemiqr-vaara-tausta:   #fbe3e0;
  --kemiqr-vaara-teksti:   #b02e24;
  --kemiqr-hyva-tausta:    #dff3e8;
  --kemiqr-hyva-teksti:    #17704a;
  --kemiqr-tieto-tausta:   #e4edf5;
  --kemiqr-tieto-teksti:   #1c3d5a;
  --kemiqr-sans:    Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
  --kemiqr-otsikko: Sora, Inter, ui-sans-serif, system-ui, sans-serif;
  color-scheme: light;
}
html:is(.kemiqr,.kemiqr-ilme).yo {
  --oranssi:        #f47920;
  --oranssi-tumma:  #c95f0a;
  --oranssi-vaalea: rgba(244,121,32,.14);
  --teksti:         #edeae4;
  --teksti-himmea:  #a7adb8;
  --viiva:          #2a313b;
  --viiva-tumma:    #3a424e;
  --tausta:         #171c24;
  --tausta-2:       #10141a;
  --tausta-3:       #1e242d;
  --sininen:        #93c5fd;
  --sininen-rivi:   rgba(33,150,243,.14);
  --sininen-rivi-2: rgba(33,150,243,.22);
  --vihrea:         #6ee7a8;
  --vihrea-oma:     rgba(76,175,80,.14);
  --vihrea-oma-2:   rgba(76,175,80,.22);
  --punainen:       #fca5a5;
  --keltainen:      #fcd34d;
  --varjo:          0 1px 2px rgba(0,0,0,.35), 0 12px 32px -18px rgba(0,0,0,.6);
  --kemiqr-pinta:          #171c24;
  --kemiqr-korotettu:      #1e242d;
  --kemiqr-reuna:          #2a313b;
  --kemiqr-reunaL:         #3a424e;
  --kemiqr-sivu:           #10141a;
  --kemiqr-oranssi-teksti: #f47920;
  --kemiqr-oranssi-pehmea: rgba(244,121,32,.14);
  --kemiqr-varo-tausta:    rgba(255,193,7,.14);
  --kemiqr-varo-teksti:    #fcd34d;
  --kemiqr-vaara-tausta:   rgba(244,67,54,.14);
  --kemiqr-vaara-teksti:   #fca5a5;
  --kemiqr-hyva-tausta:    rgba(76,175,80,.14);
  --kemiqr-hyva-teksti:    #6ee7a8;
  --kemiqr-tieto-tausta:   rgba(33,150,243,.14);
  --kemiqr-tieto-teksti:   #93c5fd;
  color-scheme: dark;
}

/* KEMIQR:N VALMISTEEMAT LASKIMESSA (Asko 26.9.2026: "sivulla pitää myös
   toimia teemavaihtovärit"). Portaali kertoo teeman nimen (?vari= ja
   viesti {vari}), kaynnistys.js kirjoittaa sen data-kemiqr-teemaan. Arvot
   ovat portaalin globals.css:stä: portaalin tausta = laskimen tausta-2 ja
   sivu, pinta = tausta ja kemiqr-pinta, korotettu = tausta-3. Vaalea vai
   tumma pohja tulee edelleen .yo-luokasta (?teema=), joten tilavärit
   ovat oikean pohjan mukaiset. Päivä ja Ilta ovat laskimen perusarvot. */
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="kalkki"] {
  --tausta-2: #F4F5F7; --kemiqr-sivu: #F4F5F7;
  --tausta: #FFFFFF; --kemiqr-pinta: #FFFFFF;
  --tausta-3: #EBEDF0; --kemiqr-korotettu: #EBEDF0;
  --viiva: #DDE0E5; --kemiqr-reuna: #DDE0E5;
  --viiva-tumma: #C5CAD2; --kemiqr-reunaL: #C5CAD2;
  --teksti: #141A22; --teksti-himmea: #555E6B;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="hiekka"] {
  --tausta-2: #EFE6D6; --kemiqr-sivu: #EFE6D6;
  --tausta: #FBF7EF; --kemiqr-pinta: #FBF7EF;
  --tausta-3: #E6DAC4; --kemiqr-korotettu: #E6DAC4;
  --viiva: #D9CAAE; --kemiqr-reuna: #D9CAAE;
  --viiva-tumma: #C4B08E; --kemiqr-reunaL: #C4B08E;
  --teksti: #2A2217; --teksti-himmea: #5C5144;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="taivas"] {
  --tausta-2: #EAF1F8; --kemiqr-sivu: #EAF1F8;
  --tausta: #FFFFFF; --kemiqr-pinta: #FFFFFF;
  --tausta-3: #DDE8F3; --kemiqr-korotettu: #DDE8F3;
  --viiva: #CCDBEA; --kemiqr-reuna: #CCDBEA;
  --viiva-tumma: #AFC4DA; --kemiqr-reunaL: #AFC4DA;
  --teksti: #0F1C2E; --teksti-himmea: #4C5E75;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="salvia"] {
  --tausta-2: #EDF1EC; --kemiqr-sivu: #EDF1EC;
  --tausta: #FFFFFF; --kemiqr-pinta: #FFFFFF;
  --tausta-3: #E0E7DF; --kemiqr-korotettu: #E0E7DF;
  --viiva: #D0DACE; --kemiqr-reuna: #D0DACE;
  --viiva-tumma: #B3C2B0; --kemiqr-reunaL: #B3C2B0;
  --teksti: #18221A; --teksti-himmea: #52604F;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="betoni"] {
  --tausta-2: #E4E4E1; --kemiqr-sivu: #E4E4E1;
  --tausta: #F4F4F2; --kemiqr-pinta: #F4F4F2;
  --tausta-3: #D9D9D5; --kemiqr-korotettu: #D9D9D5;
  --viiva: #CACAC5; --kemiqr-reuna: #CACAC5;
  --viiva-tumma: #AEAEA8; --kemiqr-reunaL: #AEAEA8;
  --teksti: #1B1B1A; --teksti-himmea: #52524F;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="grafiitti"] {
  --tausta-2: #141414; --kemiqr-sivu: #141414;
  --tausta: #1C1C1C; --kemiqr-pinta: #1C1C1C;
  --tausta-3: #252525; --kemiqr-korotettu: #252525;
  --viiva: #323232; --kemiqr-reuna: #323232;
  --viiva-tumma: #444444; --kemiqr-reunaL: #444444;
  --teksti: #ECECEC; --teksti-himmea: #ABABAB;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="laivasto"] {
  --tausta-2: #0B1628; --kemiqr-sivu: #0B1628;
  --tausta: #12203A; --kemiqr-pinta: #12203A;
  --tausta-3: #1A2B4A; --kemiqr-korotettu: #1A2B4A;
  --viiva: #243759; --kemiqr-reuna: #243759;
  --viiva-tumma: #32476D; --kemiqr-reunaL: #32476D;
  --teksti: #EAEFF7; --teksti-himmea: #A5B3C9;
}
html:is(.kemiqr,.kemiqr-ilme)[data-kemiqr-teema="tyomaa"] {
  --tausta-2: #000000; --kemiqr-sivu: #000000;
  --tausta: #0F0F0F; --kemiqr-pinta: #0F0F0F;
  --tausta-3: #1C1C1C; --kemiqr-korotettu: #1C1C1C;
  --viiva: #3A3A3A; --kemiqr-reuna: #3A3A3A;
  --viiva-tumma: #5A5A5A; --kemiqr-reunaL: #5A5A5A;
  --teksti: #FFFFFF; --teksti-himmea: #D6D6D6;
  --oranssi: #FFD400; --oranssi-tumma: #E6BE00; --oranssi-vaalea: rgba(255,212,0,.16);
  --kemiqr-oranssi-teksti: #FFD400; --kemiqr-oranssi-pehmea: rgba(255,212,0,.16);
}

/* Kehys: laskimen oma yläpalkki ja sivupalkki ovat KemiQR:n oksassa. */
html.kemiqr #yla, html.kemiqr #sivu { display: none; }
html.kemiqr #runko { height: calc(100% - var(--ala)); }
html:is(.kemiqr,.kemiqr-ilme) body { font-family: var(--kemiqr-sans); background: var(--kemiqr-sivu); color: var(--teksti); }
html:is(.kemiqr,.kemiqr-ilme) #sisalto { background: var(--kemiqr-sivu); }
html:is(.kemiqr,.kemiqr-ilme) * { font-variant-numeric: tabular-nums; }

/* Työkalurivi (sovellus.js kemiqrTyokalut): kumoa, tee uudelleen, avaa/sulje
   rivit, laskelman nimi, Laskennat, + Lisää rakenteita. */
html.kemiqr .kemiqr-tyokalut {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 14px 18px 0;
}
html.kemiqr .kemiqr-nappi {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 16px; border-radius: 12px;
  border: 1px solid var(--kemiqr-reuna); background: var(--kemiqr-pinta); color: var(--teksti);
  font: 600 15.75px var(--kemiqr-sans); cursor: pointer; white-space: nowrap;
  transition: background 150ms, border-color 150ms, transform 150ms;
}
html.kemiqr .kemiqr-nappi:hover { background: var(--kemiqr-korotettu); border-color: var(--kemiqr-reunaL); }
html.kemiqr .kemiqr-nappi.kuvake { width: 44px; padding: 0; }
html.kemiqr .kemiqr-nappi.kuvake svg { width: 20px; height: 20px; }
html.kemiqr .kemiqr-nappi.paa {
  background: var(--oranssi); border-color: var(--oranssi); color: var(--kemiqr-oranssi-paalla);
  box-shadow: 0 8px 20px -12px rgba(244,121,32,.85);
}
html.kemiqr .kemiqr-nappi.paa:hover { background: var(--oranssi-tumma); border-color: var(--oranssi-tumma); }
html.kemiqr .kemiqr-nappi.valittu { border-color: var(--oranssi); color: var(--kemiqr-oranssi-teksti); background: var(--kemiqr-oranssi-pehmea); }
html.kemiqr .kemiqr-nappi.pois, html.kemiqr .kemiqr-nappi:disabled { color: var(--teksti-himmea); opacity: .55; cursor: default; }
html.kemiqr .kemiqr-nimi {
  margin-right: auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 600 18px var(--kemiqr-otsikko); letter-spacing: -.02em;
}
html.kemiqr .kemiqr-nimi small { display: block; font: 400 13.5px var(--kemiqr-sans); color: var(--teksti-himmea); }

/* Otsakkeet: sivun otsikko ja tunnusluvut Soralla, nimiöt versaalilla. */
html:is(.kemiqr,.kemiqr-ilme) .sivuotsikko {
  background: var(--kemiqr-sivu); border-bottom: 0; padding: 14px 18px 8px; align-items: center;
}
html:is(.kemiqr,.kemiqr-ilme) .sivuotsikko h1 { font: 800 27px var(--kemiqr-otsikko); letter-spacing: -.02em; }
html:is(.kemiqr,.kemiqr-ilme) .sivuotsikko .kuvaus,
html:is(.kemiqr,.kemiqr-ilme) .mittari .otsake,
html:is(.kemiqr,.kemiqr-ilme) .kertoimet b,
html:is(.kemiqr,.kemiqr-ilme) .ryhma,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko th,
html:is(.kemiqr,.kemiqr-ilme) tr.ryhma td {
  font: 600 13.5px var(--kemiqr-sans); letter-spacing: .05em; text-transform: uppercase; color: var(--teksti-himmea);
}
html:is(.kemiqr,.kemiqr-ilme) .osaotsikko, html:is(.kemiqr,.kemiqr-ilme) h2, html:is(.kemiqr,.kemiqr-ilme) h3 { font-family: var(--kemiqr-otsikko); letter-spacing: -.02em; }

/* Hankepalkki: kertoimet ja tunnusluvut kortteina; luku ei saa katketa. */
html:is(.kemiqr,.kemiqr-ilme) .hankepalkki { background: transparent; border-bottom: 0; padding: 10px 18px 4px; gap: 13.5px; }
html:is(.kemiqr,.kemiqr-ilme) .kertoimet {
  background: var(--kemiqr-pinta); border: 1px solid var(--kemiqr-reuna); border-radius: 18px;
  box-shadow: var(--varjo); padding: 12px 16px; gap: 6px 12px; font-size: 15.75px;
}
html:is(.kemiqr,.kemiqr-ilme) .kertoimet:hover { border-color: var(--kemiqr-reunaL); background: var(--kemiqr-pinta); }
html:is(.kemiqr,.kemiqr-ilme) .mittarit { gap: 13.5px; margin-bottom: 0; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
html:is(.kemiqr,.kemiqr-ilme) .mittari {
  overflow: visible; min-width: 0; background: var(--kemiqr-pinta); border: 1px solid var(--kemiqr-reuna);
  border-radius: 18px; box-shadow: var(--varjo); padding: 14px 18px 12px;
}
html:is(.kemiqr,.kemiqr-ilme) .mittari .arvo {
  font: 800 27px var(--kemiqr-otsikko); letter-spacing: -.02em; line-height: 1.1; margin-top: 4px;
  white-space: normal; overflow-wrap: anywhere;
}
html:is(.kemiqr,.kemiqr-ilme) .mittari .lisa { font-size: 13.5px; margin-top: 4px; }
html:is(.kemiqr,.kemiqr-ilme) .mittari.paino { border-color: var(--oranssi); box-shadow: inset 4px 0 0 var(--oranssi), var(--varjo); }
html:is(.kemiqr,.kemiqr-ilme) .mittari.paino .arvo { color: var(--kemiqr-oranssi-teksti); }

/* Taulukko: yksi kortti, 44 px rivit, yksi kirjasinkoko. */
/* EI overflow: hidden TAULUKOLLE: se tekisi taulukosta sticky-otsikoiden
   vierityssäiliön, ja otsikkorivi valuisi 66 px alas rivien päälle
   (mitattu 20.9.2026). Pyöristys tehdään erillisillä reunoilla. */
html:is(.kemiqr,.kemiqr-ilme) #sisalto > table.ruudukko, html:is(.kemiqr,.kemiqr-ilme) .padded > table.ruudukko {
  background: var(--kemiqr-pinta); border: 1px solid var(--kemiqr-reuna); border-radius: 18px;
  box-shadow: var(--varjo); border-collapse: separate; border-spacing: 0;
}
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko thead tr:first-child th:first-child { border-top-left-radius: 18px; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko thead tr:first-child th:last-child { border-top-right-radius: 18px; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr:last-child td:first-child { border-bottom-left-radius: 18px; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr:last-child td:last-child { border-bottom-right-radius: 18px; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr:last-child td { border-bottom: 0; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko { font-size: 13.5px; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko th {
  background: var(--kemiqr-korotettu); border-bottom: 1px solid var(--kemiqr-reuna);
  height: 44px; padding: 0 8px; white-space: normal; line-height: 1.15;
}
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko td {
  height: 44px; padding: 0 8px; vertical-align: middle; border-bottom: 1px solid var(--kemiqr-reuna);
}
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko th + th, html:is(.kemiqr,.kemiqr-ilme) table.ruudukko td + td { border-left: 0; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr:hover > td { background: var(--kemiqr-korotettu); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.valittu > td { background: var(--kemiqr-oranssi-pehmea); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .johdettu, html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .pieni,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .t80, html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .merkki,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .rivitoimittaja, html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .ryhmakerroin,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko .painike.pikku, html:is(.kemiqr,.kemiqr-ilme) table.ruudukko input { font-size: 13.5px; }
html:is(.kemiqr,.kemiqr-ilme) td.rahasolu .summa { font-weight: 600; line-height: 16px; }
html:is(.kemiqr,.kemiqr-ilme) td.rahasolu .johdettu { line-height: 16px; color: var(--teksti-himmea); }

/* Pääryhmärivi: Askon 30.8.2026 oranssi korostus säilyy raitana ja pehmeänä
   taustana, teksti pysyy tummana/vaaleana teeman mukaan - ei valkoista
   oranssilla. Väliotsikko korotettuna, rakennerivi kirjastosta = tieto. */
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma:hover > td,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma.valittu > td {
  background: var(--kemiqr-oranssi-pehmea); color: var(--teksti); font-weight: 600; border-top: 0;
}
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td:first-child { box-shadow: inset 4px 0 0 var(--oranssi); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .solu.teksti { font: 700 13.5px var(--kemiqr-otsikko); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .ratas,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .nuoli,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .jno,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .himmea { color: var(--teksti-himmea); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .nuoli.avaus::before,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .nuoli.avaus:hover::before,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .nuoli.avaus.auki::before,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .nuoli.avaus.auki:hover::before { border-left-color: var(--teksti-himmea); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.laskelma > td .t80 { background: #141a22; color: #e8e3d9; }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.valiotsikko > td,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.valiotsikko:hover > td,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.valiotsikko.taso1 > td,
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.valiotsikko.taso2 > td { background: var(--kemiqr-korotettu); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.rakenne > td { background: var(--kemiqr-tieto-tausta); }
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko tr.rakenne:hover > td { background: var(--sininen-rivi-2); }
html:is(.kemiqr,.kemiqr-ilme) tr.summarivi td {
  background: var(--kemiqr-korotettu) !important; border-top: 2px solid var(--kemiqr-reunaL);
}
html:is(.kemiqr,.kemiqr-ilme) tr.summarivi td.oik:nth-last-child(6) { color: var(--kemiqr-oranssi-teksti); }
html:is(.kemiqr,.kemiqr-ilme) .t80 { border-radius: 999px; padding: 2px 8px; background: var(--kemiqr-korotettu); border: 1px solid var(--kemiqr-reuna); font-weight: 600; }
html:is(.kemiqr,.kemiqr-ilme) .merkki { border-radius: 999px; font-weight: 600; }
html:is(.kemiqr,.kemiqr-ilme) .merkki.A { background: var(--kemiqr-varo-tausta); color: var(--kemiqr-varo-teksti); }
html:is(.kemiqr,.kemiqr-ilme) .merkki.AI, html:is(.kemiqr,.kemiqr-ilme) .merkki.K { background: var(--kemiqr-vaara-tausta); color: var(--kemiqr-vaara-teksti); }
html:is(.kemiqr,.kemiqr-ilme) .merkki.OMA, html:is(.kemiqr,.kemiqr-ilme) .merkki.T, html:is(.kemiqr,.kemiqr-ilme) .merkki.H { background: var(--kemiqr-hyva-tausta); color: var(--kemiqr-hyva-teksti); }
html:is(.kemiqr,.kemiqr-ilme) .merkki.R { background: var(--kemiqr-tieto-tausta); color: var(--kemiqr-tieto-teksti); }
html:is(.kemiqr,.kemiqr-ilme) .rivitoimittaja { background: var(--kemiqr-tieto-tausta); color: var(--kemiqr-tieto-teksti); border-radius: 999px; }

/* Hankinnat pois: KemiQR:ään menee pro-haara ilman hankintaa. Sarake on
   OTSIKOT-listan 8. (sovellus.js), yhteenvedon Laskelmat-taulukossa 3. */
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko.puu col:nth-child(8),
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko.puu th:nth-child(8),
html:is(.kemiqr,.kemiqr-ilme) table.ruudukko.puu td:nth-child(8) { display: none; }
html:is(.kemiqr,.kemiqr-ilme) .yhteenvetosivu table.ruudukko:not(.ketju) th:nth-child(3),
html:is(.kemiqr,.kemiqr-ilme) .yhteenvetosivu table.ruudukko:not(.ketju) td:nth-child(3) { display: none; }

/* Painikkeet ja kentät: 12 px pyöristys, tumma teksti oranssilla, ≥ 44 px. */
html:is(.kemiqr,.kemiqr-ilme) .painike {
  min-height: 44px; padding: 0 16px; border-radius: 12px; border: 1px solid var(--kemiqr-reuna);
  background: var(--kemiqr-pinta); color: var(--teksti); font: 600 15.75px var(--kemiqr-sans);
}
html:is(.kemiqr,.kemiqr-ilme) .painike:hover { background: var(--kemiqr-korotettu); border-color: var(--kemiqr-reunaL); }
html:is(.kemiqr,.kemiqr-ilme) .painike.vahva { background: var(--oranssi); border-color: var(--oranssi); color: var(--kemiqr-oranssi-paalla); }
html:is(.kemiqr,.kemiqr-ilme) .painike.vahva:hover { background: var(--oranssi-tumma); border-color: var(--oranssi-tumma); }
html:is(.kemiqr,.kemiqr-ilme) .painike.pikku { min-height: 36px; padding: 0 12px; border-radius: 999px; font-size: 13.5px; }
html:is(.kemiqr,.kemiqr-ilme) .painike.aktiivinen, html:is(.kemiqr,.kemiqr-ilme) .painike.valittu { border-color: var(--oranssi); color: var(--kemiqr-oranssi-teksti); background: var(--kemiqr-oranssi-pehmea); }
html:is(.kemiqr,.kemiqr-ilme) input, html:is(.kemiqr,.kemiqr-ilme) select, html:is(.kemiqr,.kemiqr-ilme) textarea {
  background: var(--kemiqr-pinta); color: var(--teksti); border-color: var(--kemiqr-reuna); border-radius: 12px; font-family: var(--kemiqr-sans);
}
html:is(.kemiqr,.kemiqr-ilme) input:focus, html:is(.kemiqr,.kemiqr-ilme) select:focus, html:is(.kemiqr,.kemiqr-ilme) textarea:focus { outline: 2px solid var(--oranssi); outline-offset: 1px; }
html:is(.kemiqr,.kemiqr-ilme) .solukentta, html:is(.kemiqr,.kemiqr-ilme) .ryhmakerroin { border-radius: 6.75px; }

/* Alapalkki KemiQR:n pintana. */
html:is(.kemiqr,.kemiqr-ilme) #ala { background: var(--kemiqr-pinta); border-top: 1px solid var(--kemiqr-reuna); color: var(--teksti-himmea); font-size: 13.5px; }
html:is(.kemiqr,.kemiqr-ilme) #ala b { color: var(--teksti); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  html.kemiqr .kemiqr-nappi, html.kemiqr .painike { transition: none; }
}

/* Työmaan laatikko laskentaluettelon yllä (KemiQR-kehys, luku 788). */
html.kemiqr .kemiqr-tyomaa { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 14px 18px; margin: 8px 0 4px; border: 1px solid var(--viiva); border-radius: 12px; background: var(--tausta); }
html.kemiqr .kemiqr-tyomaa span { line-height: 1.4; }

/* KEMIQR-ILME OMASSA IKKUNASSA (21.9.2026): samat tokenit ja tyylit kuin
   upotuksessa (html:is(.kemiqr,.kemiqr-ilme) yllä), mutta ylapalkki ja
   sivupalkki sailyvat ja saavat KemiQR:n varit. Tuotenimi "KemiQR ·
   Kustannuslaskin". */
html.kemiqr-ilme body { font-family: var(--kemiqr-sans); background: var(--kemiqr-sivu); color: var(--teksti); }
html.kemiqr-ilme #yla .logo { font-family: var(--kemiqr-sans); font-weight: 800; letter-spacing: -0.02em; color: var(--teksti); }
html.kemiqr-ilme .logo-kemi { color: var(--teksti); }
html.kemiqr-ilme .logo-qr { color: var(--oranssi); }
html.kemiqr-ilme .logo-tuote { font-weight: 600; color: var(--teksti-himmea); }
html.kemiqr-ilme #sivu .ryhma { color: var(--teksti-himmea); }


/* Kirjanmerkki "Kopioi AI-vastaus" hakuikkunoissa (23.9.2026). */
.kirjanmerkkivinkki { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  margin-top: 10px; padding: 10px 12px; border: 1px dashed var(--viiva-tumma);
  border-radius: 10px; font-size: 12px; color: var(--teksti-himmea); }
.kirjanmerkkivinkki span { flex: 1 1 220px; }
.kirjanmerkki-nappi { display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border-radius: 10px; background: var(--oranssi); color: var(--teksti);
  font-weight: 700; font-size: 13.5px; text-decoration: none; cursor: grab; }
html.yo .kirjanmerkki-nappi { color: var(--tausta); }

/* Excel-tuonnin tarkistus (26.9.2026). */
.excel-tuonti-summat { border-collapse: collapse; margin: 8px 0 12px; min-width: 360px; }
.excel-tuonti-summat td { padding: 4px 12px 4px 0; border-bottom: 1px solid var(--viiva); }
.excel-tuonti-summat td.luku { text-align: right; font-variant-numeric: tabular-nums; }
.excel-tuonti-summat td.hyva { color: var(--vihrea); font-weight: 600; }
.excel-tuonti-summat td.vaara { color: var(--punainen); font-weight: 600; }
.excel-tuonti-summat td.varo { color: var(--keltainen); }
.excel-tuonti-varoitukset { margin: 8px 0; padding-left: 18px; color: var(--keltainen); }
.excel-tuonti-varoitukset li { margin: 4px 0; }
